---
title: Chat Completions
description: POST /v1/chat/completions — OpenAI-совместимый чат Clipia AI Gateway. Обычный и потоковый ответ, параметры, tool calling и structured outputs.
---

`POST /v1/chat/completions` — основной эндпоинт Clipia AI Gateway, полностью совместимый с OpenAI Chat Completions. Принимает массив `messages`, возвращает ответ ассистента в обычном или потоковом режиме, поддерживает вызов инструментов и структурированный вывод по JSON-схеме.

<Method name="POST" path="/v1/chat/completions" />

## Обычный ответ (non-stream)

<Tabs items={['Python', 'Node.js', 'cURL']}>
<Tab value="Python">

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["CLIPIA_API_KEY"],
    base_url="https://api.clipia.ai/v1",
)

resp = client.chat.completions.create(
    model="claude-opus-5",
    messages=[
        {"role": "system", "content": "Ты лаконичный ассистент."},
        {"role": "user", "content": "Назови три факта о Марсе."},
    ],
    temperature=0.7,
    max_tokens=512,
)

print(resp.choices[0].message.content)
print(resp.usage)
```

</Tab>
<Tab value="Node.js">

```ts
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.CLIPIA_API_KEY,
  baseURL: "https://api.clipia.ai/v1",
});

const resp = await client.chat.completions.create({
  model: "claude-opus-5",
  messages: [
    { role: "system", content: "Ты лаконичный ассистент." },
    { role: "user", content: "Назови три факта о Марсе." },
  ],
  temperature: 0.7,
  max_tokens: 512,
});

console.log(resp.choices[0].message.content);
```

</Tab>
<Tab value="cURL">

```bash
curl https://api.clipia.ai/v1/chat/completions \
  -H "Authorization: Bearer $CLIPIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "messages": [
      { "role": "system", "content": "Ты лаконичный ассистент." },
      { "role": "user", "content": "Назови три факта о Марсе." }
    ],
    "temperature": 0.7,
    "max_tokens": 512
  }'
