Imagine: você escreveu um cliente que faz requisições a um site e tudo funciona. De repente, começam a surgir erros, os workers travam e o servidor responde com o misterioso código 429. Conhecido? Então este guia é para você. Vamos entender como construir um cliente HTTP que não entra em pânico ao primeiro problema, mas se comporta de maneira educada e resiliente.

Introdução: por que 429 não é um erro, mas um sinal

Muitos desenvolvedores veem o código 429 e pensam: quebrou. Na verdade, o servidor está dizendo algo muito específico: você está enviando requisições demais, desacelere. Não é uma recusa nem um bloqueio permanente. É um pedido para reduzir o ritmo. E se você ouvir esse pedido corretamente, seu cliente se tornará confiável.

O que o leitor terá ao final

Ao final deste guia, você terá um cliente HTTP funcional que sabe fazer várias coisas importantes. Ele lida corretamente com o código 429 e respeita o cabeçalho Retry-After. Ele usa backoff exponencial com jitter para não causar uma enxurrada de repetições. Ele limita a concorrência para não sobrecarregar o servidor de destino. E ele não trava graças a timeouts bem configurados.

Você receberá trechos de código prontos em três linguagens: Python (com as bibliotecas httpx e urllib3 Retry), Node.js e Go. Cada trecho pode ser inserido no seu projeto e adaptado à sua necessidade.

Para quem é este guia

O guia é voltado para desenvolvedores iniciantes que já sabem fazer requisições HTTP simples, mas ainda não enfrentaram cargas de produção. No entanto, também contém blocos avançados: circuit breaker, métricas, degradação controlada. Se você está escrevendo um parser, uma integração com uma API de terceiros ou um serviço que acessa recursos externos, este material vai economizar muitas noites em claro.

O que você precisa saber de antemão

Você só precisa entender o que é uma requisição HTTP e uma resposta HTTP. É desejável conhecer códigos de status (por exemplo, 200 é sucesso, 404 é página não encontrada). Conhecimento básico de pelo menos uma das linguagens (Python, JavaScript ou Go) será útil. Não são necessários conhecimentos profundos de redes – tudo será explicado de forma simples.

Quanto tempo será necessário

Ler e entender a teoria – cerca de 40 minutos. Montar o cliente básico passo a passo – aproximadamente uma hora. A implementação completa com todas as proteções, métricas e testes – cerca de três horas. Não se apresse: é melhor entender cada passo lentamente do que copiar rapidamente um código que você não compreende.

Dica: Leia o guia com o editor de código aberto. Teste os exemplos em um endpoint de teste, não em um serviço de produção real.

Preparação prévia

Antes de escrever código, vamos preparar o ambiente de trabalho. Isso leva um pouco de tempo, mas evita confusões mais adiante.

Ferramentas necessárias

  • Uma das linguagens e seu ambiente: Python 3.11 ou superior, ou Node.js 20 ou superior, ou Go 1.22 ou superior.
  • Editor de código – qualquer um serve, por exemplo VS Code.
  • Terminal para executar scripts.
  • Acesso à internet para um serviço HTTP de teste que consiga retornar diferentes códigos de resposta.

O que instalar para Python

  1. Verifique a versão do Python no terminal: digite python --version e pressione Enter.
  2. Crie um ambiente virtual com python -m venv venv.
  3. Ative-o: no Windows com venv\Scripts\activate, no macOS e Linux com source venv/bin/activate.
  4. Instale as bibliotecas com pip install httpx urllib3 requests.

O que instalar para Node.js

  1. Verifique a versão com node --version.
  2. Crie uma pasta para o projeto e entre nela.
  3. Inicialize o projeto com npm init -y.
  4. A partir do Node.js 20, o fetch nativo está disponível sem instalação; nenhum pacote adicional é necessário para o cliente básico.

O que instalar para Go

  1. Verifique a versão com go version.
  2. Crie uma pasta e inicialize o módulo com go mod init myclient.
  3. A biblioteca padrão net/http é suficiente; pacotes externos não são obrigatórios.

Cópias de segurança e segurança

⚠️ Atenção: Nunca teste um cliente novo diretamente em um serviço de produção importante. Use primeiro um endpoint de teste ou um servidor local simulado que você controla. Caso contrário, repetições agressivas podem prejudicar o serviço alheio e levar ao seu bloqueio.

Se você estiver modificando um projeto existente, faça uma cópia do arquivo ou crie um branch separado no sistema de controle de versão. Assim, você sempre poderá reverter as alterações.

