Для AI-агентов: markdown этой страницы — /docs-content/ai/chat/rate-limits.md индекс документации — /llms.txt
Лимиты запросов и повторы
Обращения к моделям ограничены двухуровневой корзиной, а при пиковой нагрузке платформа может ненадолго отказывать в обслуживании. Оба сигнала — 429 и 503 — означают «повтори позже», а не «запрос неверен». Ни один из них не списывает квоту.
Два уровня корзины
- На ключ — по умолчанию
600запросов в минуту. Корзина привязана к ключу, а не к IP-адресу: клиент за NAT не делит её с другими ключами, а один и тот же ключ с разных адресов имеет общий счётчик. - На пользователя — по умолчанию
1500запросов в минуту суммарно по всем ключам одного пользователя. Это защищает платформу, когда один аккаунт раздаёт десяток ключей агентам и каждый выбирает свой лимит.
Запрос проверяется сначала по ключу, затем по пользователю. Первый исчерпанный счётчик выдаёт 429.
Для отдельных ключей — партнёрских интеграций, пакетных обработчиков — администратор платформы может поднять лимит на ключ. Лимит на пользователя при этом продолжает действовать.
Ответ `429`
{
"error": {
"type": "rate_limit_exceeded",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded (per-key). Limit: 600 per minute. Retry after 12 seconds.",
"scope": "per-key",
"limit": 600,
"retryAfter": 12
}
}
Поле scope принимает одно из двух значений:
per-key— исчерпан лимит конкретного ключа. Снизьте число одновременных запросов этого ключа.per-user— исчерпан общий лимит аккаунта. Снизьте суммарную нагрузку по всем ключам или попросите администратора платформы поднять лимит.
Вместе с 429 приходят заголовки:
Retry-After: <секунды>— сколько ждать перед следующей попыткой.X-RateLimit-Scope: per-key | per-user— уровень, в который упёрлись. Полезно для логов и метрик клиента.
Лимит проверяется после проверки ключа, поэтому запросы с некорректным ключом (401) квоту не расходуют.
Транзитные `503`: `pool_exhausted` и `db_transient`
Помимо 429, который означает превышение клиентской квоты, платформа может отдать 503 с одним из двух кодов. pool_exhausted — внутренний пул соединений с базой временно исчерпан, например при одновременном пике у нескольких клиентов. db_transient — транзакция в базе закрылась или истекла до того, как операция завершилась, и платформа откатила её целиком. Причины разные, реакция одна: это сигнал «попробуй ещё раз через несколько секунд», а не «инфраструктура сломана». Ответ всегда содержит заголовок Retry-After — целое число секунд от 3 до 7, со случайным разбросом на стороне сервера, чтобы массовое восстановление не било повторно в пул.
В обычном (не потоковом) режиме обрабатывайте оба кода одинаково — по статусу 503 вместе с Retry-After, а не списком известных вам строк: так клиент переживёт появление третьего транзитного кода.
{
"error": {
"code": "pool_exhausted",
"type": "server_error",
"retryAfter": 5
}
}
В потоковом режиме (stream: true) статус 200 уже отправлен до того, как платформа узнаёт о перегрузке, поэтому сигнал приходит последним событием потока: data: { "error": { "code": "pool_exhausted", "retryAfter": <число> } }, после чего идёт обычный data: [DONE]. Распознайте code === "pool_exhausted" и повторите запрос через retryAfter секунд.
Есть и третий сигнал перегрузки — 429 ai_congested. Он приходит, когда перегружен пул AI-кластера. Запрос при этом не выполнялся и списания нет. Отличить его от превышения квоты можно по заголовку X-AI-Admission: shed, которого нет у ответа 429 rate_limit_exceeded.
Ответ `429 ai_pacing_limited`
Четвёртый ответ со статусом 429 не связан с нагрузкой на платформу. Он приходит, когда администратор портала включил равномерное расходование AI-квоты и вызов пробил суточное или недельное окно. Тело такого ответа приходит в конверте { "success": false, "error": { ... } }, а не в сыром формате остальных ошибок этой страницы, и несёт поля reason, overageDenied и resetAt. Разбор полей и режимов — на странице AI-квота компании.
Ответ `429 ai_provider_cooldown`
Пятый ответ со статусом 429 приходит, когда кластер моделей отвечает ошибками подряд и платформа перестаёт обращаться к нему, чтобы повторы не мешали ему восстановиться. Пауза снимается сама; заголовок Retry-After несёт её остаток в секундах. Запрос не выполнялся, списания нет.
Длительность паузы не фиксирована: первая — около минуты, но если по её истечении кластер всё ещё отвечает ошибками, следующая пауза становится длиннее (до четырёх минут). Поэтому берите время ожидания из заголовка Retry-After, а не из фиксированной константы в своём коде.
Отличить его от остальных 429 можно по коду и по отсутствию заголовков: у него нет ни X-RateLimit-Scope (он есть у rate_limit_exceeded), ни X-AI-Admission (он есть у ai_congested). В потоковом режиме статус 200 уже отправлен, поэтому сигнал приходит последним событием потока — тем же способом, что и pool_exhausted, с полем retryAfter внутри кадра ошибки.
Ответ `429 ai_deadline_exceeded`
Шестой ответ со статусом 429 приходит, когда запрос не уложился в срок обслуживания — предельное время, которое платформа готова держать вызов, прежде чем честно отказать. Раньше такой вызов просто обрывался по таймауту без тела; теперь у него есть объявленная граница и понятный отказ.
Тело несёт код ai_deadline_exceeded, тип rate_limit_exceeded и поле retryAfter. Вместе с ним приходят два заголовка: Retry-After — пауза перед повтором в секундах, и X-AI-Deadline-Ms — бюджет, который реально был у этого запроса, в миллисекундах.
Свой, более короткий бюджет можно назвать заголовком запроса X-AI-Deadline-Ms (миллисекунды). Он только сокращает срок: значение больше платформенного не выдаётся, а там, где срок отключён администратором, заголовок его не включает. Нечисловое или неположительное значение считается неуказанным и вызов не отклоняет.
Срок отсчитывается от прихода запроса и покрывает всю его обработку целиком, включая автоматический повтор на резервной модели. В потоковом режиме статус 200 уже отправлен, поэтому отказ приходит последним событием потока — как у pool_exhausted и stream_idle_timeout, с полем retryAfter внутри кадра ошибки.
Отличить его от stream_idle_timeout просто: тот означает «кластер замолчал посреди ответа», а этот — «времени на весь запрос больше нет», и он срабатывает даже когда фрагменты продолжают идти.
Ошибки
| HTTP | Код | Описание |
|---|---|---|
| 429 | rate_limit_exceeded |
Превышен лимит запросов. Уровень — в поле scope и заголовке X-RateLimit-Scope |
| 429 | ai_congested |
Пул AI-кластера перегружен. Запрос не выполнялся, списания нет. Ответ несёт заголовок X-AI-Admission: shed |
| 429 | ai_pacing_limited |
Пробито суточное или недельное окно равномерного расходования квоты. Повторите запрос по заголовку Retry-After |
| 429 | ai_provider_cooldown |
Кластер моделей временно недоступен, платформа держит короткую паузу. Запрос не выполнялся, списания нет. Повторите его через число секунд из Retry-After |
| 429 | ai_deadline_exceeded |
Запрос не уложился в срок обслуживания. Повторите его через число секунд из Retry-After; фактический бюджет — в заголовке X-AI-Deadline-Ms |
| 503 | pool_exhausted |
Платформа временно перегружена. Повторите запрос через число секунд из Retry-After |
| 503 | db_transient |
Транзакция в базе закрылась до завершения операции, изменение откатилось целиком. Повторите запрос через число секунд из Retry-After |
Полный список общих ошибок API — Ошибки.
Готовый рецепт повторов
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 и добавляем небольшой случайный разброс,
// чтобы параллельные обработчики не возвращались синхронно.
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('Повторы исчерпаны')
}
Что важно:
- Всегда соблюдайте
Retry-After. Он соответствует ожидаемому времени восстановления, меньший интервал ничего не ускоряет, а только нагружает платформу. - Добавляйте собственный случайный разброс поверх серверного — задержка в 100-1000 мс разрывает синхронные циклы обработчиков.
- Ограничивайте количество попыток. Пяти достаточно для большинства сценариев, дальше возвращайте ошибку наружу.
- При
5xxбезRetry-After, например при сбое на стороне провайдера, увеличивайте паузу по экспоненте:min(2^attempt × 1s, 30s)со случайным разбросом. - Не запускайте
Promise.allповерх тысяч записей. Без пауз пакетная обработка упрётся в один из уровней корзины.