Como implementar circuit breaker em integrações com API de CPF

Aprenda a implementar o padrão circuit breaker para proteger sua aplicação quando a API de CPF apresenta falhas. Exemplos em Python e Node.js.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como implementar circuit breaker em integrações com API de CPF

O padrão circuit breaker protege sua aplicação quando a API de CPF apresenta falhas persistentes: ao detectar um número excessivo de erros consecutivos, ele "abre o circuito" e interrompe temporariamente as chamadas, permitindo que o serviço se recupere sem sobrecarregar a infraestrutura.

Introdução

Quando uma API externa apresenta falhas persistentes, continuar enviando requisições não apenas não resolve o problema como pode agravar a situação — tanto para a API quanto para a sua aplicação. O padrão circuit breaker resolve esse problema ao "abrir o circuito" quando um número excessivo de falhas é detectado, interrompendo temporariamente as chamadas e permitindo que o serviço se recupere.


Como funciona o circuit breaker

O circuit breaker opera em três estados:

1. Fechado (Closed)

Estado normal de operação. Todas as requisições passam normalmente para a API. O circuit breaker monitora a taxa de falhas. Quando o número de falhas consecutivas atinge um limiar predefinido, o circuito "abre".

2. Aberto (Open)

Nenhuma requisição é enviada para a API. Todas as chamadas retornam imediatamente com um erro ou um valor de fallback. Após um período de espera (tempo de reset), o circuito passa para o estado "meio-aberto".

3. Meio-aberto (Half-Open)

Uma única requisição de teste é enviada para a API. Se essa requisição for bem-sucedida, o circuito volta ao estado "fechado". Se falhar, o circuito retorna ao estado "aberto" e o timer de reset é reiniciado.


Implementação em Python

import requests
import time
from enum import Enum

class Estado(Enum):
    FECHADO = "fechado"
    ABERTO = "aberto"
    MEIO_ABERTO = "meio_aberto"

class CircuitBreaker:
    def __init__(self, limiar_falhas=5, tempo_reset=30):
    self.limiar_falhas = limiar_falhas
    self.tempo_reset = tempo_reset
    self.estado = Estado.FECHADO
    self.falhas_consecutivas = 0
    self.ultimo_tempo_falha = None

    def pode_executar(self):
    if self.estado == Estado.FECHADO:
    return True

    if self.estado == Estado.ABERTO:
    tempo_passado = time.time() - self.ultimo_tempo_falha
    if tempo_passado >= self.tempo_reset:
    self.estado = Estado.MEIO_ABERTO
    return True
    return False

    if self.estado == Estado.MEIO_ABERTO:
    return True

    return False

    def registrar_sucesso(self):
    self.falhas_consecutivas = 0
    self.estado = Estado.FECHADO

    def registrar_falha(self):
    self.falhas_consecutivas += 1
    self.ultimo_tempo_falha = time.time()

    if self.falhas_consecutivas >= self.limiar_falhas:
    self.estado = Estado.ABERTO
    print(f"Circuit breaker ABERTO após {self.falhas_consecutivas} falhas")

    def get_estado(self):
    return self.estado.value

# Instância global do circuit breaker
cb = CircuitBreaker(limiar_falhas=5, tempo_reset=30)

def consultar_cpf(cpf):
    if not cb.pode_executar():
    return {
    "erro": "Serviço temporariamente indisponível (circuit breaker aberto)",
    "circuit_breaker": cb.get_estado()
    }

    url = f"https://api.cpfhub.io/cpf/{cpf}"
    headers = {
    "x-api-key": "SUA_CHAVE_DE_API",
    "Accept": "application/json"
    }

    try:
    response = requests.get(url, headers=headers, timeout=10)

    if response.status_code == 200:
    cb.registrar_sucesso()
    return response.json()

    if response.status_code in (500, 502, 503):
    cb.registrar_falha()
    return {"erro": f"Erro do servidor: {response.status_code}"}

    # Erros do cliente (4xx) não devem acionar o circuit breaker
    return {"erro": f"Erro: {response.status_code}"}

    except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
    cb.registrar_falha()
    return {"erro": "Timeout ou erro de conexão"}