✅ Verificação: Você instalou a linguagem escolhida, criou o projeto e confirmou que um script de teste é executado sem erros. Agora podemos passar para a teoria.

Conceitos básicos em linguagem simples

Para construir o cliente com confiança, é preciso entender alguns termos-chave. Vamos explicá-los sem complicação.

O que significam os códigos 403, 407, 429 e 503

Esses quatro códigos são fáceis de confundir, mas têm comportamentos diferentes e tratamentos diferentes.

  • Código 429 Too Many Requests – o servidor diz que você excedeu o limite de requisições. É temporário. Você precisa desacelerar e tentar novamente mais tarde.
  • Código 403 Forbidden – acesso proibido. Geralmente não é sobre velocidade, mas sobre permissões: chave inválida, falta de autorização, restrição regional. Repetir a requisição sem alterações geralmente é inútil.
  • Código 503 Service Unavailable – o servidor está temporariamente sobrecarregado ou em manutenção. Assim como o 429, é temporário e uma repetição posterior pode ajudar.
  • Código 407 Proxy Authentication Required – e aqui está uma nuance importante. Esse código não vem do site de destino, mas do servidor proxy. Ele significa que o proxy exige autenticação e você não a forneceu ou a forneceu incorretamente.

⚠️ Atenção: O código 407 não pode ser tratado com rotação de IP ou backoff. É um erro de configuração do seu cliente, especificamente credenciais de proxy incorretas. Verifique o login, a senha e o formato da string de conexão. Nenhuma repetição ajudará até que você corrija a autenticação.

Diferença entre 429 e 403

Lembre-se de uma regra simples. 429 é sobre quantidade: você está fazendo requisições com muita frequência. 403 é sobre permissão: você não tem direito de acessar. No 429, repetir após uma pausa resolve o problema. No 403, repetir sem mudar as condições não resolve – é preciso alterar a chave, os cabeçalhos ou a abordagem.

Cabeçalhos Retry-After e X-RateLimit

Servidores educados indicam quando você pode retornar. O cabeçalho Retry-After diz quantos segundos esperar antes de repetir a requisição. Às vezes é um número de segundos, às vezes uma data específica. Seu cliente deve respeitar esse cabeçalho: se o servidor disse para esperar 10 segundos, repetir em 1 segundo só piora a situação.

O grupo de cabeçalhos X-RateLimit informa os limites: quantas requisições são permitidas, quantas restam e quando o contador será zerado. Por exemplo, X-RateLimit-Remaining mostra o saldo. Se ele estiver perto de zero, é melhor reduzir o ritmo antecipadamente, sem esperar pelo 429.

Como funcionam os limites: token bucket e janela deslizante

Os servidores contam suas requisições de duas maneiras populares.

Token bucket (balde de tokens) funciona assim: imagine um balde no qual tokens caem constantemente a uma taxa fixa. Cada requisição consome um token. Se não houver tokens, a requisição é rejeitada com código 429. Esse esquema permite picos curtos: se você ficou muito tempo sem fazer requisições, o balde se encheu e você pode fazer um lote de uma vez.

Janela deslizante (sliding window) conta o número de requisições no último intervalo, por exemplo, no último minuto. Assim que você excede o limite nessa janela, recebe 429. Aqui, os picos são punidos com mais rigor.

Por que concorrência também é um limite

Muitos esquecem: o limite não é apenas de frequência, mas também de número de conexões simultâneas. Se você abrir 500 requisições paralelas, o servidor pode interpretar como um ataque, mesmo que o número total por minuto seja pequeno. A concorrência deve ser limitada com a mesma rigidez que a frequência.

Dica: Antes de construir o cliente, descubra os limites do serviço de destino na documentação. Conhecer os números exatos evita suposições e 429 desnecessários.

✅ Verificação: Você entende a diferença entre 429, 403, 407 e 503, sabe o que é Retry-After e tem uma ideia de como o servidor conta suas requisições. Ótimo, vamos para a prática.

Passo 1: configurando timeouts corretamente

Objetivo da etapa: garantir que nenhuma requisição possa travar para sempre e bloquear um worker.

Por que um cliente sem timeout é perigoso

Um cliente sem timeout é uma bomba-relógio. Se o servidor parar de responder, sua requisição vai esperar indefinidamente. Uma requisição travada segura um worker. Dez requisições travadas – e todo o seu pool de workers fica ocupado, novas tarefas não são processadas, o serviço para. Timeout é sua primeira linha de defesa.

Quatro tipos de timeout

