#Создать чат-комплишен

Формат ответа: raw OpenAI

Этот endpoint возвращает «голый» OpenAI-shape — БЕЗ envelope {success, data}, который используется в остальных endpoint'ах Вайбкод (/v1/deals, /v1/tasks и т.д.).

Это сделано для совместимости с OpenAI SDK. Если у вас единый клиент с проверкой if (!response.success) — сделайте исключение для AI Router.

POST /v1/chat/completions

Генерирует ответ AI-модели на массив сообщений. Возвращает результат целиком (синхронно) или потоком событий через Server-Sent Events при stream: true. Формат запроса и ответа совместим с POST /v1/chat/completions из OpenAI API.

#Поля запроса (body)

Поле Тип Обяз. По умолч. Описание
model string нет auto ID модели или алиас. См. секцию «Алиасы» ниже. Если не указан или auto — будет выбрана модель по умолчанию портала
messages array да Массив сообщений диалога. Минимум 1, максимум 256
messages[].role string да Роль: system, user, assistant, tool
messages[].content string | array | null да Текст сообщения. Максимум 500 000 символов в одном сообщении или 64 элемента в массиве content. Для запросов с изображениями — массив с type: "text" и type: "image_url". У assistant с tool_calls может быть null
messages[].name string нет Имя отправителя (мультиагентные сценарии)
messages[].tool_calls array нет Вызовы функций от ассистента — присутствуют в ответе модели при finish_reason: "tool_calls"
messages[].tool_call_id string нет ID вызова функции — обязателен в сообщении с role: "tool"
temperature number нет по модели Температура генерации, диапазон 0..2. Меньше — точнее и детерминированнее, больше — креативнее
max_tokens number нет по модели Максимум токенов в ответе
top_p number нет Nucleus sampling, диапазон 0..1
stop string | array нет Стоп-последовательности (до 4 штук, каждая до 64 символов)
stream boolean нет false Если true — ответ приходит потоком Server-Sent Events, см. секцию «Потоковая передача»
response_format object нет Управление форматом ответа: {"type": "text"} (default), {"type": "json_object"} (валидный JSON), {"type": "json_schema", "json_schema": {...}} (строгая JSON Schema — требует поддержки моделью)
tools array нет Определения функций, которые модель может вызвать
tool_choice string | object нет auto (модель решает сама), none (запретить вызовы) или {"type": "function", "function": {"name": "..."}} (форсировать конкретную)

#Алиасы моделей

Поле model принимает три значения, разрешающиеся в модель по умолчанию портала (как правило bitrix/bitrixgpt-5.5): auto, bitrix/free или пустая строка.

Май 2026: дефолт сменился с bitrix/bitrixgpt-5 на bitrix/bitrixgpt-5.5 (262K, vision встроен). Старая bitrix/bitrixgpt-5 (и её Vision-вариант bitrix/bitrixgpt-5-vl) помечены DEPRECATED и работают до 31 июля 2026 — дальше любой запрос к ним прозрачно перенаправляется на bitrix/bitrixgpt-5.5 с заголовками X-Model-Replacement. Параллельно появилась bitrix/bitrixgpt-5.5-thinking — то же ядро в reasoning-режиме.

Кроме того, любой частичный modelId сопоставляется с каталогом подстрочно (fuzzy match): если передан gpt-4o-mini — будет найдена модель, в modelId которой эта подстрока встречается, при условии, что она доступна вашему ключу.

#Примеры

#curl — личный ключ

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/chat/completions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/bitrixgpt-5.5",
    "messages": [
      {"role": "system", "content": "Ты эксперт по продажам. Классифицируй лидов по качеству."},
      {"role": "user", "content": "ООО Вектор, 50 пользователей, бюджет 500 тысяч в месяц."}
    ],
    "temperature": 0.3,
    "max_tokens": 300
  }'

#curl — OAuth-приложение

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/chat/completions \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/bitrixgpt-5.5",
    "messages": [
      {"role": "system", "content": "Ты эксперт по продажам. Классифицируй лидов по качеству."},
      {"role": "user", "content": "ООО Вектор, 50 пользователей, бюджет 500 тысяч в месяц."}
    ],
    "temperature": 0.3,
    "max_tokens": 300
  }'

