Convide e ganhe

Como funcionam as recompensas

Compartilhe seu link. Quando um amigo se cadastrar por ele e adicionar saldo, você receberá a recompensa exibida nas recargas posteriores.

Erro API 429 Too Many Requests: limites, Retry-After e backoff seguro

Guia prático para resolver erros HTTP 429 em APIs de LLM: análise de limites RPM/TPM, leitura de Retry-After e implementação de backoff com jitter.

Conteúdo

Erro API 429 Too Many Requests: limites, Retry-After e backoff seguro

O erro HTTP 429 Too Many Requests ocorre quando o cliente ultrapassa os limites de frequência de requisições ou o volume de tokens estabelecidos pelo provedor de API. Executar tentativas infinitas e sem controle não resolve a falha, mas sim agrava o bloqueio, gerando tempestades de repetição (retry storms).

Para restabelecer a estabilidade do seu sistema, é fundamental identificar com precisão o tipo de limite (RPM, TPM ou saldo esgotado), processar corretamente o cabeçalho Retry-After e aplicar um algoritmo de recuo exponencial com ruído aleatório (Full Jitter Exponential Backoff).

Por que ocorre o HTTP 429: anatomia dos limites

Em APIs modernas de modelos de linguagem (OpenAI, Anthropic e gateways compatíveis), o status 429 é disparado por três mecanismos principais:

  1. RPM (Requests Per Minute): limite na quantidade de requisições HTTP por minuto. Ocorre com frequência ao executar vários workers paralelos sem filas de controle.
  2. TPM (Tokens Per Minute): limite no volume acumulado de tokens de entrada e saída em uma janela de um minuto. É comum ao enviar contextos extensos.
  3. Esgotamento de cota ou saldo: bloqueio decorrente de saldo zerado, limite de gastos atingido ou término do pacote pré-pago.
HTTP/1.1 429 Too Many Requests
Date: Sun, 23 Aug 2026 03:00:00 GMT
Content-Type: application/json
Retry-After: 6
x-ratelimit-limit-requests: 500
x-ratelimit-remaining-requests: 0
x-ratelimit-reset-requests: 6s
x-ratelimit-limit-tokens: 30000
x-ratelimit-remaining-tokens: 1200
x-ratelimit-reset-tokens: 150ms

{
  "error": {
    "message": "Rate limit reached for model in organization on tokens per minute (TPM). Please try again in 6s.",
    "type": "tokens",
    "param": null,
    "code": "rate_limit_exceeded"
  }
}

Quando o erro decorre de falta de créditos ou limites rígidos de assinatura, tentativas automáticas apenas desperdiçam recursos de rede. Para diagnosticar a causa raiz imediatamente e sem decodificar logs complexos, o BetterToken oferece um painel de controle transparente: exibe em tempo real o status HTTP de cada chamada, o detalhamento exato de tokens de entrada, saída e cache, e o saldo no modelo pay-as-you-go sem bloqueios de 5 horas.

Matriz de diagnóstico do erro 429

SintomaCausa principalO que verificar na respostaSolução de engenharia
Falha em rajadas paralelasLimite de RPM excedidox-ratelimit-remaining-requests: 0Limitar concorrência via semáforos ou filas
Falha em prompts longosLimite de TPM excedidox-ratelimit-remaining-tokens < tamanho do promptOtimizar contexto, usar prompt caching ou dividir lotes
100% das chamadas retornam 429Saldo / Cota esgotadaCódigo insufficient_quota ou mensagem de cobrançaInterromper retry e recarregar saldo ou ajustar chave
Crescimento em cascata de 429Tempestade de retentativasCabeçalhos Retry-After ignorados pelos workersAdicionar ruído aleatório (Jitter) ao cálculo do atraso

Como interpretar o cabeçalho Retry-After

A especificação RFC 6585 define dois formatos válidos para o cabeçalho Retry-After:

  • Segundos relativos (número inteiro ou decimal, por exemplo Retry-After: 12);
  • Data HTTP (timestamp GMT padronizado, por exemplo Retry-After: Sun, 23 Aug 2026 03:05:00 GMT).
import datetime
import email.utils
import time