Um cliente bem feito distingue vários timeouts, em vez de colocar um único para tudo.

  1. Connect timeout (conexão) – quanto tempo esperar para estabelecer a conexão com o servidor. Se o servidor estiver inacessível, você saberá rapidamente.
  2. Read timeout (leitura) – quanto tempo esperar pelos dados após enviar a requisição. Protege contra servidores que aceitam a requisição, mas ficam mudos.
  3. Write timeout (escrita) – quanto tempo esperar para enviar o corpo da requisição. Relevante para uploads grandes.
  4. Timeout geral (total) – tempo máximo para toda a requisição, incluindo todas as fases.

Quais valores usar como ponto de partida

Não existem números universais, mas há valores iniciais razoáveis. Para connect, use 3 a 5 segundos: a conexão geralmente é rápida. Para read, use 10 a 30 segundos, dependendo da velocidade com que o serviço retorna dados. O timeout total deve cobrir a requisição mais longa razoável, por exemplo, 30 a 60 segundos.

⚠️ Atenção: Nunca coloque timeouts enormes como 300 segundos para todas as requisições. Isso mascara problemas e cria uma fila de operações travadas. É melhor falhar rapidamente e repetir do que esperar muito em vão.

Configuração passo a passo

  1. Determine quanto tempo leva uma requisição bem-sucedida para o seu serviço. Meça algumas vezes.
  2. Defina o read timeout aproximadamente como o dobro do tempo médio de resposta.
  3. Defina o connect timeout entre 3 e 5 segundos.
  4. Defina o timeout total como a soma das fases razoáveis mais uma pequena margem.
  5. Faça uma requisição de teste e confirme que ela termina, não fica pendente.

Dica: Se seu serviço às vezes retorna arquivos grandes e outras vezes respostas pequenas, crie perfis de timeout diferentes para diferentes tipos de requisição. Um tamanho não serve para todos.

Resultado esperado: ao acessar um endereço notoriamente lento ou inacessível, seu cliente encerra a tentativa após o tempo definido com um erro de timeout claro, em vez de ficar pendente para sempre.

✅ Verificação: Direcione uma requisição para um endereço que não responde (por exemplo, uma porta inexistente). O cliente deve retornar um erro de timeout aproximadamente no tempo definido. Se ficar pendente por mais tempo, o timeout está mal configurado.

Passo 2: construindo retentativas com backoff exponencial e jitter

Objetivo da etapa: ensinar o cliente a repetir requisições de forma inteligente, sem prejudicar a si mesmo ou ao servidor.

O que pode ser repetido: idempotência

Antes de repetir uma requisição, pergunte-se: é seguro executá-la duas vezes? Essa propriedade é chamada de idempotência. Uma requisição é idempotente se repeti-la produz o mesmo resultado e não causa efeitos colaterais.

  • GET, HEAD, PUT, DELETE geralmente são idempotentes. Repeti-los é seguro.
  • POST geralmente não é idempotente. Repetir pode criar um pedido duplicado, uma segunda cobrança, um registro duplicado.

⚠️ Atenção: Nunca repita requisições POST cegamente. O reenvio de uma requisição não idempotente pode resultar em cobrança em dobro ou dados duplicados. Se precisar repetir um POST, use uma chave de idempotência (Idempotency-Key) que o servidor entenda e não execute a operação duas vezes.

Quantas vezes repetir

Repetições infinitas são más. Um limite razoável é de 3 a 5 tentativas. Se após cinco tentativas a requisição não passou, o problema é mais sério que uma falha temporária, e deve ser registrado em log e tratado separadamente.

O que é backoff exponencial

Backoff é a pausa entre repetições. Exponencial significa que a pausa cresce de forma multiplicativa a cada tentativa. Por exemplo: primeira pausa de 1 segundo, segunda de 2 segundos, terceira de 4, quarta de 8. A fórmula é simples: atraso base multiplicado por dois elevado ao número da tentativa.

Por que assim? Se o servidor está sobrecarregado, repetições curtas e frequentes só vão piorar a situação. Pausas crescentes dão tempo ao servidor para se recuperar.

Por que sem jitter temos uma enxurrada de repetições

Imagine que mil clientes recebam 429 ao mesmo tempo. Todos esperam exatamente 1 segundo, depois exatamente 2, depois exatamente 4. E todos repetem no mesmo instante. Temos uma enxurrada síncrona: o servidor recebe mil requisições de uma vez e novamente responde com 429. O problema não se resolve, entra em loop.

