#Создать чат-комплишен
Формат ответа: 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 — личный ключ
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-приложение
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 — личный ключ
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-приложение
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 | Сумма токенов в запросе и ответе |
#Пример ответа
{
"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:
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. Структуру результата опишите явно:
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"}
}'
Ответ:
{
"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/me → ai.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. |
Пример:
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:
{
"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, а content — null. Аргументы функции придут в tool_calls[].function.arguments как JSON-строка.
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"
}'
Ответ:
{
"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:
{
"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-6anthropic/claude-sonnet-4-6anthropic/claude-haiku-4-5-20251001
Для Claude backend автоматически конвертирует OpenAI-совместимый image_url-парт в нативный Anthropic-формат — на стороне клиента ничего менять не нужно.
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:
{
"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 — модель не найдена или у ключа нет доступа к ней:
{
"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 — для модели нет учётных данных провайдера:
{
"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 с указанием уровня:
{
"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:
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'ом.
#Смотрите также
- Чат-комплишены — обзор подраздела
- Список моделей — выбор модели по характеристикам и цене
- AI Router — полный пример сценария с распознаванием и записью в CRM
- Лимиты и оптимизация — общие rate-limits Vibe API