429 Too Many Requests de uma API de IA: retries com backoff
O que um 429 de uma API de IA significa e como tratá-lo: backoff exponencial com jitter, Retry-After e concorrência de agentes, com código em Python e JavaScript.

Nesta página
Um 429 Too Many Requests de uma API de IA significa que a requisição foi rejeitada porque você enviou demais rápido demais, ou atingiu um limite na sua conta. A solução é esperar e tentar novamente com backoff exponencial e jitter, usar o cabeçalho Retry-After quando a resposta tiver um, e limitar quantas requisições seu agente envia em paralelo. Não repita um 429 que na verdade é um limite de gasto ou de saldo: esperar não vai resolver.
Este guia é baseado nos guias oficiais de rate limit da OpenAI e da Anthropic, ambos verificados em 22 de outubro de 2026. Nenhuma das páginas mostra data de última atualização.
O que um 429 significa#
A OpenAI descreve um 429 como uma requisição rejeitada por exceder um rate limit (rate_limit_error, com o código slow_down quando o tráfego cresce rápido demais), ou porque um limite rígido de gasto foi atingido. A Anthropic também usa rate_limit_error para 429. Um 429 de limite de gasto não tem cabeçalho retry-after e continua falhando até o acesso ser retomado.
Dois vizinhos são fáceis de confundir com ele. O 503 server_ da OpenAI e o 529 overloaded_ da Anthropic são condições de sobrecarga, não o seu rate limit. Normalmente são tratados com o mesmo loop de retry, mas dizem algo diferente sobre a causa.
Então há três casos, e cada um exige uma reação diferente:
Role a tabela para a direita →
| Resposta | Causa | O que fazer |
|---|---|---|
| 429 rate limit | Requisições ou tokens por minuto em excesso | Esperar, repetir com backoff |
| 429 limite de gasto ou saldo | Limite da conta atingido | Não repetir; ajustar o limite ou recarregar o saldo |
| 503 / 529 overloaded | O lado do provedor está ocupado | Repetir com backoff, com um teto |
Se seu saldo acabou, leia o erro "not enough tokens". Para falhas em nível de rede, como timeouts e conexões recusadas, veja erros de conexão comuns.
Leia os cabeçalhos antes de chutar um atraso#
A OpenAI documenta estes cabeçalhos em respostas com rate limit: Retry-After, e x-, x-, x- com variantes -tokens. Outras APIs podem enviar um subconjunto ou nenhum deles, então escreva o cliente para usar Retry-After quando estiver presente e recorrer ao seu próprio cronograma quando não estiver.
A orientação da OpenAI sobre retries, em resumo:
- Trate
Retry-Aftercomo a espera mínima e adicione um pequeno atraso aleatório por cima. - Sem ele, use backoff exponencial com jitter.
- Limite tanto o número de tentativas quanto o tempo total de retry.
- Não empilhe seus próprios retries em cima dos retries do SDK.
- Não repita erros de cota ou de cobrança.
- Requisições com falha ainda contam para o limite por minuto, então um loop agressivo piora as coisas.
- Para streaming, não reenvie uma requisição depois de já ter consumido saída dela.
Python: backoff com jitter#
O pacote oficial openai funciona com qualquer endpoint compatível com OpenAI. Desative os retries do próprio SDK (max_retries=0) para que não se multipliquem com os seus. O id do modelo vem da documentação do CheapTokens; os ids diferem entre provedores.
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)O limite superior do atraso sem cabeçalho cresce 1, 2, 4, 8 segundos (limitado a 30), e random.uniform escolhe um valor aleatório abaixo dele, para que muitos workers não acordem todos no mesmo momento. Retry-After também pode ser uma data HTTP; o código acima trata a forma numérica e, caso contrário, recorre ao backoff.
JavaScript: o mesmo loop#
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);
}
}
}Quando funciona, uma chamada com rate limit simplesmente demora mais e depois retorna uma completion normal. Quando ainda falha após a última tentativa, o erro original é lançado, para que seu código possa registrá-lo ou enfileirar o job para depois.
Como agentes devem tratar 429#
Um loop de agente envia muitas chamadas curtas, e vários agentes ou chamadas de ferramentas podem rodar ao mesmo tempo. Repetir sozinho não basta; reduza a taxa de requisições na origem:
- Limite a concorrência. Um semáforo de 3–5 chamadas paralelas é um começo sensato; aumente só enquanto os erros permanecerem raros.
- Compartilhe um único limitador. Se vários workers usam a mesma chave, eles compartilham os mesmos limites, então um loop de retry por worker não basta: use uma fila ou um semáforo para o processo.
- Faça backoff global. Quando uma chamada recebe um 429 com atraso, pause o pool inteiro por esse tempo, não apenas a chamada que falhou.
- Mantenha as requisições menores. Menos tokens por requisição significa menos pressão sobre os limites de tokens por minuto: corte histórico e saída de ferramentas. Veja como estimar o custo de um agente de IA para saber para onde vão os tokens.
- Pare em erros não repetíveis. Um 401, 400 ou um erro de saldo não se resolve sozinho; falhe rápido e reporte.
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 aboveRode loops de agente no CheapTokens#
Para loops de agente com muitas chamadas curtas, o CheapTokens oferece o MiniMax-M2.7: um preço a qualquer hora do dia, contexto de 180K, até 8.192 tokens por resposta e um saldo para todos os modelos. A saída custa $0.15 por 1M de tokens e, no total, com entrada e saída contadas juntas, é mais barato que o OpenRouter. Funciona pelo endpoint compatível com OpenAI https:/. O código acima já aponta para esse endpoint; defina o id do modelo a partir da documentação do CheapTokens e execute.
Preços ao vivo para uma carga de 500.000 de entrada e 100.000 de saída:
| Modelo | Entrada $/1M | Saída $/1M | Custo | 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 tokens de entrada e 100.000 tokens de saída no total. Os preços por 1M de tokens são atuais e mudam junto com nossas tarifas; a coluna OpenRouter mostra preços de referência para a mesma carga de trabalho.
Veja todos os preços, a documentação de configuração, e crie uma chave para enviar sua primeira requisição.
Se ainda falhar#
- 429 na primeira requisição. Leia o tipo e a mensagem de erro no corpo da resposta: um 429 de gasto ou limite de conta continua falhando no retry até o limite ser aumentado ou o acesso ser retomado, enquanto um 429 de rate limit se resolve após uma pausa.
- 429 continua aparecendo com um loop longo. Você pode estar empilhando retries: retries do SDK vezes seus retries. Defina o SDK para zero, como nos exemplos.
- Erros vêm em rajadas no início de um lote. Aumente gradualmente em vez de disparar todos os workers de uma vez; a OpenAI reporta
slow_downpara tráfego que cresce rápido demais. - Chamada de streaming falhou no meio. Não a reenvie às cegas depois de consumir saída; decida se reinicia a resposta inteira ou continua a partir do que já tem.
- Sem Retry-After na resposta. O código recorre ao backoff com jitter, então nenhuma mudança é necessária.