A solução é o jitter, ou seja, um acréscimo aleatório à pausa. Em vez de exatamente 2 segundos, um cliente espera 1.7, outro 2.3, outro 1.9. As repetições se espalham no tempo e o servidor se alivia gradualmente.

Como respeitar o Retry-After

Se o servidor enviou o cabeçalho Retry-After, ele tem prioridade sobre sua fórmula de backoff. A regra é simples: use o maior valor entre sua pausa calculada e o Retry-After. Nunca repita antes do que o servidor pediu. Isso é uma violação grosseira de etiqueta e levará a novos 429.

Implementação passo a passo da lógica de retentativas

  1. Verifique se a requisição é idempotente. Se não for e não houver chave de idempotência, não repita.
  2. Verifique o código de resposta. Repita apenas para 429, 503 e erros de rede (timeout, conexão interrompida).
  3. Incremente o contador de tentativas. Se exceder o limite, pare e retorne um erro.
  4. Calcule a pausa base usando a fórmula de crescimento exponencial.
  5. Adicione um jitter aleatório à pausa.
  6. Se houver Retry-After, use o maior dos dois valores.
  7. Aguarde o tempo calculado e repita a requisição.

Dica: Limite a pausa máxima, por exemplo, 30 ou 60 segundos. Caso contrário, na quinta tentativa o backoff pode crescer para valores absurdamente grandes e o usuário esperará tempo demais.

Resultado esperado: ao receber código 429, o cliente faz uma pausa, repete a requisição, e as pausas entre as repetições crescem e variam ligeiramente a cada vez.

✅ Verificação: Configure um servidor de teste que retorne 429 várias vezes seguidas e depois 200. Seu cliente deve obter a resposta final com sucesso, e nos logs você verá pausas crescentes com dispersão.

Passo 3: limitando a concorrência

Objetivo da etapa: impedir que o cliente sobrecarregue o servidor com uma avalanche de requisições simultâneas.

O que é um semáforo em palavras simples

Um semáforo é um contador de permissões. Imagine um guarda-roupa com um número limitado de cabides. Enquanto houver um cabide livre, você pendura o casaco. Se todos estiverem ocupados, você espera até que alguém libere. O semáforo permite que um número limitado de tarefas seja executado simultaneamente, mantendo as demais em espera.

Fila de tarefas

Todas as requisições a serem executadas são colocadas em uma fila. Os workers retiram as tarefas da fila à medida que ficam livres. Isso dá controle total sobre o ritmo: quantos workers, no máximo, tantas requisições simultâneas.

Limite por host

Uma nuance importante: o limite deve ser definido para cada host separadamente. Se você trabalha com vários serviços, um limite global único não é ideal. Um host lento não deve bloquear requisições para outro. Defina um limite individual para cada domínio.

Pool de conexões e keep-alive

Cada nova conexão TCP tem um custo: handshake, estabelecimento de canal seguro. O keep-alive permite reutilizar a conexão para várias requisições consecutivas. Isso economiza tempo e recursos do servidor. O pool de conexões mantém conexões abertas prontas para uso. Configure o tamanho do pool de forma consistente com seu limite de concorrência.

⚠️ Atenção: Não confunda o tamanho do pool de conexões com o limite de concorrência. O pool pode ser ligeiramente maior que o limite para folga, mas se o pool for enorme e o limite pequeno, você estará mantendo conexões abertas desnecessariamente. Mantenha um equilíbrio razoável.

Configuração passo a passo da limitação

  1. Determine um número seguro de requisições simultâneas por host. Comece baixo, por exemplo, 5 a 10.
  2. Crie um semáforo com esse número de permissões.
  3. Antes de cada requisição, solicite uma permissão ao semáforo.
  4. Após a conclusão da requisição, seja bem-sucedida ou não, libere a permissão obrigatoriamente.
  5. Configure o pool de conexões com keep-alive para valores na mesma ordem.
  6. Aumente gradualmente o limite, observando a taxa de 429. Assim que ela subir, pare.

Dica: Libere a permissão do semáforo em um bloco finally ou equivalente. Caso contrário, se ocorrer um erro, a permissão não será devolvida, o contador vazará e, com o tempo, o cliente travará completamente.

Resultado esperado: independentemente de quantas tarefas sejam colocadas na fila, o número de requisições simultâneas ao host não ultrapassa o limite definido.

✅ Verificação: Coloque 100 tarefas na fila com um limite de 5. Nos logs ou monitor de conexões, você deve ver no máximo 5 requisições ativas em qualquer momento.

Passo 4: reagindo especificamente ao código 429

