# Chat Completions API

POST /v1/chat/completions: the OpenAI Chat Completions format, streamed or not, with reasoning and tools.

## Reasoning

Reasoning models think before they answer. The reasoning arrives in `reasoning_content` next
to `content` and its tokens are billed as output, reported in
`usage.completion_tokens_details.reasoning_tokens`. Reasoning is on by default. Control it with
the `reasoning` object or OpenAI's `reasoning_effort`:

| Request field | Effect |
| --- | --- |
| `"reasoning": { "enabled": false }` or `"reasoning_effort": "none"` | No reasoning: faster and cheaper answers. |
| `"reasoning": { "effort": "low" }` (or `medium`, `high`) | Reasoning on, at that effort. |
| `"reasoning": { "exclude": true }` | The model still reasons, but `reasoning_content` is left out of the reply. |

GLM 5.3 Flash always reasons: with reasoning turned off it still thinks briefly, the reasoning is
left out of the reply, and those tokens are billed as output. It has two effort levels, low and
high; `medium` runs as high.

## Streaming

With `stream: true` the reply arrives as Server-Sent Events, and the final chunk before
`data: [DONE]` always carries `usage`, whether or not you set `stream_options`. While a long
prompt is being read, the stream sends `: ping` comment lines to keep the connection open;
SSE clients ignore them. If the request fails after the stream has started, it ends with one
`data: {"error": {...}}` event in the error shape below.

## Endpoint

