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. A predefinição é 120 requisições por minuto por IP, ajustável do lado do CaptainDNS. Esta camada protege contra ataques volumétricos e credential stuffing.
Uma ultrapassagem 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 gere o limite. A capacidade segue o plano:
| Plano | Capacidade (tokens) | Recarga |
|---|---|---|
| Free | 10 | 10 tokens/min |
| Solo | 10 | 10 tokens/min |
| Starter | 60 | 60 tokens/min |
| Pro | 500 | 500 tokens/min |
| Business | 1 000 | 1 000 tokens/min |
| Enterprise | 1 200 | 1 200 tokens/min |
Cada requisição autenticada consome um token. O bucket recarrega-se continuamente (recarga fracionária), pelo que não precisa de esperar um tick completo de minuto.
Um bucket vazio retorna 429 RATE_LIMITED. O header Retry-After contém os segundos até ao 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,wa janela em segundos.X-RateLimit-Limit: capacidade do bucket, idêntica aoqacima.
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 o seu cliente com base na capacidade, no header Retry-After e nas suas próprias medidas do lado cliente, não num contador servidor em tempo real.
Num 429, 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:
- Conte os tokens no lado cliente a partir de
X-RateLimit-Limit. Mantenha o seu próprio bucket local para antecipar a saturação antes de enviar a requisição. - Em
429 RATE_LIMITED, leiaRetry-Aftere espere pelo menos esse tempo. - Se acumular vários 429 consecutivos, aplique um multiplicador: 2x, 4x, 8x com teto de 60 segundos.
- Adicione um jitter aleatório de +/- 20 %, para que vários clientes não voltem a tentar ao mesmo tempo.
- Limite o total de retries (por exemplo 5). Acima disso, registe a falha como erro e leve-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 todas as noites) 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 evita os 429 e o jitter que eles arrastam.
- 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 lançar 500
page-crawl-check(10 créditos) e 500dmarc/lookup(1 crédito), reparta-os por duas filas distintas. Suaviza o consumo de créditos e respeita o limite com mais folga. - Janelas de manutenção: alguns batches podem correr fora do horário de pico, para não disputarem 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, como é natural, porque o bucket já está vazio.401 INVALID_API_KEY: não toca no bucket por chave, porque ainda não se sabe de que chave se trata. O limite por IP, esse, 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 a aumentar:
- Passe a um plano superior em
/account/billing. - O efeito é imediato: a chave herda o novo teto logo na requisição seguinte.
Clientes Enterprise podem negociar um teto personalizado, 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: ultrapassa 120 req/min num único IP. Verifique se vários serviços não partilham o mesmo egress (NAT, CGNAT).
- Token bucket por chave: o 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 num segundo. Acrescente jitter e suavização do lado do cliente.
- Desvio de clock: nos raros casos de um servidor dessincronizado do NTP, o
Retry-Afterpode parecer excessivo. Sincronize o relógio.
Se o problema persistir apesar de um respeito escrupuloso por 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 registo 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.