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

**Publicado:** 18/01/2025
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-implementar-circuit-breaker-em-integracoes-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

```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)

```javascript
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.

```python
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âmetro | Recomendação | Justificativa |
| --- | --- | --- |
| Limiar de falhas | 3-5 falhas consecutivas | Baixo o suficiente para reagir rápido |
| Tempo de reset | 15-60 segundos | Tempo razoável para recuperação |
| Timeout da requisição | 10 segundos | Compatível com o tempo de resposta da API |

Para a API da [**CPFHub.io**](https://www.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](https://owasp.org/API-Security/editions/2023/en/0x00-header/) 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.

### Leia também

- [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)
- [Como implementar retry automático em consultas de CPF que falham](https://cpfhub.io/blog/como-implementar-retry-automatico-em-consultas-de-cpf-que-falham)
- [Como implementar health check para monitorar disponibilidade da API de CPF](https://cpfhub.io/blog/como-implementar-health-check-para-monitorar-disponibilidade-da-api-de-cpf)
- [Como implementar retry com backoff exponencial em consultas de API de CPF](https://cpfhub.io/blog/como-implementar-retry-backoff-exponencial-consultas-api-cpf)

---

## 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](https://www.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.

