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
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
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:
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
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 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.
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
Cadastre-se em cpfhub.io
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



