Powrót do bloga

Rozwiązywanie problemówPrzewodnik

429 Too Many Requests z API AI: ponowienia z backoffem

Co oznacza 429 z API AI i jak sobie z nim radzić: wykładniczy backoff z jitterem, Retry-After i współbieżność agentów, z kodem w Pythonie i JavaScripcie.

Autor: CheapTokens TeamOpublikowano Zaktualizowano 5 min czytania

Diagram pętli żądań: odpowiedź 429, krok oczekiwania z rosnącym opóźnieniem i ponowienie
Na tej stronie
  1. Co oznacza 429
  2. Przeczytaj nagłówki, zanim zgadniesz opóźnienie
  3. Python: backoff z jitterem
  4. JavaScript: ta sama pętla
  5. Jak agenci powinni obsługiwać 429
  6. Uruchamiaj pętle agentów na CheapTokens
  7. Jeśli nadal zawodzi

Błąd 429 Too Many Requests z API AI oznacza, że żądanie zostało odrzucone, ponieważ wysłano zbyt dużo zbyt szybko lub osiągnięto limit na koncie. Rozwiązaniem jest odczekanie i ponowna próba z wykładniczym backoffem i jitterem, użycie nagłówka Retry-After, gdy jest obecny w odpowiedzi, oraz ograniczenie liczby równoległych żądań wysyłanych przez agenta. Nie ponawiaj próby przy 429, który w rzeczywistości jest limitem wydatków lub salda: czekanie tego nie naprawi.

Ten przewodnik opiera się na oficjalnych dokumentacjach limitów szybkości OpenAI i Anthropic, obie sprawdzone 22 października 2026. Żadna ze stron nie podaje daty ostatniej aktualizacji.

Co oznacza 429#

OpenAI opisuje 429 jako żądanie odrzucone z powodu przekroczenia limitu szybkości (rate_limit_error, z kodem slow_down, gdy ruch rośnie zbyt szybko) lub z powodu osiągnięcia twardego limitu wydatków. Anthropic również używa rate_limit_error dla 429. 429 związany z limitem wydatków nie ma nagłówka retry-after i będzie się powtarzać, dopóki dostęp nie zostanie przywrócony.

Dwa sąsiednie kody łatwo pomylić z tym błędem. 503 server_is_overloaded OpenAI i 529 overloaded_error Anthropic to warunki przeciążenia, a nie Twój limit szybkości. Zwykle obsługuje się je tą samą pętlą ponowień, ale wskazują na inną przyczynę.

Zatem istnieją trzy przypadki, a każdy wymaga innej reakcji:

Odpowiedź Przyczyna Co zrobić
429 limit szybkości Zbyt wiele żądań lub tokenów na minutę Poczekaj, ponów z backoffem
429 limit wydatków lub salda Osiągnięto limit konta Nie ponawiaj; popraw limit lub doładuj saldo
503 / 529 przeciążenie Strona dostawcy jest zajęta Ponów z backoffem, z ograniczeniem

Jeśli skończyło się saldo, przeczytaj błąd „not enough tokens”. W przypadku awarii na poziomie sieci, takich jak przekroczenia limitu czasu i odrzucone połączenia, zobacz typowe błędy połączenia.

Przeczytaj nagłówki, zanim zgadniesz opóźnienie#

OpenAI dokumentuje następujące nagłówki w odpowiedziach z limitem szybkości: Retry-After oraz x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-reset-requests z wariantami -tokens. Inne API mogą wysyłać ich podzbiór lub żadnego, więc napisz klienta tak, aby używał Retry-After, gdy jest obecny, a w przeciwnym razie korzystał z własnego harmonogramu.

Wskazówki OpenAI dotyczące ponowień w skrócie:

  • Traktuj Retry-After jako minimalny czas oczekiwania i dodaj do niego niewielkie losowe opóźnienie.
  • Bez niego używaj wykładniczego backoffu z jitterem.
  • Ogranicz zarówno liczbę prób, jak i całkowity czas ponowień.
  • Nie nakładaj własnych ponowień na ponowienia SDK.
  • Nie ponawiaj prób przy błędach limitu lub rozliczeń.
  • Nieudane żądania nadal liczą się do limitu na minutę, więc agresywna pętla pogarsza sytuację.
  • W przypadku streamingu nie odtwarzaj żądania po tym, jak już skonsumowałeś jego wynik.

Python: backoff z jitterem#

Oficjalny pakiet openai działa z każdym punktem końcowym zgodnym z OpenAI. Wyłącz własne ponowienia SDK (max_retries=0), aby nie mnożyły się z Twoimi. Identyfikator modelu pochodzi z dokumentacji CheapTokens; identyfikatory różnią się między dostawcami.

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)