#JavaScript — личный ключ

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/chat/completions', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'bitrix/bitrixgpt-5.5',
    messages: [
      { role: 'system', content: 'Ты эксперт по продажам. Классифицируй лидов по качеству.' },
      { role: 'user', content: 'ООО Вектор, 50 пользователей, бюджет 500 тысяч в месяц.' },
    ],
    temperature: 0.3,
    max_tokens: 300,
  }),
})

const data = await res.json()
console.log(data.choices[0].message.content)
console.log('Токены:', data.usage.total_tokens)

#JavaScript — OAuth-приложение

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/chat/completions', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'bitrix/bitrixgpt-5.5',
    messages: [
      { role: 'system', content: 'Ты эксперт по продажам. Классифицируй лидов по качеству.' },
      { role: 'user', content: 'ООО Вектор, 50 пользователей, бюджет 500 тысяч в месяц.' },
    ],
    temperature: 0.3,
    max_tokens: 300,
  }),
})

const data = await res.json()
console.log(data.choices[0].message.content)

#Поля ответа

Поле Тип Описание
id string Уникальный ID комплишена для отслеживания
object string Всегда chat.completion для синхронного ответа
created number Unix-timestamp создания комплишена
model string Фактически использованная модель (может отличаться от model запроса при автоматическом переключении на резервную модель или DISABLED-перенаправлении)
choices array Варианты ответа модели (обычно один элемент)
choices[].index number Порядковый номер варианта
choices[].finish_reason string Причина завершения: stop, length, tool_calls, content_filter
choices[].message object Сгенерированное сообщение
choices[].message.role string Всегда assistant
choices[].message.content string | null Текст ответа. null при finish_reason: "tool_calls" — содержимое в tool_calls
choices[].message.tool_calls array Список вызовов функций (если модель решила их вызвать)
usage.prompt_tokens number Токены входа
usage.completion_tokens number Токены ответа
usage.total_tokens number Сумма токенов в запросе и ответе

#Пример ответа

JSON
{
  "id": "chatcmpl-a1a73c6eb3f180fd",
  "object": "chat.completion",
  "created": 1777289339,
  "model": "bitrix/bitrixgpt-5.5",
  "choices": [
    {
      "index": 0,
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "Качество: ВЫСОКОЕ\n\nОбоснование:\n- Юридическое лицо (ООО) — B2B-клиент\n- Бюджет 500 тысяч в месяц — выше среднего\n- Конкретный объём (50 пользователей) — осознанная потребность\n\nРекомендация: назначить звонок в течение 24 часов."
      }
    }
  ],
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 85,
    "total_tokens": 127
  }
}

#Потоковая передача (Server-Sent Events)

При stream: true ответ приходит как поток Server-Sent Events. Заголовки ответа: Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive. Каждое событие — строка data: {JSON}\n\n, завершающее событие — data: [DONE]\n\n.

Пример обработки потока на JavaScript:

javascript
const response = await fetch('https://vibecode.bitrix24.tech/v1/chat/completions', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'bitrix/bitrixgpt-5.5',
    messages: [{ role: 'user', content: 'Расскажи про CRM' }],
    stream: true,
  }),
})

const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ''

while (true) {
  const { done, value } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  const lines = buffer.split('\n')
  buffer = lines.pop() ?? ''

  for (const line of lines) {
    if (!line.startsWith('data: ')) continue
    const payload = line.slice(6)
    if (payload === '[DONE]') return
    const chunk = JSON.parse(payload)
    const delta = chunk.choices?.[0]?.delta?.content
    if (delta) process.stdout.write(delta)
  }
}

Формат событий потока:

data: {"id":"chatcmpl-a9f6128818355f17","object":"chat.completion.chunk","model":"bitrix/bitrixgpt-5.5","choices":[{"index":0,"delta":{"role":"assistant","content":"CRM"}}]}

data: {"id":"chatcmpl-a9f6128818355f17","object":"chat.completion.chunk","model":"bitrix/bitrixgpt-5.5","choices":[{"index":0,"delta":{"content":" — это"}}]}

data: {"id":"chatcmpl-a9f6128818355f17","object":"chat.completion.chunk","model":"bitrix/bitrixgpt-5.5","choices":[{"index":0,"finish_reason":"stop","delta":{}}],"usage":{"prompt_tokens":10,"completion_tokens":15,"total_tokens":25}}