def parse_retry_after(header_value: str | None, default_delay: float = 1.0) -> float:
    if not header_value:
        return default_delay
    header_value = header_value.strip()
    try:
        return max(0.0, float(header_value))
    except ValueError:
        pass

    try:
        parsed_date = email.utils.parsedate_to_datetime(header_value)
        now = datetime.datetime.now(datetime.timezone.utc)
        delay = (parsed_date - now).total_seconds()
        return max(0.0, delay)
    except Exception:
        return default_delay

Implementação do Full Jitter Exponential Backoff

Caso o cabeçalho Retry-After não esteja presente, a abordagem padrão é o recuo exponencial com ruído aleatório total (Full Jitter). A fórmula para a tentativa $i$ é:

$$T_{ ext{wait}} = ext{random}(0, \min(T_{ ext{max}}, T_{ ext{base}} imes 2^i))$$

import asyncio
import json
import random
import httpx

class RateLimitRetryClient:
    def __init__(
        self,
        base_url: str = "https://www.bettertoken.ai/v1",
        api_key: str = "",
        max_retries: int = 4,
        base_delay: float = 1.0,
        max_delay: float = 32.0,
    ):
        self.base_url = base_url
        self.api_key = api_key
        self.max_retries = max_retries
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.client = httpx.AsyncClient(
            base_url=self.base_url,
            headers={"Authorization": f"Bearer {self.api_key}"},
            timeout=60.0,
        )

    async def send_chat_completion(self, payload: dict) -> dict:
        for attempt in range(self.max_retries + 1):
            try:
                response = await self.client.post("/chat/completions", json=payload)
                if response.status_code == 200:
                    return response.json()

                if response.status_code == 429:
                    error_data = response.json().get("error", {})
                    error_code = error_data.get("code")

                    if error_code in ("insufficient_quota", "billing_not_active"):
                        raise RuntimeError(f"Erro de cobrança: {error_data.get('message')}")

                    if attempt == self.max_retries:
                        raise RuntimeError(f"Limite de tentativas esgotado (429): {response.text}")

                    retry_after = response.headers.get("Retry-After")
                    if retry_after:
                        wait_time = parse_retry_after(retry_after) + random.uniform(0.1, 0.5)
                    else:
                        backoff_cap = min(self.max_delay, self.base_delay * (2 ** attempt))
                        wait_time = random.uniform(0, backoff_cap)

                    await asyncio.sleep(wait_time)
                    continue

                response.raise_for_status()

            except httpx.RequestError as exc:
                if attempt == self.max_retries:
                    raise
                wait_time = min(self.max_delay, self.base_delay * (2 ** attempt))
                await asyncio.sleep(wait_time)

        raise RuntimeError("A requisição falhou após esgotar o orçamento de tentativas")

Idempotência e segurança em operações com efeitos colaterais

Repetir operações de leitura (GET) é seguro por natureza. No entanto, ao acionar tarefas generativas via POST:

  1. Evite duplicar execuções de agentes: caso uma conexão caia por timeout, verifique se tokens foram debitados antes de reenviar a tarefa.
  2. Utilize identificadores únicos de cliente: inclua o cabeçalho X-Request-ID para rastrear requisições nos logs.
  3. Não trate erros 401 ou 403 como 429: falhas de autenticação exigem correção de credenciais e não pausas em loop.

Verificação e recuperação do serviço

Antes de restaurar o fluxo completo de requisições:

  • Envie uma requisição de teste mínima (max_tokens: 5).
  • Confirme o recebimento do código HTTP 200 e inspecione o cabeçalho x-ratelimit-remaining-requests.
  • Aumente a concorrência gradualmente enquanto monitora a taxa de erros 429 em suas métricas.

Para evitar interrupções inesperadas por limites de requisições por minuto e manter visibilidade completa sobre o status de cada chamada, acesse a BetterToken API, crie chaves dedicadas e monitore o consumo de tokens em tempo real no Dashboard.

Fontes e referências

Quer otimizar seu fluxo de trabalho com LLMs?

Conecte modelos por uma única API, gerencie chaves e controle os gastos com IA.

Começar grátis