Ir para o conteúdo principal

Suavizar chamadas com rate limiting

A API pública CaptainDNS aplica duas camadas de rate limiting: uma por IP de origem antes da autenticação e um token bucket por chave cuja capacidade depende do plano. Este guia explica as duas camadas e como escrever um cliente que as respeita sem perder requisições.

Duas camadas de proteção

Limite por IP pre-auth

Antes de ler o header Authorization, um limitador em memória limita o tráfego por IP de origem. O padrão é 120 requisições por minuto por IP, ajustável no lado CaptainDNS. Esta camada protege contra ataques volumétricos e credential stuffing.

Um estouro retorna 429 RATE_LIMITED com Retry-After: 60. Nenhuma chave é verificada, nenhum crédito consumido.

Token bucket por chave

Uma vez autenticada a chave, um token bucket persistente no Postgres gerencia o limite. A capacidade segue o plano:

PlanoCapacidade (tokens)Recarga
Free1010 tokens/min
Solo1010 tokens/min
Starter6060 tokens/min
Pro500500 tokens/min
Business1.0001.000 tokens/min
Enterprise1.2001.200 tokens/min

Cada requisição autenticada consome um token. O bucket se recarrega continuamente (recarga fracionária), então você não precisa esperar um tick completo de minuto.

Um bucket vazio retorna 429 RATE_LIMITED. O header Retry-After contém os segundos até o próximo token disponível.

Headers de resposta

Cada resposta pública posiciona estes headers:

RateLimit-Policy: "default";q=60;w=60
X-RateLimit-Limit: 60
  • RateLimit-Policy: formato IETF draft, descreve a política. q é a capacidade do bucket, w a janela em segundos.
  • X-RateLimit-Limit: capacidade do bucket, idêntica ao q acima.

X-RateLimit-Remaining não é emitido a cada requisição: é posicionado em 0 apenas em caso de recusa 429 (a função PL/pgSQL que consome um token não retorna o número de tokens restantes, e uma leitura adicional teria um custo de latência injustificado). Dimensione seu cliente com base na capacidade, no header Retry-After e em suas próprias medidas do lado cliente, não em um contador servidor em tempo real.

Em um 429, você terá adicionalmente:

Retry-After: 12
X-RateLimit-Remaining: 0

O cliente deve respeitar Retry-After e aguardar no mínimo o número de segundos indicado antes de tentar novamente.

Estratégia de retry recomendada

O padrão idiomático é um backoff exponencial limitado:

  1. Conte os tokens no lado cliente a partir de X-RateLimit-Limit. Mantenha seu próprio bucket local para antecipar a saturação antes de enviar a requisição.
  2. Em 429 RATE_LIMITED, leia Retry-After e espere pelo menos esse tempo.
  3. Se você acumular vários 429 consecutivos, aplique um multiplicador: 2x, 4x, 8x com teto de 60 segundos.
  4. Adicione um jitter aleatório de +/- 20%, para que vários clientes não tentem de novo no mesmo instante.
  5. Limite o total de retries (por exemplo 5). Acima disso, registre a falha como erro e leve para a sua observabilidade.

Exemplo de pseudocódigo:

delay = retryAfter + random(-0.2, 0.2) * retryAfter
for attempt in 1..5:
    response = send(request)
    if response.status != 429:
        return response
    retryAfter = response.header["Retry-After"] or delay * attempt
    sleep(min(retryAfter, 60))
raise RateLimitExceeded

Estratégias para pipelines em batch

Integrações que processam grandes volumes em batch (por exemplo, varrer 10.000 domínios toda noite) se beneficiam de estratégias adicionais:

  • Token bucket no cliente: replique localmente o bucket do servidor e envie apenas quando um token estiver disponível. Assim você evita os 429 e o jitter que vem junto.
  • Paralelismo baseado na capacidade: um plano Pro tem 500 tokens/min, cerca de 8 req/s. Oito workers em paralelo são mais eficientes que um único que faz 500 iterações por minuto com pausas.
  • Separação de endpoints caros: se você disparar 500 page-crawl-check (10 créditos) e 500 dmarc/lookup (1 crédito), separe em duas filas distintas. Isso suaviza o consumo de créditos e facilita respeitar o limite.
  • Janelas de manutenção: alguns batches podem rodar fora do horário de pico, para não disputar o envelope com o tráfego em tempo real.

O que conta como requisição

Cada requisição HTTP autenticada consome um token, mesmo se falhar com 400 ou 500. O limite protege o backend; a validação dos dados de entrada vem depois de obtido o token, não antes.

Exceções:

  • 429 RATE_LIMITED: não consome token, claro, porque o bucket já está vazio.
  • 401 INVALID_API_KEY: não toca o bucket por chave, porque ainda não se sabe de qual chave se trata. Já o limite por IP conta.
  • 403 IP_NOT_ALLOWED: não consome nem token nem crédito, porque a verificação acontece antes do consumo.

Aumentar a capacidade

A capacidade do token bucket é propriedade do plano, não um parâmetro da chave. Para aumentar:

  1. Passe a um plano superior em /account/billing.
  2. O efeito é imediato: a chave herda o novo teto já na requisição seguinte.

Clientes Enterprise podem negociar um teto customizado, até 5.000 req/min sob orçamento, com o seu account manager CaptainDNS.

Diagnosticar um 429

Um 429 inesperado pode ter várias causas:

  • Bucket pre-auth: você ultrapassa 120 req/min em um único IP. Verifique se vários serviços não compartilham o mesmo egress (NAT, CGNAT).
  • Token bucket por chave: seu plano não oferece a capacidade necessária. Suba de plano ou reduza a concorrência.
  • Pico pontual: um batch sem suavização enviou 200 requisições em um segundo. Acrescente jitter e suavização no cliente.
  • Desvio de clock: nos raros casos de um servidor dessincronizado do NTP, o Retry-After pode parecer excessivo. Sincronize o relógio.

Se o problema persistir apesar de um respeito escrupuloso a Retry-After, abra um ticket de suporte com o X-Request-Id de uma requisição 429 representativa.

Ferramentas CaptainDNS relacionadas

  • O DNS lookup permite verificar manualmente um registro sem gastar créditos.
  • O DMARC monitoring agrega relatórios DMARC sem passar pela API pública.

Próximo passo: a idempotência para economizar créditos em retries, ou os códigos de erro.