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.

Redação CPFHub.io
Redação CPFHub.io
··9 min de leitura
Como implementar retry automático 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 HTTPSignificadoAção recomendada
500Internal Server ErrorRetry com backoff
503Service UnavailableRetry com backoff
TimeoutSem resposta no tempo limiteRetry com backoff

Erros que NÃO justificam retry

Código HTTPSignificadoAção recomendada
400Bad Request (CPF inválido)Corrigir os dados enviados
401Unauthorized (API key inválida)Verificar a chave de API
404Not FoundO 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:

TentativaIntervalo baseCom jitter (aleatoriedade)
1a1 segundo0,8 - 1,2 segundos
2a2 segundos1,6 - 2,4 segundos
3a4 segundos3,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 recomenda backoff exponencial como controle padrão contra sobrecarga em integrações externas.


Implementação em 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)

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

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.


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 — 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.

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

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.

WhatsAppFale conosco via WhatsApp