Решение проблемИнструкция
429 Too Many Requests в API нейросети: повторы с backoff
Что значит 429 в API нейросети и как её обработать: экспоненциальная задержка с джиттером, Retry-After и параллелизм агента, код на Python и JavaScript.

Содержание
Ответ 429 Too Many Requests от API нейросети значит, что запрос отклонён: вы отправили слишком много и слишком быстро, либо упёрлись в лимит аккаунта. Решение — подождать и повторить с экспоненциальной задержкой и джиттером, взять паузу из заголовка Retry-After, если он есть, и ограничить число параллельных запросов агента. Не повторяйте 429, который на самом деле лимит расходов или баланса: ожидание его не исправит.
Руководство основано на официальных материалах о лимитах OpenAI и Anthropic, проверенных 22 октября 2026 года. На обеих страницах нет даты обновления.
Что значит 429#
OpenAI описывает 429 как запрос, отклонённый из-за превышения лимита (rate_limit_error, с кодом slow_down, когда трафик растёт слишком быстро), либо из-за достигнутого жёсткого лимита расходов. У Anthropic 429 тоже приходит как rate_limit_error. 429 из-за лимита расходов приходит без заголовка retry-after и продолжает повторяться, пока доступ не вернётся.
С ним легко перепутать два соседних ответа. 503 server_ у OpenAI и 529 overloaded_ у Anthropic — это перегрузка, а не ваш лимит. Обычно их обрабатывают тем же циклом повторов, но причина у них другая.
Итого три случая, и реакция на каждый своя:
Прокрутите таблицу вправо →
| Ответ | Причина | Что делать |
|---|---|---|
| 429, лимит запросов | Слишком много запросов или токенов в минуту | Подождать, повторить с backoff |
| 429, лимит расходов или баланса | Достигнут лимит аккаунта | Не повторять; поднять лимит или пополнить баланс |
| 503 / 529, перегрузка | Занята сторона провайдера | Повторять с backoff и ограничением |
Если закончился баланс, читайте ошибку «not enough tokens». Про сетевые сбои вроде таймаутов и отказов в соединении — частые ошибки подключения.
Сначала заголовки, потом догадки о паузе#
OpenAI документирует такие заголовки в ответах с ограничением: Retry-After, а также x-, x-, x- и варианты -tokens. Другие API могут присылать часть из них или ни одного, поэтому пишите клиент так: если Retry-After есть — используйте его, если нет — свой график.
Совет OpenAI по повторам, коротко:
Retry-After— это минимальное ожидание; добавьте сверху небольшую случайную паузу.- Без него — экспоненциальный backoff с джиттером.
- Ограничьте и число попыток, и общее время повторов.
- Не накладывайте свои повторы на повторы SDK.
- Не повторяйте ошибки квоты и биллинга.
- Неудачные запросы тоже считаются в минутный лимит, поэтому агрессивный цикл делает хуже.
- При стриминге не повторяйте запрос, если вы уже получили часть вывода.
Python: backoff с джиттером#
Официальный пакет openai работает с любым OpenAI-совместимым адресом. Отключите собственные повторы SDK (max_retries=0), чтобы они не умножались на ваши. Id модели берите из документации CheapTokens: у разных провайдеров они отличаются.
import os
import random
import time
from openai import OpenAI, APIStatusError
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
max_retries=0, # we retry ourselves
)
RETRYABLE = {429, 500, 502, 503, 504}
def chat_with_retry(model, messages, max_attempts=5, max_total=60.0):
started = time.monotonic()
for attempt in range(max_attempts):
try:
return client.chat.completions.create(model=model, messages=messages)
except APIStatusError as err:
last = attempt == max_attempts - 1
if err.status_code not in RETRYABLE or last:
raise
header = err.response.headers.get("retry-after")
if header and header.replace(".", "", 1).isdigit():
delay = float(header) + random.uniform(0, 1) # minimum wait + jitter
else:
delay = random.uniform(0, min(30.0, 2.0 ** attempt)) # full jitter
if time.monotonic() - started + delay > max_total:
raise
time.sleep(delay)Верхняя граница паузы без заголовка растёт 1, 2, 4, 8 секунд (максимум 30), а random.uniform выбирает случайное значение в её пределах, чтобы много воркеров не просыпались одновременно. Retry-After может быть и HTTP-датой; код выше обрабатывает числовую форму, иначе переходит на backoff.
JavaScript: тот же цикл#
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.cheaptokens.dev/v1",
apiKey: process.env.CHEAPTOKENS_API_KEY,
maxRetries: 0, // we retry ourselves
});
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export async function chatWithRetry(model, messages, maxAttempts = 5, maxTotalMs = 60000) {
const started = Date.now();
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await client.chat.completions.create({ model, messages });
} catch (err) {
const last = attempt === maxAttempts - 1;
if (!RETRYABLE.has(err.status) || last) throw err;
const header = Number(err.headers?.get?.("retry-after"));
const delayMs = Number.isFinite(header) && header > 0
? header * 1000 + Math.random() * 1000 // minimum wait + jitter
: Math.random() * Math.min(30000, 2 ** attempt * 1000); // full jitter
if (Date.now() - started + delayMs > maxTotalMs) throw err;
await sleep(delayMs);
}
}
}Когда всё работает, запрос с ограничением просто занимает больше времени и возвращает обычный ответ. Если после последней попытки ошибка осталась, поднимается исходное исключение — залогируйте его или отложите задачу.
Как агенту обрабатывать 429#
Цикл агента отправляет много коротких запросов, а несколько агентов или вызовов инструментов идут одновременно. Одних повторов мало: снижайте частоту запросов у источника.
- Ограничьте параллелизм. Семафор на 3–5 запросов — разумное начало; повышайте, пока ошибки редки.
- Один ограничитель на всех. Если несколько воркеров используют один ключ, лимиты у них общие, и цикла повторов на каждого мало: нужна одна очередь или один семафор на процесс.
- Пауза общая. Когда один вызов получил 429 с паузой, остановите весь пул на это время, а не только упавший вызов.
- Делайте запросы меньше. Меньше токенов в запросе — меньше давление на лимиты токенов в минуту: обрезайте историю и вывод инструментов. Куда уходят токены — в статье как оценить стоимость ИИ-агента.
- Останавливайтесь на ошибках без повтора. 401, 400 или ошибка баланса сами не пройдут: завершайте сразу и сообщайте.
import asyncio
limit = asyncio.Semaphore(4) # at most 4 requests in flight
async def run(job):
async with limit:
return await call_model(job) # your async call with the retry loop aboveЦиклы агентов на CheapTokens#
Для циклов агентов с множеством коротких вызовов в CheapTokens — MiniMax-M2.7: одна цена в любое время суток, контекст 180K, до 8 192 токенов в ответе и один баланс на все модели. Выход стоит $0.15 за 1M токенов, а в сумме, с учётом входа и выхода, дешевле OpenRouter. Работает через OpenAI-совместимый адрес https:/. Код выше уже указывает на этот адрес; подставьте id модели из документации CheapTokens и запускайте.
Живые цены для нагрузки 500 000 токенов на входе и 100 000 на выходе:
| Модель | Вход $/1M | Выход $/1M | Стоимость | OpenRouter |
|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.014 | $0.0282 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.0175 | $0.0394 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.04 | $0.27 |
500 000 входных и 100 000 выходных токенов всего. Цены за 1M токенов актуальны и меняются вместе с нашими тарифами; столбец OpenRouter показывает справочные цены для той же нагрузки.
Смотрите все цены и документацию по настройке, создайте ключ и отправьте первый запрос.
Если всё равно не работает#
- 429 на самом первом запросе. Прочитайте тип и текст ошибки в теле ответа: 429 из-за лимита расходов или аккаунта продолжает падать при повторах, пока лимит не поднят или доступ не вернулся, а 429 из-за лимита скорости проходит после паузы.
- 429 приходит в длинном цикле. Возможно, повторы накладываются: повторы SDK умножаются на ваши. Выставьте для SDK ноль, как в примерах.
- Ошибки пачками в начале пакета. Наращивайте нагрузку постепенно, а не запускайте всех воркеров разом; OpenAI сообщает
slow_down, когда трафик растёт слишком быстро. - Стриминг оборвался посередине. Не повторяйте его вслепую после того, как получили часть вывода: решите, начинать ответ заново или продолжать с того, что есть.
- В ответе нет Retry-After. Код переходит на backoff с джиттером, менять ничего не нужно.



