# Como implementar retry automático em consultas de CPF que falham

> Aprenda a implementar retry com backoff exponencial em consultas de CPF via API. Exemplos em Python, JavaScript e Go com boas práticas.

**Publicado:** 19/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-implementar-retry-automatico-em-consultas-de-cpf-que-falham

---


Retry automático com backoff exponencial é a estratégia mais eficaz para lidar com falhas transitórias em consultas de CPF via API. Em vez de retornar um erro imediatamente ao usuário, a aplicação tenta novamente após intervalos crescentes, recuperando-se de instabilidades de rede e picos de carga sem intervenção manual. Este guia mostra como implementar essa lógica em Python, JavaScript e Go com exemplos prontos para produção.

## Introdução

Nenhuma integração com APIs externas está imune a falhas temporárias. Instabilidades de rede, picos de carga no servidor e timeouts são cenários que acontecem mesmo nas APIs mais confiáveis. Quando uma consulta de CPF falha, a decisão de simplesmente retornar um erro para o usuário ou tentar novamente pode fazer a diferença entre uma experiência fluida e uma frustração desnecessária.

---

## Quando o retry é adequado

Nem toda falha justifica um retry. É importante distinguir entre erros temporários (que podem se resolver sozinhos) e erros permanentes (que exigem ação do desenvolvedor):

### Erros que justificam retry

| Código HTTP | Significado | Ação recomendada |
| --- | --- | --- |
| 500 | Internal Server Error | Retry com backoff |
| 503 | Service Unavailable | Retry com backoff |
| Timeout | Sem resposta no tempo limite | Retry com backoff |

### Erros que NÃO justificam retry

| Código HTTP | Significado | Ação recomendada |
| --- | --- | --- |
| 400 | Bad Request (CPF inválido) | Corrigir os dados enviados |
| 401 | Unauthorized (API key inválida) | Verificar a chave de API |
| 404 | Not Found | O CPF não existe na base |

Fazer retry em erros 400 ou 401 é inútil — o resultado será sempre o mesmo. Vale destacar que a API da CPFHub.io **não retorna HTTP 429 nem bloqueia requisições** quando o limite do plano gratuito é atingido: ela simplesmente cobra R$0,15 por consulta adicional, então não há necessidade de tratar 429 como erro de rate limit.

---

## O que é backoff exponencial

O backoff exponencial é uma estratégia em que o intervalo entre cada tentativa aumenta progressivamente. Isso evita sobrecarregar a API com requisições repetidas e dá tempo para o problema ser resolvido.

**Exemplo de intervalos:**

| Tentativa | Intervalo base | Com jitter (aleatoriedade) |
| --- | --- | --- |
| 1a | 1 segundo | 0,8 - 1,2 segundos |
| 2a | 2 segundos | 1,6 - 2,4 segundos |
| 3a | 4 segundos | 3,2 - 4,8 segundos |