# Uso
resultado = consultar_cpf("12345678900")
print(f"Estado do circuit breaker: {cb.get_estado()}")
print(resultado)

Implementação em JavaScript (Node.js)

class CircuitBreaker {
    constructor(options = {}) {
    this.limiarFalhas = options.limiarFalhas || 5;
    this.tempoReset = options.tempoReset || 30000; // 30 segundos
    this.estado = 'fechado';
    this.falhasConsecutivas = 0;
    this.ultimaFalha = null;
    }

    podeExecutar() {
    if (this.estado === 'fechado') return true;

    if (this.estado === 'aberto') {
    const tempoPassado = Date.now() - this.ultimaFalha;
    if (tempoPassado >= this.tempoReset) {
    this.estado = 'meio_aberto';
    return true;
    }
    return false;
    }

    return this.estado === 'meio_aberto';
    }

    registrarSucesso() {
    this.falhasConsecutivas = 0;
    this.estado = 'fechado';
    }

    registrarFalha() {
    this.falhasConsecutivas++;
    this.ultimaFalha = Date.now();

    if (this.falhasConsecutivas >= this.limiarFalhas) {
    this.estado = 'aberto';
    console.log(
    `Circuit breaker ABERTO após ${this.falhasConsecutivas} falhas`
    );
    }
    }
}

const cb = new CircuitBreaker({ limiarFalhas: 5, tempoReset: 30000 });

async function consultarCPF(cpf) {
    if (!cb.podeExecutar()) {
    return {
    erro: 'Serviço temporariamente indisponível',
    circuitBreaker: cb.estado
    };
    }

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    try {
    const response = await fetch(
    `https://api.cpfhub.io/cpf/${cpf}`,
    {
    headers: {
    'x-api-key': 'SUA_CHAVE_DE_API',
    'Accept': 'application/json'
    },
    signal: controller.signal
    }
    );

    clearTimeout(timeoutId);

    if (response.ok) {
    cb.registrarSucesso();
    return await response.json();
    }

    if ([500, 502, 503].includes(response.status)) {
    cb.registrarFalha();
    return { erro: `Erro do servidor: ${response.status}` };
    }

    return { erro: `Erro: ${response.status}` };

    } catch (error) {
    clearTimeout(timeoutId);
    cb.registrarFalha();
    return { erro: error.name === 'AbortError' ? 'Timeout' : error.message };
    }
}

Quando o circuit breaker deve abrir

O circuit breaker deve ser acionado apenas por falhas que indicam um problema no servidor ou na rede — nunca por erros do cliente:

  • Acionar o circuit breaker — Erros 5xx (500, 502, 503), timeouts e erros de conexão.

  • Não acionar o circuit breaker — Erros 4xx como 400 (CPF inválido), 401 (API key incorreta) e 404 (CPF não encontrado). Esses erros são previsíveis e não indicam instabilidade da API. Vale lembrar que a API CPFHub.io não retorna HTTP 429: ao superar o limite do plano gratuito, ela cobra R$0,15 por consulta adicional sem bloquear as requisições.


Combinando circuit breaker com fallback

Quando o circuit breaker está aberto, em vez de simplesmente retornar um erro, a aplicação pode oferecer um fallback:

  • Cache — Retornar o último resultado conhecido para aquele CPF.

  • Fila — Enfileirar a consulta para processamento posterior.

  • Degradação graceful — Prosseguir com o fluxo sem a validação de CPF, sinalizando que a verificação será feita posteriormente.

