# Chat Completions (/docs/api/chat-completions)

`POST /v1/chat/completions` speaks the OpenAI Chat Completions protocol and serves every chat model: GPT, Claude, Gemini, DeepSeek, Qwen, and the rest. Point an OpenAI SDK at `https://api.ohmygpt.com/v1` and change `model` to switch between them. The full request and response schema is in the [endpoint reference](/docs/api/chat/createChatCompletion).

| | |
|---|---|
| Endpoint | `POST https://api.ohmygpt.com/v1/chat/completions` |
| SDK base URL | `https://api.ohmygpt.com/v1` |
| Auth | `Authorization: Bearer $OHMYGPT_API_KEY` |
| Content type | `application/json` |

```python tab="Python"
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.ohmygpt.com/v1",
    api_key=os.environ["OHMYGPT_API_KEY"],
)

completion = client.chat.completions.create(
    model="anthropic/claude-haiku-4-5",
    messages=[{"role": "user", "content": "Explain QUIC in two sentences."}],
)
print(completion.choices[0].message.content)
```

```ts tab="TypeScript"
import OpenAI from 'openai'

const client = new OpenAI({
  baseURL: 'https://api.ohmygpt.com/v1',
  apiKey: process.env.OHMYGPT_API_KEY,
})

const completion = await client.chat.completions.create({
  model: 'anthropic/claude-haiku-4-5',
  messages: [{ role: 'user', content: 'Explain QUIC in two sentences.' }],
})
console.log(completion.choices[0].message.content)
```

```bash tab="cURL"
curl https://api.ohmygpt.com/v1/chat/completions \
  -H "Authorization: Bearer $OHMYGPT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-haiku-4-5",
    "messages": [{"role": "user", "content": "Explain QUIC in two sentences."}]
  }'
```

## Native and other models [#native-and-other-models]

For models whose native protocol is Chat Completions, such as GPT, DeepSeek, Qwen, Kimi, and GLM, the vendor receives your body as sent, and vendor-specific fields work as the vendor documents them. Claude and Gemini models answer in the same `chat.completion` shape; [Protocols](/docs/api/protocols) covers what carries over.

## Claude and Gemini responses [#claude-and-gemini-responses]

### Finish reasons [#finish-reasons]

For Claude and Gemini models, `finish_reason` uses the OpenAI values and `native_finish_reason` keeps the model's original reason:

```json
"choices": [
  {
    "index": 0,
    "finish_reason": "length",
    "native_finish_reason": "max_tokens",
    "message": { "role": "assistant", "content": "Pong! " }
  }
]
```

| Model's reason | `finish_reason` |
|---|---|
| Claude `end_turn`, `stop_sequence`, `pause_turn`; Gemini `STOP` | `stop` |
| Claude `max_tokens`; Gemini `MAX_TOKENS` | `length` |
| Claude `tool_use`; Gemini function call | `tool_calls` |
| Claude `refusal`; Gemini safety and recitation blocks | `content_filter` |

### Reasoning in the response [#reasoning-in-the-response]

For Claude and Gemini models, the model's visible reasoning arrives in `message.reasoning`, with structured entries in `message.reasoning_details`. Claude entries carry a `signature`:

```json
"message": {
  "role": "assistant",
  "content": "391",
  "reasoning": "17 * 23 = ?\n\nLet me calculate this:\n17 * 23 = 17 * 20 + 17 * 3\n= 340 + 51\n= 391",
  "reasoning_details": [
    {
      "type": "reasoning.text",
      "text": "17 * 23 = ?\n\nLet me calculate this:\n17 * 23 = 17 * 20 + 17 * 3\n= 340 + 51\n= 391",
      "signature": "EuoCCrgBCBMYAipATvOr...",
      "index": 0
    }
  ]
}
```

Models whose native protocol is Chat Completions return reasoning in the vendor's own field, for example `reasoning_content` on DeepSeek and Qwen. Streams carry the same data as `delta.reasoning` and `delta.reasoning_details`. [Thinking](/docs/api/thinking) covers the request parameters that turn reasoning on, off, or down.

For multi-turn Claude conversations that combine extended thinking with tool use, use [Messages](/docs/api/messages), which keeps thinking blocks and their signatures from turn to turn. Gemini tool calls carry their thought signature in `tool_calls[].extra_content`; send assistant messages back unchanged to keep it.

### Routing [#routing]

A top-level `provider` object chooses which provider serves the model (`order`, `only`, `ignore`, `sort`). The OpenAI Python SDK takes it in `extra_body`:

```python
completion = client.chat.completions.create(
    model="openai/gpt-5.4-nano",
    messages=[{"role": "user", "content": "ping"}],
    extra_body={"provider": {"sort": "price"}},
)
```

See [Provider routing](/docs/provider-routing) for the fields and their rules.

## Streaming [#streaming]

Set `stream: true` to receive `chat.completion.chunk` events ending with `data: [DONE]`. The last chunk before `[DONE]` carries `usage` whether or not you set `stream_options`, and a `: x-cost-credits` comment line reports the charge. See [Streaming](/docs/api/streaming).

## Errors [#errors]

Errors use the OpenAI format. `error.code` is a stable value, for example `unsupported_parameter` with the field named in `param`:

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "unsupported_parameter",
    "param": "functions",
    "message": "The model `anthropic/claude-haiku-4-5` does not support the `functions` parameter."
  }
}
```

[Errors](/docs/errors) lists the codes and status handling.