- [Create a chat completion](#create-a-chat-completion): `POST https://api.openscale.so/v1/chat/completions`

### Create a chat completion

`POST https://api.openscale.so/v1/chat/completions`

Send a conversation and receive the next assistant message. Set `stream: true` for Server-Sent Events. Models that reason return their reasoning in `reasoning_content` alongside `content`; when replaying a tool-calling conversation, send `reasoning_content` back on the assistant messages.

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | A chat model id from `GET /models`. One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `messages` | array of Message | Yes |  |
| `stream` | boolean | No | Stream the completion as Server-Sent Events. The final chunk before `[DONE]` carries `usage`. Default `false`. |
| `stream_options` | object | No |  |
| `stream_options.include_usage` | boolean | No |  |
| `max_tokens` | integer | No | Maximum completion tokens, including reasoning tokens. Reasoning models spend part of this budget thinking, so allow room for it. Above the model's output limit the request is refused with `max_tokens_exceeded`. At least 1. |
| `max_completion_tokens` | integer | No | Same as `max_tokens` (the newer OpenAI name). At least 1. |
| `temperature` | number | No | From 0 to 2. |
| `top_p` | number | No | From 0 to 1. |
| `stop` | string or array of string | No |  |
| `tools` | array of object | No | OpenAI-style function tools. |
| `tools[].type` | string | No | Always `function`. |
| `tools[].function` | object | No |  |
| `tool_choice` | string or object | No |  |
| `response_format` | object | No | Structured outputs: `json_object` or `json_schema`. `json_object` works whether or not your prompt mentions JSON. |
| `response_format.type` | string | No | One of `text`, `json_object`, `json_schema`. |
| `response_format.json_schema` | object | No |  |
| `reasoning` | object | No | Reasoning control. `enabled: false` turns reasoning off; `effort` sets how much the model thinks; `exclude: true` keeps reasoning out of the reply. |
| `reasoning.enabled` | boolean | No |  |
| `reasoning.effort` | string | No | One of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`. |
| `reasoning.exclude` | boolean | No |  |
| `reasoning_effort` | string | No | How much the model reasons before answering (OpenAI style); `none` turns reasoning off. One of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`. |
| `logprobs` | boolean | No |  |
| `top_logprobs` | integer | No | From 0 to 20. |
| `frequency_penalty` | number | No | From -2 to 2. |
| `presence_penalty` | number | No | From -2 to 2. |
| `user` | string | No |  |
| `service_tier` | string | No | `priority` runs the request in fast mode (same model, faster serving, higher price) when it is available; any other value runs at the standard tier. The response reports the tier that served it. Example: `priority`. |

#### Simple request

```bash
curl https://api.openscale.so/v1/chat/completions \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "deepseek/deepseek-v4.1-flash",
  "messages": [
    {
      "role": "user",
      "content": "Hello from OpenScale"
    }
  ]
}'
```

#### Streamed, reasoning off

```bash
curl https://api.openscale.so/v1/chat/completions \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "deepseek/deepseek-v4.1-flash",
  "stream": true,
  "reasoning": {
    "enabled": false
  },
  "messages": [
    {
      "role": "user",
      "content": "Hello from OpenScale"
    }
  ]
}'
```

#### Response

`200`: The completion, or an SSE stream when `stream` is true.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `chatcmpl-ucOlCLBJhTr5ahpQzgJaeiK8`. |
| `object` | string | Always `chat.completion`. |
| `created` | integer |  |
| `model` | string | One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `choices` | array of object |  |
| `choices[].index` | integer |  |
| `choices[].message` | object |  |
| `choices[].finish_reason` | string or null | One of `stop`, `length`, `tool_calls`, `null`. |
| `usage` | Usage |  |
| `service_tier` | string | The tier that served the request: `priority` in fast mode. One of `default`, `priority`. |

```json
{
  "id": "chatcmpl-ucOlCLBJhTr5ahpQzgJaeiK8",
  "object": "chat.completion",
  "created": 1790078324,
  "model": "deepseek/deepseek-v4.1-flash",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?",
        "reasoning_content": "The user is greeting me."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 17,
    "completion_tokens": 42,
    "total_tokens": 59,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 31
    }
  }
}
```

```text
data: {"id":"chatcmpl-y3nR30I9R87biSC5oz4gHeF5","object":"chat.completion.chunk","model":"deepseek/deepseek-v4.1-flash","choices":[{"index":0,"delta":{"role":"assistant","content":"Hel"},"finish_reason":null}]}

data: {"id":"chatcmpl-y3nR30I9R87biSC5oz4gHeF5","object":"chat.completion.chunk","model":"deepseek/deepseek-v4.1-flash","choices":[{"index":0,"delta":{"content":"lo"},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-y3nR30I9R87biSC5oz4gHeF5","object":"chat.completion.chunk","model":"deepseek/deepseek-v4.1-flash","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":2,"total_tokens":19}}

data: [DONE]
```

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | Invalid request: `invalid_messages`, `max_tokens_exceeded`, `invalid_reasoning_effort`, `context_length_exceeded` or `request_rejected`. |
| 401 | Missing or invalid API key. |
| 402 | Empty balance, or the key reached its spend limit. |
| 404 | Unknown model id. |
| 429 | Rate limit crossed or model at capacity. |
| 502 | The model is temporarily unavailable. |
| 504 | The model did not respond in time. |

## Objects

### The Message object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `role` | string | Yes | One of `system`, `user`, `assistant`, `tool`. |
| `content` | string or array of object | Yes | Text, or an array of content parts for multimodal input. |
| `name` | string | No |  |
| `tool_call_id` | string | No |  |
| `tool_calls` | array of ToolCall | No |  |
| `reasoning_content` | string | No | The model's reasoning from a previous turn. Send it back on assistant messages when replaying a tool-calling conversation. |

### The ToolCall object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | No |  |
| `type` | string | No | Always `function`. |
| `function` | object | No |  |
| `function.name` | string | No |  |
| `function.arguments` | string | No | JSON-encoded arguments. |

### The Usage object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `prompt_tokens` | integer | No |  |
| `completion_tokens` | integer | No |  |
| `total_tokens` | integer | No |  |
| `prompt_tokens_details` | object | No |  |
| `prompt_tokens_details.cached_tokens` | integer | No | Prompt tokens billed at the cached rate. |
| `completion_tokens_details` | object | No |  |
| `completion_tokens_details.reasoning_tokens` | integer | No | Reasoning tokens, billed as completion tokens. |

### The ChatCompletionChunk object

One Server-Sent Event `data:` payload. The stream ends with `data: [DONE]`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | No |  |
| `object` | string | No | Always `chat.completion.chunk`. |
| `created` | integer | No |  |
| `model` | string | No | One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `choices` | array of object | No |  |
| `choices[].index` | integer | No |  |
| `choices[].delta` | object | No |  |
| `choices[].finish_reason` | string or null | No |  |
| `usage` | Usage or null | No | Present on the final chunk only. |
| `service_tier` | string | No | The tier that served the request: `priority` in fast mode. One of `default`, `priority`. |

## More of the API reference

- [API reference](https://openscale.so/docs): base URLs, authentication and a first request.
- [Responses](https://openscale.so/docs/responses): POST /v1/responses: the OpenAI Responses format with stored state, background runs and enforced tool rules.
- [Conversations](https://openscale.so/docs/conversations): Keep the turns of a Responses conversation on our side and continue it by id.
- [Prompts](https://openscale.so/docs/prompts): Save instructions, messages and settings once, then use them by id in the Responses API.
- [Anthropic Messages](https://openscale.so/docs/messages): POST /anthropic/v1/messages: the Anthropic Messages format, for Claude Code and Anthropic SDKs.
- [Models and pricing](https://openscale.so/docs/models): Model ids, prices per 1M tokens, context windows, output limits, versions and the live catalog endpoint.
- [Batch](https://openscale.so/docs/batch): Upload a JSONL file of chat completions, create a batch and collect the results when it finishes.
- [Errors](https://openscale.so/docs/errors): The error shape, every status code and code, and which ones to retry.
- [Rate limits](https://openscale.so/docs/rate-limits): Requests and tokens per minute, how a 429 tells you when to retry, and how to get more.
- [Data retention](https://openscale.so/docs/data-retention): What is stored, for how long, how to delete it, and zero data retention.
- [Interactive reference](https://openscale.so/docs/reference): try every endpoint in the browser.
- [OpenAPI document](https://openscale.so/openapi.json): the whole API as OpenAPI 3.1.