import redis
import json

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def consultar_cpf_com_fallback(cpf):
    if not cb.pode_executar():
    # Fallback: tentar cache
    cached = redis_client.get(f"cpf_cache:{cpf}")
    if cached:
    dados = json.loads(cached)
    dados["fonte"] = "cache"
    return dados

    return {
    "erro": "Serviço indisponível e sem cache disponível",
    "acao": "enfileirado_para_reprocessamento"
    }

    url = f"https://api.cpfhub.io/cpf/{cpf}"
    headers = {
    "x-api-key": "SUA_CHAVE_DE_API",
    "Accept": "application/json"
    }

    try:
    response = requests.get(url, headers=headers, timeout=10)

    if response.status_code == 200:
    cb.registrar_sucesso()
    dados = response.json()

    # Atualizar cache
    if dados.get("success"):
    redis_client.setex(
    f"cpf_cache:{cpf}",
    86400,
    json.dumps(dados)
    )

    return dados

    cb.registrar_falha()
    return {"erro": f"HTTP {response.status_code}"}

    except Exception:
    cb.registrar_falha()
    return {"erro": "Falha na requisição"}

Configurando os parâmetros ideais

ParâmetroRecomendaçãoJustificativa
Limiar de falhas3-5 falhas consecutivasBaixo o suficiente para reagir rápido
Tempo de reset15-60 segundosTempo razoável para recuperação
Timeout da requisição10 segundosCompatível com o tempo de resposta da API

Para a API da CPFHub.io, recomenda-se timeout de 10 segundos — valor conservador que acomoda a latência típica de ~900ms e deixa margem para picos ocasionais sem disparar o circuit breaker prematuramente.


Monitorando o circuit breaker

Registre as transições de estado do circuit breaker em seus logs e métricas:

  • Abertura do circuito — Indica que a API está enfrentando problemas.
  • Fechamento do circuito — Indica que o problema foi resolvido.
  • Tentativas em estado meio-aberto — Mostram a frequência das verificações de recuperação.

Essas métricas ajudam a identificar padrões de instabilidade e a ajustar os parâmetros ao longo do tempo. O OWASP API Security Top 10 recomenda monitoramento ativo de dependências externas como parte das boas práticas de segurança em integrações.


Perguntas frequentes

O circuit breaker é necessário mesmo quando a API tem alta disponibilidade?

Sim. Nenhuma API externa tem 100% de disponibilidade garantida, e falhas transitórias de rede podem ocorrer independentemente do SLA do provedor. O circuit breaker protege sua aplicação durante esses intervalos curtos — sem ele, requisições em cascata podem sobrecarregar tanto o servidor externo quanto o seu próprio backend em momentos de instabilidade.

Qual o limiar de falhas recomendado para integrações com API de CPF?

Para consultas de CPF em fluxos de cadastro, 3 a 5 falhas consecutivas é um limiar razoável. Com volume alto (onboarding em massa), prefira 5 falhas para evitar aberturas falsas por variações normais de latência. Com volume baixo, 3 falhas já é suficiente para detectar instabilidade real sem atrasar a resposta ao usuário.

O que acontece com as consultas enfileiradas quando o circuito fecha novamente?

Depende da estratégia de fallback implementada. Se a aplicação usou uma fila de reprocessamento, as consultas pendentes são processadas após o fechamento do circuito. Se usou degradação graceful (marcando registros para revisão posterior), um job pode buscar os registros pendentes e revalidar os CPFs assim que a conectividade for restabelecida.

O circuit breaker interfere no comportamento de cobrança por excedente da CPFHub.io?

Não. A lógica de cobrança fica inteiramente no servidor da API. O circuit breaker apenas decide se a sua aplicação envia ou não a requisição. Consultas bem-sucedidas são contabilizadas normalmente; consultas bloqueadas pelo circuit breaker não chegam ao servidor e, portanto, não são cobradas.


Conclusão

O circuit breaker é um padrão de resiliência essencial para qualquer integração com APIs externas. Ao interromper temporariamente as chamadas quando a API apresenta falhas persistentes, ele protege tanto a sua aplicação quanto o serviço externo, permitindo uma recuperação mais rápida e uma experiência de usuário mais estável.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e implemente o circuit breaker desde o primeiro dia para garantir que sua integração resista a qualquer instabilidade de rede.

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