# Create a chat completion

`POST /v1/chat/completions`

Model-specific vision and tool support. stream=true returns SSE; usage-only events may have empty choices. Client cancellation closes the upstream connection. Cancelled streams with zero recorded output tokens are not charged; partial usage after output may be estimated and billed. No automatic retry after response starts.

Base URL: `https://api.simplellm.eu`. Authenticate using `Authorization: Bearer $SIMPLELLM_API_KEY`.

## Request body

Content-Type: `application/json`

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `model` | `string` | Yes | Exact chat model ID from GET /v1/models. |
| `messages` | `array<Message>` | Yes | Minimum items: 1. |
| `messages[].role` | `string` | Yes | Values: `system`, `developer`, `user`, `assistant`, `tool`. |
| `messages[].content` | `string / null / array<ContentPart>` | No |  |
| `messages[].content[].type` | `string` | Yes |  |
| `messages[].content[].text` | `string` | No |  |
| `messages[].content[].image_url` | `object` | No |  |
| `messages[].content[].image_url.url` | `string` | Yes |  |
| `messages[].content[].image_url.detail` | `string` | No | Values: `auto`, `low`, `high`. |
| `messages[].name` | `string` | No |  |
| `messages[].tool_call_id` | `string` | No |  |
| `messages[].tool_calls` | `array<ToolCall>` | No |  |
| `messages[].tool_calls[].id` | `string` | Yes |  |
| `messages[].tool_calls[].type` | `"function"` | Yes |  |
| `messages[].tool_calls[].function` | `object` | Yes |  |
| `messages[].tool_calls[].function.name` | `string` | Yes |  |
| `messages[].tool_calls[].function.arguments` | `string` | Yes | JSON-encoded arguments; parse and validate before executing. |
| `messages[].reasoning_content` | `string / null` | No | Optional provider-specific reasoning; not guaranteed. |
| `messages[].reasoning` | `string / null` | No | Alternative provider-specific reasoning field; not guaranteed. |
| `stream` | `boolean` | No | Default: `false`. |
| `max_tokens` | `integer` | No | Output budget; reasoning can consume this budget. Minimum: 1. |
| `temperature` | `number` | No |  |
| `top_p` | `number` | No |  |
| `stream_options` | `object` | No |  |
| `stream_options.include_usage` | `boolean` | No |  |
| `stop` | `array<string>` | No | V1 accepts an array of strings, not a plain string. |
| `n` | `integer` | No | Forwarded; multiple choices are model-dependent. |
| `presence_penalty` | `number` | No | Model-dependent sampling parameter. |
| `frequency_penalty` | `number` | No | Model-dependent sampling parameter. |
| `tools` | `array<Tool>` | No |  |
| `tools[].type` | `"function"` | Yes |  |
| `tools[].function` | `object` | Yes |  |
| `tools[].function.name` | `string` | Yes |  |
| `tools[].function.description` | `string` | No |  |
| `tools[].function.parameters` | `object` | No |  |
| `tools[].function.strict` | `boolean` | No | Only where the selected model/backend supports strict schemas. |
| `tool_choice` | `string / object` | No |  |
| `response_format` | `object` | No | Forwarded, model-dependent output-format request; no universal JSON schema guarantee. |

Nested required fields are required only when their containing object is supplied. Extra chat parameters may be provider-specific; acceptance does not guarantee model support.

## Example request

~~~bash
curl --fail-with-body https://api.simplellm.eu/v1/chat/completions \
  -H "Authorization: Bearer $SIMPLELLM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "model": "qwen3.8-flash-next",
  "messages": [
    {
      "role": "user",
      "content": "Explain what an API is in one sentence."
    }
  ],
  "max_tokens": 1024
}'
~~~

## Responses

### HTTP 200

JSON response or incremental SSE stream ending with data: [DONE].

Content-Type: `application/json`

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `id` | `string` | No |  |
| `object` | `string` | No |  |
| `created` | `integer` | No |  |
| `model` | `string` | No |  |
| `choices` | `array<object>` | No |  |
| `choices[].index` | `integer` | No |  |
| `choices[].message` | `Message` | No |  |
| `choices[].message.role` | `string` | Yes | Values: `system`, `developer`, `user`, `assistant`, `tool`. |
| `choices[].message.content` | `string / null / array<ContentPart>` | No |  |
| `choices[].message.content[].type` | `string` | Yes |  |
| `choices[].message.content[].text` | `string` | No |  |
| `choices[].message.content[].image_url` | `object` | No |  |
| `choices[].message.content[].image_url.url` | `string` | Yes |  |
| `choices[].message.content[].image_url.detail` | `string` | No | Values: `auto`, `low`, `high`. |
| `choices[].message.name` | `string` | No |  |
| `choices[].message.tool_call_id` | `string` | No |  |
| `choices[].message.tool_calls` | `array<ToolCall>` | No |  |
| `choices[].message.tool_calls[].id` | `string` | Yes |  |
| `choices[].message.tool_calls[].type` | `"function"` | Yes |  |
| `choices[].message.tool_calls[].function` | `object` | Yes |  |
| `choices[].message.tool_calls[].function.name` | `string` | Yes |  |
| `choices[].message.tool_calls[].function.arguments` | `string` | Yes | JSON-encoded arguments; parse and validate before executing. |
| `choices[].message.reasoning_content` | `string / null` | No | Optional provider-specific reasoning; not guaranteed. |
| `choices[].message.reasoning` | `string / null` | No | Alternative provider-specific reasoning field; not guaranteed. |
| `choices[].finish_reason` | `string / null` | No |  |
| `usage` | `Usage` | No |  |
| `usage.prompt_tokens` | `integer` | No |  |
| `usage.completion_tokens` | `integer` | No |  |
| `usage.total_tokens` | `integer` | No |  |
| `usage.prompt_tokens_details` | `object` | No |  |
| `usage.prompt_tokens_details.cached_tokens` | `integer` | No | Subset of prompt_tokens, not additional input. |

Illustrative response shape only; IDs, timestamps and counters below are synthetic, not live limits or customer data.

~~~json
{
  "id": "chatcmpl-example",
  "object": "chat.completion",
  "created": 1789380000,
  "model": "qwen3.8-flash-next",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "An API lets software communicate."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 10,
    "total_tokens": 30,
    "prompt_tokens_details": {
      "cached_tokens": 0
    }
  }
}
~~~

Content-Type: `text/event-stream`

Response type: `string`.

~~~text
data: {"choices":[{"delta":{"content":"Hello"}}]}

data: [DONE]


~~~

## Error handling

Errors can occur in authentication, billing, routing or the upstream service; not every status applies to every route.

| Status | Meaning |
| --- | --- |
| 400 | Invalid request |
| 401 | Invalid credentials |
| 402 | Insufficient credits |
| 403 | Access denied |
| 404 | Model or resource not found |
| 429 | Rate or capacity limit |
| 500 | Internal error |
| 502 | Routing or upstream error |
| 503 | Temporarily unavailable |
| 504 | Deployment timeout |

See [Troubleshooting](/docs/errors.html) for response examples and [Rate limits & retries](/docs/limits.html) for retry rules.

## Machine-readable schema

[Download OpenAPI JSON](/openapi.json). Operation ID: `createChatCompletion`.