Objetivo da etapa: estabelecer uma reação correta ao sinal de sobrecarga e entender quando a troca de IP é adequada.

Três ações ao receber 429

Quando chega um 429, você tem três ferramentas, e elas devem ser usadas em conjunto.

  1. Desacelerar – reduzir o ritmo geral das requisições, não apenas fazer uma pausa para uma requisição específica. Isso é fundamental: 429 é um sinal de que seu ritmo geral está alto demais.
  2. Trocar de IP – se você usa rotação de endereços IP, a troca pode ajudar quando o limite está vinculado a um IP específico. Mas não é uma solução mágica.
  3. Adiar a tarefa – colocar a requisição de volta na fila com um atraso, para executá-la mais tarde, quando os limites se restaurarem.

⚠️ Atenção: Trocar de IP não anula a etiqueta. Se o limite não for por IP, mas por conta ou chave, nenhuma rotação ajudará – você ainda esbarrará no 429. Não transforme a rotação em uma forma de burlar as regras: respeite os limites do serviço e o Retry-After em qualquer caso.

Matriz de ações por código de resposta

Tenha à mão uma tabela simples de decisões. Veja o que fazer para cada código.

  • 200-299 Sucesso – processar a resposta, liberar recursos, pegar a próxima tarefa.
  • 429 Too Many Requests – desacelerar o ritmo, respeitar Retry-After, repetir com backoff, se necessário adiar a tarefa ou trocar de IP.
  • 503 Service Unavailable – repetir com backoff, respeitar Retry-After, mas não trocar de IP: o problema está no servidor.
  • 403 Forbidden – não repetir cegamente. Verificar autenticação, cabeçalhos, permissões. Registrar em log para análise.
  • 407 Proxy Authentication Required – corrigir as credenciais do proxy. Não repetir nem rotacionar até corrigir a configuração.
  • 400, 404, 422 erros do cliente – não repetir. É um erro na sua requisição; repetir não mudará nada.
  • 500, 502, 504 erros do servidor – repetir com cautela, com backoff e número reduzido de tentativas.
  • Erros de rede e timeouts – repetir com backoff, se a requisição for idempotente.

Implementação passo a passo da reação ao 429

  1. Ao receber 429, interrompa imediatamente o aumento do ritmo.
  2. Leia o cabeçalho Retry-After, se existir.
  3. Calcule a pausa como o maior valor entre backoff e Retry-After.
  4. Se o limite provavelmente está vinculado ao IP e você tem rotação, troque de endereço antes de repetir.
  5. Se as tentativas se esgotarem, coloque a tarefa de volta na fila com um grande atraso.
  6. Reduza o limite geral de concorrência temporariamente para dar um respiro ao servidor.

Dica: Mantenha um contador separado da proporção de 429 no último minuto. Se ela estiver subindo, reduza automaticamente o ritmo antes que a situação se torne crítica. Isso é chamado de limitação adaptativa.

Resultado esperado: diante de uma série de 429, o cliente reduz o ritmo gradualmente, respeita o Retry-After e, no final, completa as requisições com sucesso, sem causar uma enxurrada.

✅ Verificação: Simule um pico de 429 em um servidor de teste. O cliente deve diminuir a atividade, não aumentar as repetições. A proporção de respostas bem-sucedidas após a pausa deve se recuperar.

Passo 5: adicionando circuit breaker e degradação controlada

Objetivo da etapa: dar ao cliente um fusível que protege tanto você quanto o servidor em problemas prolongados.

O que é circuit breaker

Circuit breaker é um fusível, como no quadro elétrico. Se os erros vêm em sequência, ele abre o circuito: para de permitir requisições para o serviço problemático por um tempo. Isso protege o servidor de ser sobrecarregado e seu cliente de desperdiçar recursos inutilmente.

Três estados do fusível

  • Closed (fechado) – operação normal, as requisições passam. O cliente conta os erros.
  • Open (aberto) – muitos erros, as requisições são bloqueadas imediatamente sem ir ao servidor. Permanece aberto por um tempo determinado.
  • Half-open (semiaberto) – modo de teste. O cliente permite algumas requisições para verificar se o serviço se recuperou. Se sim, volta para closed; se não, volta para open.

Degradação controlada em vez de parada total

Quando o serviço está indisponível, não é necessário derrubar tudo. Degradação controlada é a capacidade de funcionar pior, mas continuar funcionando. Exemplos: retornar dados do cache em vez de dados frescos, mostrar um resultado reduzido, adiar tarefas não essenciais, retornar um placeholder compreensível em vez de um erro.