O **jitter** (variação aleatória) evita que múltiplas instâncias da aplicação façam retry exatamente ao mesmo tempo, o que poderia causar um novo pico de carga na API. A [OWASP](https://owasp.org/www-community/controls/Blocking_Brute_Force_Attacks) recomenda backoff exponencial como controle padrão contra sobrecarga em integrações externas.

---

## Implementação em Python

```python
import requests
import time
import random

def consultar_cpf_com_retry(cpf, max_tentativas=3):
 url = f"https://api.cpfhub.io/cpf/{cpf}"
 headers = {
 "x-api-key": "SUA_CHAVE_DE_API",
 "Accept": "application/json"
 }

 codigos_retry = {500, 503}

 for tentativa in range(max_tentativas):
 try:
 response = requests.get(url, headers=headers, timeout=10)

 # Sucesso: retornar dados
 if response.status_code == 200:
 return response.json()

 # Erro permanente: não fazer retry
 if response.status_code in (400, 401, 404):
 return {
 "erro": f"Erro {response.status_code}",
 "retry": False
 }

 # Erro temporário: fazer retry
 if response.status_code in codigos_retry:
 if tentativa < max_tentativas - 1:
 espera = (2 ** tentativa) + random.uniform(0, 1)
 print(f"Tentativa {tentativa + 1} falhou ({response.status_code}). "
 f"Aguardando {espera:.1f}s...")
 time.sleep(espera)
 continue

 except requests.exceptions.Timeout:
 if tentativa < max_tentativas - 1:
 espera = (2 ** tentativa) + random.uniform(0, 1)
 print(f"Timeout na tentativa {tentativa + 1}. "
 f"Aguardando {espera:.1f}s...")
 time.sleep(espera)
 continue

 except requests.exceptions.ConnectionError:
 if tentativa < max_tentativas - 1:
 espera = (2 ** tentativa) + random.uniform(0, 1)
 print(f"Erro de conexão na tentativa {tentativa + 1}. "
 f"Aguardando {espera:.1f}s...")
 time.sleep(espera)
 continue

 return {"erro": "Todas as tentativas falharam", "retry": True}

# Uso
resultado = consultar_cpf_com_retry("12345678900")
print(resultado)
```

---

## Implementação em JavaScript (Node.js)

```javascript
async function consultarCPFComRetry(cpf, maxTentativas = 3) {
 const url = `https://api.cpfhub.io/cpf/${cpf}`;
 const headers = {
 'x-api-key': 'SUA_CHAVE_DE_API',
 'Accept': 'application/json'
 };

 const codigosRetry = new Set([500, 503]);

 for (let tentativa = 0; tentativa < maxTentativas; tentativa++) {
 const controller = new AbortController();
 const timeoutId = setTimeout(() => controller.abort(), 10000);

 try {
 const response = await fetch(url, {
 headers,
 signal: controller.signal
 });

 clearTimeout(timeoutId);

 // Sucesso
 if (response.ok) {
 return await response.json();
 }

 // Erro permanente
 if ([400, 401, 404].includes(response.status)) {
 return { erro: `Erro ${response.status}`, retry: false };
 }

 // Erro temporário
 if (codigosRetry.has(response.status)) {
 if (tentativa < maxTentativas - 1) {
 const espera = Math.pow(2, tentativa) * 1000 + Math.random() * 1000;
 console.log(
 `Tentativa ${tentativa + 1} falhou (${response.status}). ` +
 `Aguardando ${(espera / 1000).toFixed(1)}s...`
 );
 await new Promise(resolve => setTimeout(resolve, espera));
 continue;
 }
 }

 } catch (error) {
 clearTimeout(timeoutId);

 if (tentativa < maxTentativas - 1) {
 const espera = Math.pow(2, tentativa) * 1000 + Math.random() * 1000;
 const tipo = error.name === 'AbortError' ? 'Timeout' : 'Erro de conexão';
 console.log(
 `${tipo} na tentativa ${tentativa + 1}. ` +
 `Aguardando ${(espera / 1000).toFixed(1)}s...`
 );
 await new Promise(resolve => setTimeout(resolve, espera));
 continue;
 }
 }
 }

 return { erro: 'Todas as tentativas falharam', retry: true };
}
```

---

## Implementação em Go

```go
package main

import (
 "fmt"
 "io"
 "math"
 "math/rand"
 "net/http"
 "time"
)

func consultarCPFComRetry(cpf string, maxTentativas int) (string, error) {
 url := fmt.Sprintf("https://api.cpfhub.io/cpf/%s", cpf)
 codigosRetry := map[int]bool{500: true, 503: true}

 client := &http.Client{Timeout: 10 * time.Second}

 for tentativa := 0; tentativa < maxTentativas; tentativa++ {
 req, err := http.NewRequest("GET", url, nil)
 if err != nil {
 return "", err
 }

 req.Header.Set("x-api-key", "SUA_CHAVE_DE_API")
 req.Header.Set("Accept", "application/json")

 resp, err := client.Do(req)
 if err != nil {
 if tentativa < maxTentativas-1 {
 espera := math.Pow(2, float64(tentativa)) + rand.Float64()
 fmt.Printf("Erro na tentativa %d. Aguardando %.1fs...\n",
 tentativa+1, espera)
 time.Sleep(time.Duration(espera * float64(time.Second)))
 continue
 }
 return "", err
 }
 defer resp.Body.Close()

 if resp.StatusCode == 200 {
 body, _ := io.ReadAll(resp.Body)
 return string(body), nil
 }

 if codigosRetry[resp.StatusCode] && tentativa < maxTentativas-1 {
 espera := math.Pow(2, float64(tentativa)) + rand.Float64()
 fmt.Printf("Status %d na tentativa %d. Aguardando %.1fs...\n",
 resp.StatusCode, tentativa+1, espera)
 time.Sleep(time.Duration(espera * float64(time.Second)))
 continue
 }

 body, _ := io.ReadAll(resp.Body)
 return string(body), fmt.Errorf("erro HTTP %d", resp.StatusCode)
 }

 return "", fmt.Errorf("todas as %d tentativas falharam", maxTentativas)
}

