Назад до блогу

Вирішення проблемПосібник

429 Too Many Requests від AI API: повтори з відступом

Що означає 429 від AI API і як його обробити: експоненційний відступ із джиттером, Retry-After і паралельність агента, з кодом на Python і JavaScript.

Автор: CheapTokens TeamОпубліковано Оновлено 5 хв читання

Діаграма циклу запитів: відповідь 429, крок очікування зі зростаючою затримкою та повтор
На цій сторінці
  1. Що означає 429
  2. Прочитайте заголовки, перш ніж вгадувати затримку
  3. Python: відступ із джиттером
  4. JavaScript: той самий цикл
  5. Як агентам обробляти 429
  6. Запускайте цикли агентів на CheapTokens
  7. Якщо все ще не працює

429 Too Many Requests від AI API означає, що запит відхилено, бо ви надіслали забагато занадто швидко або досягли ліміту на своєму акаунті. Рішення — зачекати й повторити з експоненційним відступом і джиттером, використовувати заголовок Retry-After, коли він є у відповіді, та обмежити кількість запитів, які ваш агент надсилає паралельно. Не повторюйте 429, який насправді є лімітом витрат або балансу: очікування це не виправить.

Цей посібник базується на офіційних посібниках з обмежень швидкості OpenAI та Anthropic, обидва перевірено 22 жовтня 2026 року. Жодна сторінка не показує дату останнього оновлення.

Що означає 429#

OpenAI описує 429 як запит, відхилений через перевищення обмеження швидкості (rate_limit_error, з кодом slow_down, коли трафік зростає занадто швидко), або через досягнення жорсткого ліміту витрат. Anthropic також використовує rate_limit_error для 429. 429 через ліміт витрат не має заголовка retry-after і продовжує збоїти, доки доступ не відновиться.

Двох сусідів легко з ним сплутати. 503 server_is_overloaded від OpenAI та 529 overloaded_error від Anthropic — це умови перевантаження, а не ваше обмеження швидкості. Зазвичай їх обробляють тим самим циклом повторів, але вони говорять про іншу причину.

Отже, є три випадки, і кожен потребує різної реакції:

Відповідь Причина Що робити
429 обмеження швидкості Забагато запитів або токенів за хвилину Зачекати, повторити з відступом
429 ліміт витрат або балансу Досягнуто ліміту акаунта Не повторювати; виправити ліміт або поповнити баланс
503 / 529 перевантажено Сторона провайдера зайнята Повторити з відступом, з обмеженням

Якщо ваш баланс вичерпано, натомість прочитайте помилку "not enough tokens". Для збоїв на рівні мережі, як-от тайм-аути та відмови в з'єднанні, дивіться поширені помилки з'єднання.

Прочитайте заголовки, перш ніж вгадувати затримку#

OpenAI документує ці заголовки у відповідях з обмеженням швидкості: Retry-After, а також x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-reset-requests з варіантами -tokens. Інші API можуть надсилати підмножину з них або жодного, тому напишіть клієнт так, щоб використовувати Retry-After, коли він присутній, і повертатися до власного графіка, коли його немає.

Поради OpenAI щодо повторів, коротко:

  • Вважайте Retry-After мінімальним очікуванням і додайте зверху невелику випадкову затримку.
  • Без нього використовуйте експоненційний відступ із джиттером.
  • Обмежте як кількість спроб, так і загальний час повторів.
  • Не накладайте власні повтори поверх повторів SDK.
  • Не повторюйте помилки квоти або білінгу.
  • Невдалі запити все одно зараховуються до ліміту за хвилину, тож агресивний цикл робить гірше.
  • Для стрімінгу не відтворюйте запит після того, як ви вже спожили з нього вивід.

Python: відступ із джиттером#

Офіційний пакет openai працює з будь-якою сумісною з OpenAI кінцевою точкою. Вимкніть власні повтори SDK (max_retries=0), щоб вони не множилися з вашими. Ідентифікатор моделі береться з документації CheapTokens; ідентифікатори різняться між провайдерами.

python
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-датою; наведений вище код обробляє числову форму, а інакше повертається до відступу.

JavaScript: той самий цикл#

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#

Цикл агента надсилає багато коротких викликів, і кілька агентів або викликів інструментів можуть виконуватися одночасно. Повторів самих по собі недостатньо; знижуйте частоту запитів у джерелі:

  1. Обмежте паралельність. Семафор на 3–5 паралельних викликів — розумний початок; підвищуйте його, лише поки помилки залишаються рідкісними.
  2. Спільний обмежувач. Якщо кілька воркерів використовують той самий ключ, вони поділяють ті самі ліміти, тож одного циклу повторів на воркер недостатньо: використовуйте одну чергу або один семафор для процесу.
  3. Відступайте глобально. Коли один виклик отримує 429 із затримкою, призупиніть увесь пул на цей час, а не лише виклик, що збоїть.
  4. Робіть запити меншими. Менше токенів на запит означає менший тиск на ліміти токенів за хвилину: скоротіть історію та вивід інструментів. Дивіться як оцінити вартість AI-агента, щоб зрозуміти, куди йдуть токени.
  5. Зупиняйтеся на помилках, що не підлягають повтору. 401, 400 або помилка балансу самі не виправляться; швидко завершуйте з помилкою та повідомляйте.
python
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://api.cheaptokens.dev/v1. Наведений вище код уже вказує на цю кінцеву точку; задайте ідентифікатор моделі з документації CheapTokens і запускайте.

Актуальні ціни для навантаження 500 000 вхідних і 100 000 вихідних:

Модель Вартість OpenRouter
GLM-5.3-Flash $0.014 $0.0282
DeepSeek-V4-Flash $0.0175 $0.0394
MiniMax-M2.7 $0.04 $0.27

500 000 вхідних і 100 000 вихідних токенів загалом. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.

Дивіться усі ціни, документацію з налаштування і створіть ключ, щоб надіслати свій перший запит.

Якщо все ще не працює#

  • 429 на найпершому запиті. Прочитайте тип і повідомлення помилки в тілі відповіді: 429 через ліміт витрат або акаунта продовжує збоїти при повторі, доки ліміт не підвищать або доступ не відновиться, тоді як 429 через обмеження швидкості зникає після паузи.
  • 429 продовжує надходити з довгим циклом. Можливо, ви накладаєте повтори: повтори SDK помножені на ваші повтори. Встановіть повтори SDK на нуль, як у прикладах.
  • Помилки надходять хвилями на початку пакета. Нарощуйте поступово, а не запускайте всіх воркерів одразу; OpenAI повідомляє slow_down для трафіку, що зростає занадто швидко.
  • Стрім-виклик збійв посередині. Не відтворюйте його наосліп після того, як ви спожили вивід; вирішіть, чи перезапустити всю відповідь, чи продовжити з того, що маєте.
  • Немає Retry-After у відповіді. Код повертається до відступу з джиттером, тож жодних змін не потрібно.