data: [DONE]

usage обычно приходит в последнем событии перед [DONE]. Накапливайте delta.content из всех событий для получения полного текста ответа.

#Гарантированный JSON-ответ

При response_format: {"type": "json_object"} модель возвращает строго корректный JSON в content. Вайбкод автоматически добавляет в системное сообщение инструкцию вернуть только JSON. Структуру результата опишите явно:

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/chat/completions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/bitrixgpt-5.5",
    "messages": [
      {"role": "system", "content": "Классифицируй лид. Верни JSON: {\"quality\": \"high|medium|low\", \"score\": 0-100}"},
      {"role": "user", "content": "ООО Вектор, бюджет 2 миллиона в месяц"}
    ],
    "response_format": {"type": "json_object"}
  }'

Ответ:

JSON
{
  "id": "chatcmpl-acdb112cf9cdf9f9",
  "object": "chat.completion",
  "model": "bitrix/bitrixgpt-5.5",
  "choices": [
    {
      "index": 0,
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "{\"quality\":\"high\",\"score\":92}"
      }
    }
  ],
  "usage": {"prompt_tokens": 56, "completion_tokens": 14, "total_tokens": 70}
}

#Structured Outputs (`json_schema`)

response_format: {"type": "json_schema", "json_schema": {...}} гарантирует, что ответ модели строго соответствует заданной JSON Schema — модель не может вернуть лишние поля, пропустить обязательные или перепутать типы. Это сильнее, чем json_object (который гарантирует только валидный JSON).

Поддерживается на уровне upstream-провайдера (OpenAI / Groq для GPT-OSS / BitrixGPT 5.5). Список моделей с поддержкой — в GET /v1/meai.structuredOutputs.models. На момент мая 2026 strict-mode работает на платформенных моделях bitrix/bitrixgpt-5.5 (дефолтная), bitrix/bitrixgpt-5.5-thinking, bitrix/openai/gpt-oss-120b, а также на BYOK-моделях openai/gpt-4o, openai/gpt-4o-mini, openai/o3-mini.

Формат json_schema:

Поле Тип Обязат. Описание
name string да Имя схемы. До 64 символов, только a-zA-Z0-9_-.
description string нет Описание для модели (помогает интерпретации).
strict boolean нет true — строгая валидация (рекомендуется).
schema object да Сама JSON Schema.

Пример:

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/chat/completions \
  -H "X-Api-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/openai/gpt-oss-120b",
    "messages": [
      {"role": "system", "content": "Return strict JSON per the schema."},
      {"role": "user", "content": "Hello"}
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "hello_response",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {"message": {"type": "string"}},
          "required": ["message"],
          "additionalProperties": false
        }
      }
    }
  }'

choices[0].message.content будет содержать строку, парсящуюся в объект, точно соответствующий схеме (например {"message":"Hello"}).

Если модель не поддерживает SO:

JSON
{
  "error": {
    "message": "Model 'openai/o3' does not support response_format=json_schema. Use one of: bitrix/bitrixgpt-5.5, bitrix/bitrixgpt-5.5-thinking, openai/gpt-4o, bitrix/openai/gpt-oss-120b, openai/gpt-4o-mini.",
    "type": "invalid_request_error",
    "code": "model_does_not_support_structured_outputs",
    "param": "response_format.type",
    "structuredOutputsModels": ["bitrix/bitrixgpt-5.5", "bitrix/bitrixgpt-5.5-thinking", "openai/gpt-4o", "bitrix/openai/gpt-oss-120b", "openai/gpt-4o-mini", "openai/o3-mini"]
  }
}

Для custom OpenAI-compat провайдеров SO включается через PATCH /platform/ai/models/:id (admin), если upstream это поддерживает.

#Function calling (tools)

Опишите функции, которые модель может вызвать. Когда модель решит вызвать функцию, finish_reason будет tool_calls, а contentnull. Аргументы функции придут в tool_calls[].function.arguments как JSON-строка.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/chat/completions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/bitrixgpt-5.5",
    "messages": [
      {"role": "user", "content": "Какая погода в Москве?"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Получить текущую погоду в указанном городе",
          "parameters": {
            "type": "object",
            "properties": {
              "city": {"type": "string", "description": "Название города"}
            },
            "required": ["city"]
          }
        }
      }
    ],
    "tool_choice": "auto"
  }'