func main() {
 resultado, err := consultarCPFComRetry("12345678900", 3)
 if err != nil {
 fmt.Println("Erro:", err)
 return
 }
 fmt.Println(resultado)
}
```

---

## Boas práticas para retry

* **Limite o número de tentativas** — Três tentativas é um bom padrão. Mais do que isso raramente resolve o problema e mantém o usuário esperando por muito tempo.

* **Use backoff exponencial com jitter** — Aumentar o intervalo entre tentativas e adicionar aleatoriedade evita sobrecarregar a API.

* **Classifique os erros** — Apenas erros temporários (5xx, timeout) devem acionar o retry. Erros 4xx indicam problemas no lado do cliente e não melhoram com nova tentativa.

* **Registre cada tentativa** — Mantenha logs com o número da tentativa, o código de erro e o tempo de espera para facilitar a investigação de problemas.

* **Defina um timeout total** — Além do timeout individual de cada requisição, defina um limite máximo para o processo completo de retry, evitando que o usuário fique esperando indefinidamente.

* **Respeite o header Retry-After** — Se a API retornar o header `Retry-After`, use o valor informado em vez do backoff calculado.

---

## Perguntas frequentes

### Quantas tentativas de retry devo configurar para consultas de CPF?

Três tentativas é o padrão recomendado para a maioria dos casos. Com backoff exponencial, isso resulta em esperas de ~1s, ~2s e ~4s entre tentativas — tempo suficiente para falhas transitórias se resolverem sem deixar o usuário aguardando mais de 10 segundos no total. Aumentar para mais de 3 tentativas raramente melhora o resultado e piora a experiência percebida.

### O retry funciona quando o limite de consultas do plano gratuito é atingido?

A API da CPFHub.io não bloqueia nem retorna erro quando o limite do plano gratuito (50 consultas/mês) é ultrapassado. Ela simplesmente cobra R$0,15 por consulta adicional e continua respondendo normalmente. Portanto, não há necessidade de tratar estouro de cota como cenário de retry — a consulta será processada e cobrada automaticamente.

### Devo usar retry no frontend ou no backend?

A lógica de retry deve ficar exclusivamente no backend. Expor a API key no frontend já é um problema de segurança; adicionar retry lá amplia o risco ao tornar a chave ainda mais visível. No backend, você controla timeouts, logs centralizados e evita que o usuário dispare múltiplas tentativas simultâneas clicando várias vezes no botão.

### Como o jitter ajuda a evitar sobrecarga na API?

Quando várias instâncias da aplicação falham ao mesmo tempo (por exemplo, durante um pico de tráfego), elas tendem a fazer retry no mesmo instante — criando uma nova onda de requisições que piora a sobrecarga. O jitter adiciona uma variação aleatória de alguns décimos de segundo ao intervalo de espera, espalhando as tentativas ao longo do tempo e reduzindo o impacto sobre a API.

### Leia também

- [Como implementar retry com backoff exponencial em consultas de API de CPF](https://cpfhub.io/blog/como-implementar-retry-backoff-exponencial-consultas-api-cpf)
- [Como construir um sistema de retry inteligente com Dead Letter Queue para consultas de CPF](https://cpfhub.io/blog/retry-inteligente-dead-letter-queue-cpf)
- [Como implementar rate limiting e exponential backoff ao consumir APIs de CPF](https://cpfhub.io/blog/rate-limiting-exponential-backoff-apis-cpf)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)

---

## Conclusão

Implementar retry automático com backoff exponencial é uma prática essencial para tornar a integração com APIs de CPF mais resiliente. Ao classificar corretamente os erros e aplicar intervalos crescentes entre as tentativas, sua aplicação consegue se recuperar de falhas temporárias sem impactar a experiência do usuário.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a testar sua lógica de retry com uma API estável e previsível.