Górna granica opóźnienia bez nagłówka rośnie 1, 2, 4, 8 sekund (z limitem 30), a random.uniform wybiera losową wartość poniżej niej, więc wiele procesów roboczych nie budzi się w tym samym momencie. Retry-After może być również datą HTTP; powyższy kod obsługuje formę numeryczną, a w przeciwnym razie korzysta z backoffu.

JavaScript: ta sama pętla#

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);
    }
  }
}

Gdy to działa, wywołanie z limitem szybkości po prostu trwa dłużej, a następnie zwraca normalne ukończenie. Gdy nadal zawodzi po ostatniej próbie, zgłaszany jest oryginalny błąd, więc Twój kod może go zalogować lub zakolejkować zadanie na później.

Jak agenci powinni obsługiwać 429#

Pętla agenta wysyła wiele krótkich wywołań, a kilka agentów lub wywołań narzędzi może działać jednocześnie. Samo ponawianie nie wystarczy; zmniejsz szybkość żądań u źródła:

  1. Ogranicz współbieżność. Semafor 3–5 równoległych wywołań to rozsądny początek; zwiększaj go tylko wtedy, gdy błędy pozostają rzadkie.
  2. Udostępnij jeden limiter. Jeśli kilka procesów roboczych używa tego samego klucza, dzielą te same limity, więc jedna pętla ponowień na proces nie wystarczy: użyj jednej kolejki lub jednego semafora dla procesu.
  3. Wycofaj się globalnie. Gdy jedno wywołanie otrzyma 429 z opóźnieniem, wstrzymaj całą pulę na ten czas, a nie tylko zawodne wywołanie.
  4. Utrzymuj mniejsze żądania. Mniej tokenów na żądanie oznacza mniejszą presję na limity tokenów na minutę: przytnij historię i dane wyjściowe narzędzi. Zobacz jak oszacować koszt agenta AI, gdzie zużywają się tokeny.
  5. Zatrzymaj się przy błędach niepodlegających ponowieniu. 401, 400 lub błąd salda nie naprawi się sam; zawiedź szybko i zgłoś.
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

Uruchamiaj pętle agentów na CheapTokens#

W przypadku pętli agentów z wieloma krótkimi wywołaniami CheapTokens oferuje MiniMax-M2.7: jedna cena o każdej porze dnia, kontekst 180K, do 8192 tokenów na odpowiedź i jedno saldo dla wszystkich modeli. Wyjście kosztuje $0.15 za 1M tokenów, a łącznie, licząc wejście i wyjście razem, jest taniej niż OpenRouter. Działa przez punkt końcowy zgodny z OpenAI https://api.cheaptokens.dev/v1. Powyższy kod już wskazuje ten punkt końcowy; ustaw identyfikator modelu z dokumentacji CheapTokens i uruchom go.

Aktualne ceny dla obciążenia 500 000 wejściowych i 100 000 wyjściowych:

Model Koszt 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 tokenów wejściowych i 100 000 tokenów wyjściowych łącznie. Ceny za 1 mln tokenów są aktualne i zmieniają się wraz z naszymi taryfami; kolumna OpenRouter pokazuje ceny referencyjne dla tego samego obciążenia.

Zobacz wszystkie ceny, dokumentację konfiguracji i utwórz klucz, aby wysłać pierwsze żądanie.

Jeśli nadal zawodzi#

  • 429 przy pierwszym żądaniu. Przeczytaj typ i treść błędu w treści odpowiedzi: 429 związany z limitem wydatków lub konta będzie się powtarzać przy ponowieniu, dopóki limit nie zostanie podniesiony lub dostęp nie zostanie przywrócony, podczas gdy 429 związany z limitem szybkości ustępuje po przerwie.
  • 429 powraca przy długiej pętli. Możesz nakładać ponowienia: ponowienia SDK razy Twoje ponowienia. Ustaw SDK na zero, jak w przykładach.
  • Błędy przychodzą seriami na początku partii. Zwiększaj stopniowo zamiast uruchamiać wszystkich procesów roboczych naraz; OpenAI zgłasza slow_down dla ruchu, który rośnie zbyt szybko.
  • Wywołanie strumieniowe nie powiodło się w połowie. Nie odtwarzaj go bezmyślnie po skonsumowaniu wyniku; zdecyduj, czy zrestartować całą odpowiedź, czy kontynuować od tego, co masz.
  • Brak Retry-After w odpowiedzi. Kod korzysta z backoffu z jitterem, więc nie trzeba nic zmieniać.