# Como implementar retry com backoff exponencial em consultas de API de CPF

> Implemente retry com backoff exponencial em consultas de CPF via API. Evite sobrecarregar o servidor e aumente a resiliência.

**Publicado:** 20/08/2026
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-implementar-retry-backoff-exponencial-consultas-api-cpf

---


Retry com backoff exponencial é o padrão correto para lidar com falhas transitórias em chamadas à API de CPF. Em vez de repetir a requisição imediatamente -- o que pode sobrecarregar o servidor -- o backoff espaça as tentativas de forma crescente: 1s, 2s, 4s, 8s. Com jitter (variação aleatória), múltiplos clientes evitam retomar no mesmo instante, distribuindo a carga e aumentando a chance de recuperação.

---

## Por que backoff exponencial e não retry simples

### Retry simples (ingênuo)

```
Falha -> retry imediato -> retry imediato -> retry imediato
```

Se 100 clientes fazem isso simultaneamente, o servidor recebe 400 requisições em poucos segundos -- exatamente quando ele menos pode lidar com essa carga.

### Backoff exponencial

```
Falha -> espera 1s -> retry -> espera 2s -> retry -> espera 4s -> retry
```

As tentativas se espaçam, dando tempo para o servidor se recuperar.

### Backoff exponencial com jitter

```
Falha -> espera 1.3s -> retry -> espera 2.7s -> retry -> espera 3.9s -> retry
```

O jitter (variação aleatória) evita que múltiplos clientes retentam no mesmo instante, distribuindo a carga de forma mais uniforme.

---

## Quando fazer retry e quando não fazer

Nem todo erro justifica uma nova tentativa. A regra geral é:

### Erros que justificam retry

- **Timeout:** a requisição não completou no tempo esperado.
- **5xx (Server Error):** o servidor está com problema temporário.
- **Erros de conexão:** problemas de rede transitórios.

### Erros que NÃO justificam retry

- **400 (Bad Request):** a requisição está malformada -- repetir vai dar o mesmo resultado.
- **401 (Unauthorized):** a chave de API está errada -- repetir não vai resolver.
- **404 (Not Found):** o recurso não existe.

---

## Implementação em Python

```python
import time
import random
import requests
import logging
from typing import Optional, Dict

logger = logging.getLogger(__name__)

# Códigos HTTP que justificam retry
RETRYABLE_STATUS_CODES = {500, 502, 503, 504}

def consultar_cpf_com_retry(
 cpf: str,
 api_key: str,
 max_retries: int = 4,
 base_delay: float = 1.0,
 max_delay: float = 30.0,
 timeout: int = 30
) -> Optional[Dict]:
 """
 Consulta CPF com retry e backoff exponencial.

 Args:
 cpf: número do CPF (apenas dígitos)
 api_key: chave de API da CPFHub
 max_retries: número máximo de tentativas
 base_delay: delay inicial em segundos
 max_delay: delay máximo em segundos
 timeout: timeout da requisição HTTP em segundos
 """
 headers = {
 "x-api-key": api_key,
 "Accept": "application/json"
 }
 url = f"https://api.cpfhub.io/cpf/{cpf}"

 for tentativa in range(max_retries + 1):
 try:
 response = requests.get(url, headers=headers, timeout=timeout)

 # Sucesso -- retornar imediatamente
 if response.status_code == 200:
 dados = response.json()
 if tentativa > 0:
 logger.info(
 f"Consulta bem-sucedida na tentativa {tentativa + 1}"
 )
 return dados

 # Erro que justifica retry
 if response.status_code in RETRYABLE_STATUS_CODES:
 if tentativa < max_retries:
 delay = _calcular_delay(tentativa, base_delay, max_delay)
 logger.warning(
 f"HTTP {response.status_code}. "
 f"Retry {tentativa + 1}/{max_retries} em {delay:.1f}s"
 )
 time.sleep(delay)
 continue
 else:
 logger.error(
 f"HTTP {response.status_code} apos {max_retries} retries"
 )
 return None

 # Erro que NAO justifica retry
 logger.error(
 f"HTTP {response.status_code} -- erro nao-retentavel"
 )
 return None

 except requests.exceptions.Timeout:
 if tentativa < max_retries:
 delay = _calcular_delay(tentativa, base_delay, max_delay)
 logger.warning(
 f"Timeout. Retry {tentativa + 1}/{max_retries} em {delay:.1f}s"
 )
 time.sleep(delay)
 else:
 logger.error(f"Timeout apos {max_retries} retries")
 return None

 except requests.exceptions.ConnectionError:
 if tentativa < max_retries:
 delay = _calcular_delay(tentativa, base_delay, max_delay)
 logger.warning(
 f"Erro de conexao. Retry {tentativa + 1}/{max_retries} em {delay:.1f}s"
 )
 time.sleep(delay)
 else:
 logger.error(f"Erro de conexao apos {max_retries} retries")
 return None

 return None

def _calcular_delay(tentativa: int, base_delay: float, max_delay: float) -> float:
 """Calcula delay com backoff exponencial e jitter."""
 delay_exponencial = base_delay * (2 ** tentativa)
 delay_com_jitter = delay_exponencial * (0.5 + random.random())
 return min(delay_com_jitter, max_delay)

# Uso
resultado = consultar_cpf_com_retry(
 cpf="12345678900",
 api_key="SUA_CHAVE_API"
)

if resultado and resultado.get("success"):
 print(f"Nome: {resultado['data']['name']}")
else:
 print("Consulta falhou apos todas as tentativas.")
```