```

</Tab>
</Tabs>

**Ответ `200`**

```json
{
  "id": "chatcmpl-3f9a1c7e2b41",
  "object": "chat.completion",
  "created": 1782300000,
  "model": "claude-opus-5",
  "provider": "Clipia",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "1. ...\n2. ...\n3. ..." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 28,
    "completion_tokens": 64,
    "total_tokens": 92,
    "cost": 0.046
  }
}
```

`usage.cost` — стоимость запроса в **кредитах**. `finish_reason` нормализован к OpenAI-перечислению: `stop`, `length`, `tool_calls`, `content_filter`.

## Потоковый ответ (stream)

Передайте `stream: true` — ответ придёт по мере генерации в виде Server-Sent Events. Каждое событие — это `chat.completion.chunk` с дельтой; поток завершается строкой `data: [DONE]`.

<Tabs items={['Python', 'Node.js', 'cURL']}>
<Tab value="Python">

```python
stream = client.chat.completions.create(
    model="claude-opus-5",
    messages=[{"role": "user", "content": "Напиши хокку про море."}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta
    if delta.content:
        print(delta.content, end="", flush=True)
```

</Tab>
<Tab value="Node.js">

```ts
const stream = await client.chat.completions.create({
  model: "claude-opus-5",
  messages: [{ role: "user", content: "Напиши хокку про море." }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

</Tab>
<Tab value="cURL">

```bash
curl -N https://api.clipia.ai/v1/chat/completions \
  -H "Authorization: Bearer $CLIPIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "messages": [{ "role": "user", "content": "Напиши хокку про море." }],
    "stream": true
  }'
```

</Tab>
</Tabs>

**Поток событий**

```text
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1782300000,"model":"claude-opus-5","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"claude-opus-5","choices":[{"index":0,"delta":{"content":"Волны "},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"claude-opus-5","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]
```

<Callout type="info" title="Итоговый usage в стриме">
Чтобы получить `usage` в потоковом режиме, передайте `stream_options: { "include_usage": true }`. Тогда перед `data: [DONE]` придёт дополнительный чанк с заполненным `usage` и пустым `choices: []`.
</Callout>

## Параметры запроса

<TypeTable
  type={{
    model: { type: 'string', description: 'Идентификатор модели, напр. claude-opus-5 (см. каталог /v1/models).' },
    messages: { type: 'object[]', description: 'История диалога. role: system | user | assistant | tool (developer трактуется как system). content — строка или массив частей.' },
    temperature: { type: 'number', description: 'Степень случайности, обычно 0–2. По умолчанию модельная.' },
    top_p: { type: 'number', description: 'Nucleus sampling, 0–1. Альтернатива temperature.' },
    max_tokens: { type: 'integer', description: 'Максимум токенов в ответе. Алиас max_completion_tokens принимается тоже.' },
    stream: { type: 'boolean', description: 'true — потоковый ответ (SSE). По умолчанию false.' },
    stream_options: { type: 'object', description: '{ include_usage: true } — добавить финальный usage-чанк в стрим.' },
    stop: { type: 'string | string[]', description: 'До 4 стоп-последовательностей.' },
    seed: { type: 'integer', description: 'Запрос на детерминизм (best-effort).' },
    n: { type: 'integer', description: 'Сколько вариантов ответа сгенерировать. По умолчанию 1.' },
    presence_penalty: { type: 'number', description: 'Штраф за повторение тем, −2…2.' },
    frequency_penalty: { type: 'number', description: 'Штраф за повторение токенов, −2…2.' },
    response_format: { type: 'object', description: '{ type: "json_object" } или { type: "json_schema", json_schema: {...} } — структурированный вывод.' },
    tools: { type: 'object[]', description: 'Описания функций, которые модель может вызвать.' },
    tool_choice: { type: 'string | object', description: 'auto | none | required | { type: "function", function: { name } }.' },
    parallel_tool_calls: { type: 'boolean', description: 'Разрешить несколько вызовов инструментов за один ход.' },
    user: { type: 'string', description: 'Стабильный идентификатор конечного пользователя (для аналитики/анти-абьюза).' },
  }}
/>

<Callout type="info" title="Совместимость параметров">
Принимаются оба имени лимита токенов — `max_tokens` и `max_completion_tokens`. Неизвестные верхнеуровневые поля игнорируются (не приводят к ошибке `400`), поэтому код, написанный под OpenAI, переносится без правок.
</Callout>

## Tool / function calling

Полный цикл: вы описываете инструменты в `tools`, модель возвращает `tool_calls`, вы выполняете функцию на своей стороне и отправляете результат сообщением с `role: "tool"`, после чего модель формирует финальный ответ.

```python
import json

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Текущая погода в городе",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Название города"},
                },
                "required": ["city"],
            },
        },
    }
]

messages = [{"role": "user", "content": "Какая сейчас погода в Москве?"}]

# 1) Модель решает вызвать инструмент
resp = client.chat.completions.create(
    model="claude-opus-5", messages=messages, tools=tools,
)
msg = resp.choices[0].message
# resp.choices[0].finish_reason == "tool_calls"

# 2) Выполняем функцию на своей стороне
call = msg.tool_calls[0]
args = json.loads(call.function.arguments)   # {"city": "Москва"}
result = {"temp_c": 14, "condition": "облачно"}

# 3) Возвращаем результат модели и получаем финальный ответ
messages.append(msg)  # эхо ассистента с tool_calls
messages.append({
    "role": "tool",
    "tool_call_id": call.id,
    "content": json.dumps(result, ensure_ascii=False),
})

final = client.chat.completions.create(
    model="claude-opus-5", messages=messages, tools=tools,
)
print(final.choices[0].message.content)
```

В потоковом режиме фрагменты `tool_calls` приходят по полю `index`: имя функции и `id` — в первой дельте этого индекса, далее — фрагменты `function.arguments`, которые нужно склеить в валидный JSON. Завершение вызова инструмента помечается `finish_reason: "tool_calls"`.

## Structured outputs

Чтобы получить ответ строго по JSON-схеме, передайте `response_format` с `type: "json_schema"` и `strict: true`.

```python
resp = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "Извлеки имя и возраст: Анне 30 лет."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                },
                "required": ["name", "age"],
                "additionalProperties": False,
            },
        },
    },
)

print(resp.choices[0].message.content)  # {"name": "Анна", "age": 30}
```

<Callout type="info" title="json_object против json_schema">
`response_format: { "type": "json_object" }` гарантирует валидный JSON, но без конкретной схемы — и требует, чтобы слово «json» встречалось в `messages`. Для жёсткого соответствия структуре используйте `json_schema` со `strict: true`.
</Callout>

## Ошибки

Ошибки приходят в стандартном конверте OpenAI: `{ "error": { "message", "type", "param", "code" } }`, всегда с `Content-Type: application/json` и корректным HTTP-статусом.

| Статус | Код | Когда |
|--------|-----|-------|
| `400` | `invalid_request_error` | Некорректное тело запроса или параметры. |
| `401` | `invalid_api_key` | Отсутствует или неверный ключ. |
| `402` | `insufficient_credits` | Не хватает кредитов на балансе. |
| `404` | `model_not_found` | Неизвестный `model`. |
| `429` | `rate_limit_exceeded` | Превышен лимит запросов — см. [Аккаунт и лимиты](/docs/llm-gateway/account). |

```json
{
  "error": {
    "message": "Недостаточно кредитов на балансе.",
    "type": "insufficient_credits",
    "param": null,
    "code": "insufficient_credits"
  }
}
```