Ответ:

JSON
{
  "id": "chatcmpl-9aaee7576d8057ab",
  "object": "chat.completion",
  "model": "bitrix/bitrixgpt-5.5",
  "choices": [
    {
      "index": 0,
      "finish_reason": "tool_calls",
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "chatcmpl-tool-a423212c4e614cab",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Москва\"}"
            }
          }
        ]
      }
    }
  ],
  "usage": {"prompt_tokens": 275, "completion_tokens": 26, "total_tokens": 301}
}

Чтобы продолжить диалог после вызова функции — добавьте в messages ответ с role: "tool" и тем же tool_call_id:

JSON
{
  "messages": [
    {"role": "user", "content": "Какая погода в Москве?"},
    {"role": "assistant", "content": null, "tool_calls": [{"id": "chatcmpl-tool-a423212c4e614cab", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\": \"Москва\"}"}}]},
    {"role": "tool", "tool_call_id": "chatcmpl-tool-a423212c4e614cab", "content": "+5°C, облачно"}
  ]
}

#Анализ изображений

Модели с поддержкой работы с изображениями принимают графику через массив content с элементом type: "image_url". Адрес изображения — либо data:-URI с base64, либо https-ссылка. В каталоге `GET /v1/models` такие модели имеют флаг capabilities.vision: true.

Бесплатно через bitrix/bitrixgpt-5.5 и bitrix/bitrixgpt-5.5-thinking — поддержка изображений встроена. Также поддерживается через BYOK для Anthropic Claude:

  • anthropic/claude-opus-4-6
  • anthropic/claude-sonnet-4-6
  • anthropic/claude-haiku-4-5-20251001

Для Claude backend автоматически конвертирует OpenAI-совместимый image_url-парт в нативный Anthropic-формат — на стороне клиента ничего менять не нужно.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/chat/completions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/bitrixgpt-5.5",
    "messages": [
      {
        "role": "user",
        "content": [
          {"type": "text", "text": "Опиши, что на изображении"},
          {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."}}
        ]
      }
    ],
    "max_tokens": 500
  }'

Ограничения изображения:

Параметр Значение
MIME-типы image/png, image/jpeg, image/gif, image/webp
Максимум на изображение (декодированный размер) 20 MiB¹
Максимум image_url.url в запросе 30 MiB (с запасом на base64-кодирование 22 MiB полезных данных)
Формат url data:<mime>;base64,<данные> или https://<host>/<path>
http://-ссылки в продакшене запрещены (только https или data:)
Максимум content-элементов на сообщение 64
Максимум тела запроса на эндпоинт 30 MiB

¹ MiB (мебибайт, mebibyte) — двоичная единица по стандарту IEC 80000-13: 1 MiB = 2²⁰ = 1 048 576 байт ≈ 1.05 МБ. Лимиты в API проверяются как N × 1024 × 1024, поэтому в сообщении об ошибке валидатор возвращает размер именно в MiB — например, decoded size 21.3 MiB exceeds limit 20 MiB.

Некорректное изображение возвращает 400 invalid_image_payload с указанием индекса парта и конкретной причины: неподдерживаемый MIME, битый base64, превышен размер.

#Пример ответа при ошибке

400 invalid_request — пустой массив messages:

JSON
{
  "error": {
    "message": "messages field is required and must be a non-empty array of {role, content} objects.",
    "type": "invalid_request_error",
    "code": "invalid_request"
  }
}

404 ai_model_not_found — модель не найдена или у ключа нет доступа к ней:

JSON
{
  "error": {
    "message": "Model \"anthropic/claude-imaginary-x\" not found or disabled.",
    "type": "invalid_request_error",
    "code": "ai_model_not_found"
  }
}

402 ai_credentials_not_configured — для модели нет учётных данных провайдера:

JSON
{
  "error": {
    "message": "No AI credentials configured for this provider. Add your own API key or contact the platform administrator.",
    "type": "insufficient_quota",
    "code": "ai_credentials_not_configured"
  }
}

#Ошибки