Dica: Sempre pense no que mostrar ao usuário ou ao sistema quando o serviço externo estiver caído. Um placeholder com uma mensagem significativa é melhor do que um travamento ou um stack trace.

Configuração passo a passo do circuit breaker

  1. Defina um limiar de erros para abrir o fusível, por exemplo, 50% de falhas em uma janela de 20 requisições.
  2. Defina o tempo durante o qual o circuito fica aberto, por exemplo, 30 segundos.
  3. Conte sucessos e falhas em uma janela deslizante.
  4. Ao ultrapassar o limiar, mude o estado para open.
  5. Após o tempo, mude para half-open e permita algumas requisições de teste.
  6. Com base nos resultados, volte para closed ou novamente para open.

⚠️ Atenção: Não confunda circuit breaker com retentativas. Retentativas repetem uma requisição; o fusível gerencia todo o fluxo para o serviço. Juntos são poderosos, mas devem ser configurados de forma consistente para que o fusível não abra muito cedo devido a falhas isoladas normais.

Resultado esperado: durante uma indisponibilidade prolongada do serviço, o cliente para de bombardear o servidor com requisições, retorna rapidamente um placeholder e verifica periodicamente a recuperação.

✅ Verificação: Torne um servidor de teste indisponível. O cliente deve, após uma série de falhas, parar de enviar requisições (open) e, após a recuperação do servidor, retornar ao funcionamento normal passando pelo half-open.

Verificação do resultado: quais métricas monitorar

Resiliência não se avalia visualmente. Precisamos de números. Aqui estão as métricas-chave que mostrarão se o cliente se tornou mais confiável.

Indicadores principais

  • Taxa de sucesso (success rate) – porcentagem de requisições concluídas com código 2xx. Quanto maior, melhor. Busque um valor consistentemente alto mesmo sob carga.
  • p95 de latência – tempo dentro do qual 95% das requisições são concluídas. Esse indicador é mais honesto que a média, pois mostra como a maioria se sente, não apenas as requisições sortudas.
  • Proporção de 429 – porcentagem de respostas com código 429. Se for alta, você está enviando requisições de forma muito agressiva. O objetivo é minimizá-la.
  • Número de repetições por requisição – mostra o quão difícil é obter sucesso. Um aumento indica problemas.
  • Número de aberturas do circuit breaker – aberturas frequentes sinalizam instabilidade do serviço ou configurações muito agressivas.

Checklist de prontidão

  1. Timeouts configurados para todas as fases, nenhuma requisição fica pendente para sempre.
  2. Retentativas funcionam apenas para requisições idempotentes e códigos seguros.
  3. Backoff cresce exponencialmente e contém jitter.
  4. Retry-After é sempre respeitado.
  5. Concorrência limitada com semáforo por host.
  6. Pool de conexões com keep-alive configurado de acordo com o limite.
  7. Reação ao 429 reduz o ritmo, não aumenta as repetições.
  8. Matriz de ações por código implementada.
  9. Circuit breaker protege contra falhas prolongadas.
  10. Métricas são coletadas e estão disponíveis para análise.

Como saber se o cliente ficou mais resiliente

Compare as métricas antes e depois das melhorias sob a mesma carga. Um cliente resiliente mostra alta taxa de sucesso, baixa proporção de 429, p95 estável e nenhum worker travado. Mesmo quando o servidor está instável, seu serviço continua operando sem quedas em cascata.

✅ Verificação: Execute um teste de carga em um endpoint de teste. Se sob carga a taxa de sucesso permanecer alta e não houver travamentos, parabéns – o cliente é resiliente.

Erros típicos e suas soluções

Vamos analisar as armadilhas comuns em que quase todo mundo cai.

Erro 1: retentativas aumentam a carga

Problema: o servidor está sobrecarregado e suas repetições agressivas o finalizam de vez. Causa: repetições sem backoff e sem redução de ritmo. Solução: adicione backoff exponencial com jitter, limite o número de tentativas, reduza a concorrência geral quando os erros aumentarem.

Erro 2: repetir requisições não idempotentes

Problema: pedidos duplicados, cobranças repetidas, registros duplicados. Causa: repetição cega de requisições POST. Solução: repita apenas métodos idempotentes. Para POST, use uma chave de idempotência que o servidor reconheça e não execute a operação duas vezes.

Erro 3: tratar 429 com troca infinita de IP