---

## Implementação em Node.js

```javascript
const axios = require("axios");

const RETRYABLE_STATUS_CODES = new Set([500, 502, 503, 504]);
const API_KEY = "SUA_CHAVE_API";
const BASE_URL = "https://api.cpfhub.io/cpf";
const TIMEOUT_MS = 30000;

function calcularDelay(tentativa, baseDelay = 1000, maxDelay = 30000) {
 const delayExponencial = baseDelay * Math.pow(2, tentativa);
 const jitter = delayExponencial * (0.5 + Math.random());
 return Math.min(jitter, maxDelay);
}

function sleep(ms) {
 return new Promise(resolve => setTimeout(resolve, ms));
}

async function consultarCPFComRetry(cpf, maxRetries = 4) {
 const cpfLimpo = cpf.replace(/\D/g, "");

 for (let tentativa = 0; tentativa <= maxRetries; tentativa++) {
 try {
 const response = await axios.get(`${BASE_URL}/${cpfLimpo}`, {
 headers: {
 "x-api-key": API_KEY,
 "Accept": "application/json"
 },
 timeout: TIMEOUT_MS
 });

 if (tentativa > 0) {
 console.log(`Sucesso na tentativa ${tentativa + 1}`);
 }
 return response.data;

 } catch (error) {
 const status = error.response ? error.response.status : null;
 const isRetryable = status
 ? RETRYABLE_STATUS_CODES.has(status)
 : error.code === "ECONNABORTED" || error.code === "ECONNREFUSED";

 if (isRetryable && tentativa < maxRetries) {
 const delay = calcularDelay(tentativa);
 console.warn(
 `Erro ${status || error.code}. Retry ${tentativa + 1}/${maxRetries} em ${Math.round(delay)}ms`
 );
 await sleep(delay);
 } else if (!isRetryable) {
 console.error(`Erro ${status} -- nao retentavel`);
 return null;
 } else {
 console.error(`Falha apos ${maxRetries} retries`);
 return null;
 }
 }
 }

 return null;
}

// Uso
consultarCPFComRetry("123.456.789-00").then(resultado => {
 if (resultado && resultado.success) {
 console.log(`Nome: ${resultado.data.name}`);
 } else {
 console.log("Consulta falhou.");
 }
});
```

---

## Visualizando os intervalos de retry

Para entender concretamente os tempos de espera, veja a progressão típica:

| Tentativa | Delay base | Com jitter (exemplo) | Tempo acumulado |
|-----------|-----------|----------------------|-----------------|
| 1 | 1s | 1.3s | 1.3s |
| 2 | 2s | 2.7s | 4.0s |
| 3 | 4s | 5.1s | 9.1s |
| 4 | 8s | 9.8s | 18.9s |

Após 4 tentativas com backoff exponencial, o tempo total é de aproximadamente 19 segundos -- tempo suficiente para a maioria dos problemas transitórios se resolverem.

---

## Tratamento especial para erros de servidor

Para erros 5xx, o header `Retry-After` pode indicar o tempo recomendado de espera. Quando presente, priorize esse valor:

```python
def tratar_erro_servidor(response, tentativa, base_delay, max_delay):
 """Trata erros 5xx respeitando o header Retry-After quando presente."""
 retry_after = response.headers.get("Retry-After")

 if retry_after:
 try:
 delay = float(retry_after)
 logger.info(f"Retry-After: {delay}s")
 return delay
 except ValueError:
 pass

 # Fallback para backoff exponencial
 return _calcular_delay(tentativa, base_delay, max_delay)
```

---

## Configurações recomendadas para API de CPF

| Parâmetro | Valor recomendado | Justificativa |
|-----------------|-------------------|-----------------------------------------------------|
| max_retries | 3-4 | Equilibra resiliência e tempo de espera total |
| base_delay | 1 segundo | Suficiente para problemas transitórios rápidos |
| max_delay | 30 segundos | Evita esperas excessivas |
| timeout | 30 segundos | Margem sobre a latência média de ~900 ms |
| jitter | 50-100% do delay | Distribui retries de múltiplos clientes |

---

## Testando a implementação

```python
import unittest
from unittest.mock import patch, MagicMock

class TestRetryBackoff(unittest.TestCase):

 @patch("requests.get")
 def test_sucesso_na_primeira_tentativa(self, mock_get):
 mock_response = MagicMock()
 mock_response.status_code = 200
 mock_response.json.return_value = {"success": True, "data": {"name": "Teste"}}
 mock_get.return_value = mock_response

 resultado = consultar_cpf_com_retry("12345678900", "chave_teste")
 self.assertTrue(resultado["success"])
 self.assertEqual(mock_get.call_count, 1)

 @patch("requests.get")
 def test_sucesso_apos_retry(self, mock_get):
 erro_response = MagicMock()
 erro_response.status_code = 503

 sucesso_response = MagicMock()
 sucesso_response.status_code = 200
 sucesso_response.json.return_value = {"success": True, "data": {"name": "Teste"}}

 mock_get.side_effect = [erro_response, erro_response, sucesso_response]

 resultado = consultar_cpf_com_retry(
 "12345678900", "chave_teste", base_delay=0.01
 )
 self.assertTrue(resultado["success"])
 self.assertEqual(mock_get.call_count, 3)
```

---

## Perguntas frequentes

### Qual é a latência esperada da API de CPF e como isso afeta o timeout do retry?
A latência média da API da CPFHub.io é de aproximadamente 900ms. Configure o timeout de cada tentativa em 30 segundos para evitar falsos timeouts antes do servidor responder. Com 4 retries e backoff exponencial, o tempo total máximo de espera é de aproximadamente 19 segundos entre tentativas.

### A API CPFHub.io bloqueia as requisições quando a cota mensal é atingida?
Não. A CPFHub.io não bloqueia quando a cota é atingida -- cada consulta extra é cobrada a R$0,15 automaticamente. O plano Gratuito inclui 50 consultas/mês e o plano Pro inclui 1.000 consultas por R$149/mês. O serviço permanece disponível sem interrupção.

### Como garantir conformidade com a LGPD ao usar uma API de CPF?
Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário (não guarde o CPF cru se um token bastar), implemente controle de acesso aos logs de consulta e documente a base legal para o tratamento. A [ANPD](https://www.gov.br/anpd) orienta que dados de identificação devem ser tratados com o princípio da necessidade.

### Quanto tempo leva para integrar a API CPFHub.io?
A integração básica leva menos de 30 minutos: crie uma conta em cpfhub.io, gere a API key no painel e faça uma chamada GET para `https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key`. A documentação inclui exemplos em Python, Node.js, PHP, Java e outras linguagens.

### Leia também

- [SLA de API de CPF: entenda os níveis de disponibilidade](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [Como implementar circuit breaker em integrações com API de CPF](https://cpfhub.io/blog/como-implementar-circuit-breaker-integracoes-api-cpf)
- [Como monitorar consumo e custos da API de CPF com observabilidade](https://cpfhub.io/blog/api-cpf-monitorar-consumo-custos-observabilidade)
- [API de CPF: guia prático para debugging de erros de integração](https://cpfhub.io/blog/api-cpf-guia-pratico-debugging-erros-integracao)

---

## Conclusão

O retry com backoff exponencial e jitter é o padrão de ouro para lidar com falhas transitórias em chamadas de API. Ele protege tanto a sua aplicação -- que não fica presa em loops de retry infinitos -- quanto o servidor da API -- que não é sobrecarregado com requisições repetidas durante momentos de instabilidade.

A API da [**CPFHub.io**](https://www.cpfhub.io/)

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/)

