# Chat Completions

> Create a model response from a list of messages, with optional streaming, images and tools.

**POST** `https://deference.si/v1/chat/completions`

Follows OpenAI's Chat Completions format. Send the key as `Authorization: Bearer sk-df-...` or `x-api-key`.

## Send a request

**curl**

```bash
curl https://deference.si/v1/chat/completions \
  -H "Authorization: Bearer $DEFERENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5.5",
    "messages": [{ "role": "user", "content": "Say hello in one sentence." }]
  }'
```

**Python**

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://deference.si/v1", api_key=os.environ["DEFERENCE_API_KEY"])

response = client.chat.completions.create(
    model="anthropic/claude-sonnet-5.5",
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(response.choices[0].message.content)
```

**TypeScript**

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://deference.si/v1",
  apiKey: process.env.DEFERENCE_API_KEY,
});

const response = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5.5",
  messages: [{ role: "user", content: "Say hello in one sentence." }],
});
console.log(response.choices[0].message.content);
```

## Request body

* `model` (string, required): The model id, such as `anthropic/claude-sonnet-5.5`.
* `messages` (array, required): The conversation. Each item has a `role` (`system`, `user`, `assistant` or `tool`) and `content`. Content can be a string or an array of parts, including `image_url` parts for vision.
* `stream` (boolean): Return server-sent events. Defaults to `false`.
* `stream_options` (object): `{ "include_usage": true }` makes the final usage chunk follow OpenAI's shape, with an empty `choices` array.
* `max_tokens` (integer): The output limit. Also sets the credit held while the request runs. `max_completion_tokens` works the same way. Sending both holds the larger. Deference does not lower a cap you set.
* `temperature` (number): Sampling temperature.
* `top_p` (number): Nucleus sampling.
* `stop` (string | array): Sequences where the model stops.
* `tools` (array): Functions the model may call. See [Tool calling](https://deference.si/docs/api-reference/tool-calling).
* `tool_choice` (string | object): `auto`, `none`, `required` or a specific function.
* `response_format` (object): `json_object` or `json_schema`. See [Structured outputs](https://deference.si/docs/api-reference/structured-outputs).
* `reasoning` (object): How much a reasoning model thinks: `{ "effort": "low" | "medium" | "high" }` or `{ "max_tokens": 2000 }`.
* `modalities` (array): `["image", "text"]` on models that generate images in a chat reply.
* `seed` (integer): Requests repeatable sampling where the model supports it.

Fields pass through to the provider, which ignores parameters the model does not support. `models`, `fallbacks`, `route` and `provider` return `400 unsupported_parameter`, as does a `plugins` entry that is not `response-healing`, a `file-parser` on `cloudflare-ai` or `native`, or `context-compression` with `enabled: false`. A tool type other than `function`, `custom` or a supported web-search tool, X search and audio input are refused the same way. Deference replaces `user` with an account tag.

Set `max_tokens` or `max_completion_tokens` to control the output limit and credit held for the request. A cap you set is kept, and the request is refused if that hold does not fit. If you omit the cap and the request uses no server tools, Deference can lower the cap it would have sent so the hold fits available credit or the key's remaining limit. Requests with server tools must fit their full hold. See [Credit](https://deference.si/docs/concepts/credit-and-activation).

## Response

```json
{
  "id": "gen-1760000000-Ab3xQz9k",
  "object": "chat.completion",
  "created": 1760000000,
  "model": "anthropic/claude-sonnet-5.5",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "Hello, nice to meet you." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 14, "completion_tokens": 7, "total_tokens": 21 }
}
```

`finish_reason` is `stop`, `length`, `tool_calls`, `content_filter` or `error`.

`usage` carries the provider's own fields, such as cached and reasoning token counts and a cost. What Deference charges is OpenRouter's price plus OpenRouter's 5% platform fee. Deference adds no markup. That charge is in `x-deference-cost` and in [Activity](https://deference.si/docs/dashboard/activity).

## Response headers

| Header             | Meaning                                                                                                             |
| ------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `x-request-id`     | The request id, on every response including errors. Activity shows it once the request is accepted for a model      |
| `x-deference-cost` | The cost in micro-dollars, on a successful non-streamed response. Provisional when the body carries a generation id |

## Streaming

With `stream: true` the response is a stream of `data:` chunks that ends with `data: [DONE]`. The last chunk before it carries `usage`. See [Streaming](https://deference.si/docs/api-reference/streaming) and [Streaming events](https://deference.si/docs/api-reference/streaming-events).