HTTP Код Описание
400 invalid_request Пустой массив messages, неверная роль, нарушение схемы
400 invalid_image_payload Некорректный image_url — сообщение содержит индекс парта и причину
400 no_default_model model не указана и модель по умолчанию не настроена
402 ai_credentials_not_configured Для модели нет учётных данных провайдера — подключите BYOK
402 insufficient_balance Недостаточно средств для платной модели
403 scope_missing API-ключу не хватает скоупа vibe:ai
404 ai_model_not_found Модель не найдена или отключена
429 rate_limit_exceeded Превышен rate limit — см. секцию «Rate limit» ниже. Заголовок X-RateLimit-Scope указывает уровень (per-key или per-user)
502 ai_provider_unavailable Внешний провайдер вернул ошибку или недоступен
503 model_unavailable Модель отключена и резервной нет
503 pool_exhausted Платформа временно перегружена — повторите запрос через Retry-After секунд (3-7 с jitter'ом)

Полный список общих ошибок API — Ошибки.

#Известные особенности

Автоматическое переключение на резервную модель при сбое провайдера. Если у платной модели произошла ошибка 5xx или таймаут, синхронный запрос повторяется с моделью по умолчанию. В ответе появляется заголовок X-Model-Fallback: <исходный modelId>. В потоковом режиме автоматического переключения нет — клиент получит ошибку в финальном событии перед data: [DONE].

#Жизненный цикл моделей

Модель в каталоге Вайбкод находится в одном из трёх состояний:

Состояние Что происходит при вызове Заголовки ответа
ACTIVE Стандартное обслуживание, без сигналов клиенту
DEPRECATED Запрос обрабатывается на запрошенной модели, в заголовках приходят сигналы об устаревании Deprecation: true, Sunset: <дата отключения> (если назначена), Link: </v1/models/<преемник>>; rel="successor-version" (если назначен преемник), X-Model-Replacement: <id-преемника>
DISABLED Запрос прозрачно перенаправляется на модель-преемник; в response.model приходит фактически отработавшая модель X-Model-Fallback: <запрошенный modelId>, X-Model-Replacement: <фактический modelId>, X-Model-Fallback-Reason: model_disabled

Что делать клиенту:

  • Deprecation: true — пометьте использование модели в логах. Если приходит Link: rel=successor-version — спланируйте переход. До даты Sunset модель работает, после — будет отключена.
  • X-Model-Replacement без X-Model-Fallback — рекомендуемая модель-преемник для будущих запросов. Опционально переключитесь сразу.
  • X-Model-Fallback присутствует — модель уже отключена, и Вайбкод вернул ответ от модели-преемника. response.model содержит её фактический ID. Обновите model в коде на этот ID или оставьте исходный (Вайбкод продолжит автоматическое перенаправление).
  • Заголовки Deprecation, Sunset и Link следуют `RFC 8594`, `RFC 8288` и черновику IETF про Deprecation — стандартные клиенты их распознают.

Если для отключённой модели не настроен преемник, эндпоинт вернёт 503 model_unavailable — это единственный случай, когда DISABLED-перенаправление не срабатывает.

Сигнал в GET /v1/models. В каталоге `GET /v1/models` модели в состоянии DEPRECATED остаются — клиент видит, что они ещё работают. DISABLED-модели из каталога скрыты, но прямой запрос по modelId всё равно проходит через перенаправление на преемник.

Лимит размера тела запроса. Эндпоинт принимает до 30 MiB — это позволяет передать одно изображение размером до 20 MiB (после base64-кодирования около 27 MiB).

Передача content массивом. Для текстовых моделей передавайте content строкой. Передача массива с одним text-элементом тоже работает, но избыточна. Массив обязателен только для запросов с изображениями и других мультимодальных сценариев.

Rate limit. Эндпоинт POST /v1/chat/completions (и его deprecated-алиас POST /v1/ai/chat/completions) защищён двухуровневой корзиной:

  • per-key: по умолчанию 600 запросов в минуту на API-ключ. Корзина привязана к ключу, не к IP — клиент за NAT не делит её с другими ключами того же origin'а; и наоборот, один и тот же ключ с разных IP имеет общий счётчик.
  • per-user: по умолчанию 1500 запросов в минуту на Vibe-пользователя суммарно по всем его ключам. Это защищает платформу от ситуации, когда один аккаунт раздаёт 10+ ключей агентам и каждый утилизирует свой per-key лимит.

Запрос сначала проверяется per-key, затем per-user — первый исчерпанный счётчик выдаёт 429.

Per-key override. Для отдельных ключей (партнёрские интеграции, batch-обработчики) администратор платформы может поднять лимит через поле ApiKey.rateLimit — это значение перекрывает дефолт только для этого ключа, per-user остаётся в силе.

Конфигурация. Глобальные значения per-key / per-user editable в админ-UI /platform/settings → раздел «AI Router» (поля aiRateLimitPerKeyPerMinute и aiRateLimitPerUserPerMinute). Изменения применяются мгновенно — кэш на бэке инвалидируется на PATCH, deploy не нужен.

Формат 429 ответа — OpenAI-совместимый envelope с указанием уровня:

JSON
{
  "error": {
    "type": "rate_limit_exceeded",
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded: 600 requests/minute per API key. Retry after 12s.",
    "scope": "per-key",
    "limit": 600,
    "retryAfter": 12
  }
}

Поле scope принимает одно из двух значений:

  • per-key — упёрлись в лимит конкретного ключа. Снизьте параллелизм этого ключа (например, число одновременных запросов одного агента).
  • per-user — упёрлись в общий лимит аккаунта. Снизьте суммарную нагрузку по всем ключам пользователя или попросите администратора поднять aiRateLimitPerUserPerMinute.

Заголовки ответа. Сверх лимита приходит 429 с заголовками:

  • Retry-After: <секунды> — сколько ждать перед следующей попыткой
  • X-RateLimit-Scope: per-key | per-user — уровень, в который упёрлись (полезно для логов и метрик клиента)

Лимит проверяется после аутентификации — запросы с невалидным ключом (401) не расходуют квоту. Для пакетной обработки (например, обогащение CSV через AI) используйте backoff: при 429 ждите Retry-After секунд, при сетевой ошибке — экспоненциальную задержку с jitter'ом. Promise.all поверх тысяч записей без backoff гарантированно упрётся в один из уровней.

Транзитные 503 pool_exhausted под пиковой нагрузкой. Помимо 429 (превышение клиентской квоты) платформа может отдать 503 с кодом pool_exhausted, если внутренний пул соединений временно исчерпан — например, во время пика нескольких клиентов одновременно. Это сигнал «попробуй ещё раз через несколько секунд», а не «инфраструктура сломана»: ответ всегда содержит заголовок Retry-After (целое число секунд, 3-7 с jitter'ом на серверной стороне, чтобы массовое восстановление не било повторно в пул). Тело: { "error": { "code": "pool_exhausted", "type": "server_error", "retryAfter": <число> } }.

В streaming-режиме (stream: true) статус-код уже отправлен как 200 до того, как платформа узнаёт о перегрузке — поэтому сигнал приходит в финальном SSE-чанке: data: { "error": { "code": "pool_exhausted", "retryAfter": <число> } }, после чего идёт обычный data: [DONE]. Клиент должен распознать code === "pool_exhausted" и переотправить запрос через retryAfter секунд.

Каноничный recipe для пакетных вызовов с backoff на 429/503:

javascript
async function callWithBackoff(makeRequest, { maxAttempts = 5 } = {}) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const res = await makeRequest()
    if (res.status === 429 || res.status === 503) {
      const retryAfterSec = Number(res.headers.get('retry-after')) || 0
      // Уважаем Retry-After, добавляем небольшой клиентский jitter,
      // чтобы N параллельных воркеров не возвращались синхронно.
      const baseMs = retryAfterSec * 1000 || Math.min(1000 * 2 ** attempt, 30000)
      const jitterMs = Math.floor(Math.random() * 1000)
      await new Promise(r => setTimeout(r, baseMs + jitterMs))
      continue
    }
    return res
  }
  throw new Error('Backoff exhausted after maxAttempts')
}

Что важно:

  • Всегда уважайте Retry-After — он соответствует ожидаемому времени восстановления; меньший интервал ничего не ускоряет, а только нагружает платформу.
  • Добавляйте клиентский jitter поверх серверного — 100-1000 мс случайной задержки разрывает синхронные циклы воркеров.
  • Ограничивайте количество попыток (maxAttempts: 5 достаточно для большинства сценариев). После — фейл наружу.
  • При 5xx без Retry-After (например, провайдерская проблема) — exponential backoff: min(2^attempt × 1s, 30s) с jitter'ом.

#Смотрите также