Problema: você troca de IP repetidamente, mas o 429 não desaparece. Causa: o limite não está vinculado ao IP, mas à chave ou conta, ou você está simplesmente enviando muitas requisições no total. Solução: reduza o ritmo e respeite o Retry-After. A rotação de IP é apenas uma ferramenta, não um substituto para a etiqueta.

Erro 4: enxurrada síncrona de repetições

Problema: todos os clientes repetem nos mesmos momentos, o servidor cai novamente. Causa: backoff sem jitter. Solução: adicione um componente aleatório a cada pausa.

Erro 5: workers travados

Problema: o serviço gradualmente para de processar tarefas. Causa: ausência de timeouts, requisições pendentes para sempre. Solução: configure timeouts de connect, read e total para todas as requisições.

Erro 6: vazamento de permissões do semáforo

Problema: com o tempo, o cliente para de fazer requisições. Causa: a permissão do semáforo não é liberada em caso de erro. Solução: libere a permissão em um bloco finally, para que isso sempre aconteça.

Erro 7: reação incorreta ao 407

Problema: o cliente repete infinitamente e troca de IP, mas continua recebendo 407. Causa: o código 407 vem do proxy e significa erro de autenticação no proxy, não problema no serviço. Solução: verifique e corrija as credenciais do proxy. Repetições aqui são inúteis.

Trechos de código prontos

Abaixo, descrições das abordagens em três stacks. Adapte ao seu projeto.

Python com httpx

Crie um cliente httpx com timeouts explícitos usando o objeto Timeout, onde connect e read são definidos separadamente. Defina os limites do pool com httpx Limits, especificando o máximo de conexões por host. Envolva a chamada em um loop de repetições: para 429 e 503, leia o Retry-After, calcule a pausa como o maior valor entre backoff exponencial com jitter e o Retry-After, depois aguarde com asyncio.sleep. Limite a concorrência com asyncio.Semaphore, liberando a permissão em um bloco finally. Repita apenas métodos idempotentes, limite o número de tentativas a cinco.

Python com urllib3 Retry

A biblioteca urllib3 oferece um mecanismo pronto. Crie um objeto Retry com os parâmetros: total define o número de tentativas, backoff_factor ativa as pausas exponenciais, status_forcelist lista os códigos para repetição, por exemplo 429, 500, 502, 503, 504. O parâmetro respect_retry_after_header ativa o respeito ao Retry-After. Passe esse Retry para o PoolManager ou para o adaptador requests via HTTPAdapter. Essa é a maneira mais rápida de obter resiliência básica sem escrever um loop manualmente.

Node.js

Use o fetch nativo com AbortController para timeout: crie um controlador, defina um setTimeout para abort, passe o signal para fetch. Envolva a chamada em uma função com loop de repetições. Verifique response.status: para 429 e 503, leia o cabeçalho Retry-After com response.headers.get, calcule a pausa com jitter, aguarde com uma Promise e setTimeout. Para limitar a concorrência, use um semáforo simples baseado em Promise ou uma biblioteca de limitador popular. Mantenha o número de Promises simultâneas sob controle com uma fila.

Go

Em Go, configure http.Client com o campo Timeout para timeout geral e configure Transport com os parâmetros MaxIdleConnsPerHost e IdleConnTimeout para pool e keep-alive. Para timeout de conexão, use DialContext com net.Dialer. Implemente um loop de repetições: para 429 e 503, leia o cabeçalho Retry-After, calcule a pausa com time.Duration com crescimento exponencial e jitter aleatório, aguarde com time.Sleep ou select com context. Limite a concorrência com um canal bufferizado como semáforo: escreva no canal antes da requisição, leia dele em defer após.

Dica: Em qualquer linguagem, coloque as configurações (timeouts, número de tentativas, limite de concorrência) em um arquivo de configuração, não as codifique diretamente. Assim você pode ajustar o comportamento para cada serviço sem reescrever o código.

Recursos adicionais e otimização

Quando o cliente básico estiver funcionando, é possível torná-lo ainda mais inteligente.

Limitação adaptativa de ritmo

Em vez de um limite fixo, torne-o dinâmico. Leia os cabeçalhos X-RateLimit-Remaining e reduza o ritmo antecipadamente quando o saldo estiver baixo. Assim, você evita 429 antes mesmo de eles aparecerem.

Prioridades de tarefas

Nem todas as requisições são iguais. Crie uma fila com prioridades: tarefas importantes são executadas primeiro, as não essenciais são adiadas primeiro durante a degradação.

Cache

Para requisições GET idempotentes, adicione um cache com tempo de vida curto. Isso reduz a carga no servidor e sua taxa de 429 sem truques.

Observabilidade

Conecte logs estruturados e métricas. Registre cada repetição, cada abertura do circuit breaker, cada pausa longa. Assim, você encontrará rapidamente o gargalo ao analisar incidentes.

Dica: Comece com um cliente simples e adicione recursos avançados conforme a necessidade real. Complexidade prematura é tão prejudicial quanto sua ausência.

FAQ: perguntas frequentes

Preciso sempre respeitar o Retry-After, mesmo que seja grande?

Sim. Se o Retry-After for muito longo para o seu cenário, é melhor adiar a tarefa ou retornar uma resposta degradada do que repetir antes do prazo. Ignorar o Retry-After quase sempre leva a novos 429.

Posso repetir requisições POST?

Somente com cuidado. Se a operação não for idempotente, repetir pode criar duplicatas. Use uma chave de idempotência para que o próprio servidor o proteja contra execução dupla.

Qual número de requisições simultâneas devo usar como ponto de partida?

Comece baixo, por exemplo, 5 a 10 por host, e aumente observando a proporção de 429 e o p95. Assim que o 429 começar a subir, você encontrou o teto.

Qual a diferença prática entre 429 e 503?

429 é sobre seu ritmo: você está enviando com muita frequência. 503 é sobre o servidor: ele está sobrecarregado ou em manutenção. No 429, é útil reduzir o ritmo e, possivelmente, trocar de IP. No 503, trocar de IP não faz sentido; apenas repita mais tarde.

Por que meu cliente às vezes recebe 407?

O código 407 vem do proxy e significa que a autenticação no proxy falhou. Verifique o login e a senha do proxy. Rotações de IP e backoff não ajudam aqui – é um erro de configuração.

Quantas tentativas de repetição são consideradas normais?

Geralmente de três a cinco. Mais do que isso raramente faz sentido: se não resolveu em cinco tentativas, o problema é mais sério que uma falha temporária.

Para que serve o jitter se o backoff já cresce?

Sem jitter, vários clientes repetem nos mesmos momentos e criam uma enxurrada síncrona. A dispersão aleatória espalha as repetições no tempo e alivia o servidor gradualmente.

Quando abrir o circuit breaker?

Quando a proporção de erros em uma janela deslizante ultrapassar um limiar definido, por exemplo, metade das requisições. Isso protege tanto o servidor quanto você de desperdiçar recursos.

Trocar de IP ajuda contra 429?

Às vezes, se o limite estiver vinculado ao IP. Mas se o limite for por chave ou conta, trocar de IP é inútil. Trocar de IP não substitui reduzir o ritmo e respeitar o Retry-After.

O que mostrar ao usuário quando o serviço está fora?

Um placeholder claro, dados do cache ou um resultado reduzido. Isso é melhor do que um travamento ou um erro técnico na tela.

Conclusão

Você percorreu um longo caminho. Vamos relembrar o que você construiu. Você configurou timeouts para todas as fases, para que nenhuma requisição fique pendente para sempre. Você adicionou retentativas inteligentes com backoff exponencial e jitter, que repetem apenas requisições seguras e respeitam o Retry-After. Você limitou a concorrência com um semáforo e configurou o pool de conexões com keep-alive. Você estabeleceu uma reação correta ao 429 e montou uma matriz de ações para os códigos de resposta. Por fim, você adicionou um circuit breaker e degradação controlada.

A principal lição de todo o guia é simples. 429 não é um erro, é uma conversa. O servidor está pedindo para você desacelerar, e um cliente educado ouve. A resiliência não nasce da agressividade, mas da capacidade de desacelerar no momento certo.

O que fazer a seguir

Colete métricas sob carga real e observe as taxas de sucesso e de 429. Ajuste gradualmente os limites para cada serviço. Adicione limitação adaptativa de ritmo com base nos cabeçalhos X-RateLimit. Implemente cache para requisições GET idempotentes.

Para onde evoluir

Estude separadamente o tópico de pool de endereços IP e sua saúde – é uma área grande vizinha que propositalmente não abordamos aqui. Aprofunde-se em observabilidade: tracing, dashboards, alertas. E, acima de tudo, leia a documentação dos serviços com os quais você trabalha: limites exatos são sempre melhores que suposições.

Você se saiu muito bem. Agora você tem um cliente que não entra em pânico, mas se comporta de forma resiliente e educada. Essa é a base sobre a qual se constroem integrações confiáveis. Boa sorte em seus projetos.