Como Evitar Quedas de Serviço ao Usar uma API Gratuita de CPF

Aprenda a evitar quedas de serviço ao usar uma API gratuita de CPF. Estratégias de cache, fallback, circuit breaker e degradação graciosa.

Redação CPFHub.io
Redação CPFHub.io
··9 min de leitura
Como Evitar Quedas de Serviço ao Usar uma API Gratuita de CPF

Para evitar quedas de serviço ao usar uma API gratuita de CPF, implemente quatro camadas em sequência: cache local com TTL de 24 horas para CPFs já consultados, timeout de 10 segundos por requisição, circuit breaker que ativa fallback após 5 falhas consecutivas e validação algorítmica local como último recurso. Essas estratégias mantêm o sistema funcional mesmo quando a API está temporariamente indisponível.

Introdução

APIs gratuitas oferecem um excelente custo-benefício, mas tipicamente não vêm com garantias formais de disponibilidade (SLA). Isso não significa que seu sistema precisa ficar vulnerável a indisponibilidades. Com as estratégias certas de resiliência, cache, fallback e degradação graciosa, você pode construir um sistema que funciona de forma confiável mesmo quando a API está temporariamente fora do ar.


Camadas de proteção contra indisponibilidade

A resiliência é construída em camadas, onde cada uma protege contra um tipo diferente de falha.

CamadaProteção contraImplementação
Cache localRequisições repetidasIn-memory ou Redis
TimeoutAPI lenta ou travadaTimeout de 5-10s por requisição
Retry com backoffFalhas transitórias3 tentativas com delay exponencial
Circuit breakerAPI completamente foraEstado aberto após N falhas
Fallback localTodas as falhas de redeValidação algorítmica do CPF
Fila assíncronaPicos de demandaProcessar em background
  • Defesa em profundidade -- cada camada captura falhas que passaram pela anterior
  • Graceful degradation -- o sistema oferece funcionalidade reduzida em vez de falhar completamente
  • Transparência -- o usuário recebe feedback sobre o nível de validação aplicado

Implementando cache para reduzir dependência

Cache é a primeira e mais eficaz camada de proteção. CPFs consultados recentemente não precisam ir à API.

import time
import hashlib
from collections import OrderedDict
import requests
import os

class CPFCache:
    def __init__(self, max_entries: int = 10000, ttl_seconds: int = 86400):
    self.cache = OrderedDict()
    self.max_entries = max_entries
    self.ttl = ttl_seconds
    self.hits = 0
    self.misses = 0

    def get(self, cpf: str):
    chave = self._hash(cpf)
    if chave in self.cache:
    entrada = self.cache[chave]
    if time.time() - entrada["timestamp"] < self.ttl:
    self.hits += 1
    self.cache.move_to_end(chave)
    return entrada["dados"]
    else:
    del self.cache[chave]
    self.misses += 1
    return None

    def set(self, cpf: str, dados: dict):
    chave = self._hash(cpf)
    if len(self.cache) >= self.max_entries:
    self.cache.popitem(last=False)
    self.cache[chave] = {
    "dados": dados,
    "timestamp": time.time()
    }

    def _hash(self, cpf: str) -> str:
    return hashlib.sha256(cpf.encode()).hexdigest()

    @property
    def taxa_hit(self) -> float:
    total = self.hits + self.misses
    return (self.hits / total * 100) if total > 0 else 0

class CPFServiceResilient:
    def __init__(self, api_key: str):
    self.api_key = api_key
    self.base_url = "https://api.cpfhub.io/cpf"
    self.cache = CPFCache(max_entries=10000, ttl_seconds=86400)
    self.session = requests.Session()
    self.session.headers["x-api-key"] = api_key

    def consultar(self, cpf: str) -> dict:
    cpf_limpo = "".join(c for c in cpf if c.isdigit())

    # Camada 1: Cache
    cached = self.cache.get(cpf_limpo)
    if cached:
    return {**cached, "origem": "cache"}

    # Camada 2: API com timeout
    try:
    response = self.session.get(
    f"{self.base_url}/{cpf_limpo}",
    timeout=10
    )

    if response.status_code == 200:
    dados = response.json()
    if dados.get("success"):
    self.cache.set(cpf_limpo, dados["data"])
    return {**dados["data"], "origem": "api"}

    return {"valido": False, "erro": "CPF não encontrado", "origem": "api"}

    except Exception:
    # Camada 3: Fallback local
    return self._validacao_local(cpf_limpo)

    def _validacao_local(self, cpf: str) -> dict:
    digits = [int(d) for d in cpf]
    if len(digits) != 11 or len(set(digits)) == 1:
    return {"valido": False, "erro": "Formato inválido", "origem": "local"}

    soma1 = sum(digits[i] * (10 - i) for i in range(9))
    check1 = 0 if soma1 % 11 < 2 else 11 - (soma1 % 11)
    soma2 = sum(digits[i] * (11 - i) for i in range(10))
    check2 = 0 if soma2 % 11 < 2 else 11 - (soma2 % 11)

    if digits[9] == check1 and digits[10] == check2:
    return {
    "valido": None,
    "nota": "Formato válido, dados pendentes de confirmação",
    "origem": "local"
    }
    return {"valido": False, "erro": "CPF inválido", "origem": "local"}
CenárioCache hitChamada APIFallbackResultado
CPF recenteSimNãoNãoInstantâneo do cache
CPF novo, API okNãoSimNãoDados completos da API
CPF novo, API foraNãoFalhaSimValidação local apenas
  • Hash do CPF -- armazenar o hash em vez do CPF em texto claro protege dados em caso de leak do cache
  • LRU eviction -- quando o cache atinge o limite, as entradas mais antigas são removidas
  • Taxa de hit -- monitore essa métrica para dimensionar o tamanho do cache

Implementando circuit breaker

O circuit breaker impede que seu sistema fique tentando chamar uma API que está claramente fora do ar.

import time
from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"
    OPEN = "open"
    HALF_OPEN = "half_open"

class CircuitBreaker:
    def __init__(
    self,
    falhas_para_abrir: int = 5,
    tempo_recuperacao: int = 60,
    sucesso_para_fechar: int = 3
    ):
    self.estado = CircuitState.CLOSED
    self.falhas_consecutivas = 0
    self.sucessos_half_open = 0
    self.falhas_para_abrir = falhas_para_abrir
    self.tempo_recuperacao = tempo_recuperacao
    self.sucesso_para_fechar = sucesso_para_fechar
    self.ultimo_falha = 0

    def pode_executar(self) -> bool:
    if self.estado == CircuitState.CLOSED:
    return True
    if self.estado == CircuitState.OPEN:
    if time.time() - self.ultimo_falha > self.tempo_recuperacao:
    self.estado = CircuitState.HALF_OPEN
    self.sucessos_half_open = 0
    return True
    return False
    return True # HALF_OPEN permite tentativa

    def registrar_sucesso(self):
    if self.estado == CircuitState.HALF_OPEN:
    self.sucessos_half_open += 1
    if self.sucessos_half_open >= self.sucesso_para_fechar:
    self.estado = CircuitState.CLOSED
    self.falhas_consecutivas = 0
    else:
    self.falhas_consecutivas = 0

    def registrar_falha(self):
    self.falhas_consecutivas += 1
    self.ultimo_falha = time.time()
    if self.falhas_consecutivas >= self.falhas_para_abrir:
    self.estado = CircuitState.OPEN

# Integração com o serviço
class CPFServiceComCircuitBreaker:
    def __init__(self, api_key: str):
    self.service = CPFServiceResilient(api_key)
    self.breaker = CircuitBreaker(falhas_para_abrir=5, tempo_recuperacao=60)

    def consultar(self, cpf: str) -> dict:
    if not self.breaker.pode_executar():
    return self.service._validacao_local(
    "".join(c for c in cpf if c.isdigit())
    )

    resultado = self.service.consultar(cpf)

    if resultado.get("origem") == "api":
    self.breaker.registrar_sucesso()
    elif resultado.get("origem") == "local":
    self.breaker.registrar_falha()

    return resultado
EstadoComportamentoTransição
ClosedTodas as requisições passamAbre após 5 falhas consecutivas
OpenNenhuma requisição passa, vai direto ao fallbackSemi-abre após 60 segundos
Half-OpenPermite 1 requisição de testeFecha após 3 sucessos consecutivos
  • Proteção mútua -- protege tanto o seu sistema (evitando timeouts) quanto a API (reduzindo carga)
  • Recuperação automática -- o circuito se fecha automaticamente quando a API volta a funcionar
  • Feedback imediato -- quando o circuito está aberto, o fallback responde instantaneamente

Monitorando a saúde da integração

Visibilidade sobre o comportamento da integração é essencial para agir proativamente.

class MonitorSaude:
    def __init__(self, service: CPFServiceComCircuitBreaker):
    self.service = service
    self.metricas = {
    "total": 0,
    "cache_hits": 0,
    "api_calls": 0,
    "fallbacks": 0,
    "erros": 0
    }

    def registrar(self, resultado: dict):
    self.metricas["total"] += 1
    origem = resultado.get("origem", "desconhecido")

    if origem == "cache":
    self.metricas["cache_hits"] += 1
    elif origem == "api":
    self.metricas["api_calls"] += 1
    elif origem == "local":
    self.metricas["fallbacks"] += 1

    if resultado.get("valido") is False:
    self.metricas["erros"] += 1

    def relatorio(self) -> dict:
    total = max(self.metricas["total"], 1)
    return {
    "total_consultas": self.metricas["total"],
    "taxa_cache": f"{self.metricas['cache_hits']/total*100:.1f}%",
    "taxa_api": f"{self.metricas['api_calls']/total*100:.1f}%",
    "taxa_fallback": f"{self.metricas['fallbacks']/total*100:.1f}%",
    "circuit_breaker": self.service.breaker.estado.value,
    "cache_hit_rate": f"{self.service.service.cache.taxa_hit:.1f}%"
    }
  • Taxa de fallback -- se está alta, a API pode estar instável e merece investigação
  • Estado do circuit breaker -- monitorar se está abrindo com frequência indica problemas
  • Cache hit rate -- taxa acima de 50% indica boa economia de chamadas à API

Perguntas frequentes

O que é validação local de CPF e quando usá-la como fallback?

A validação local verifica matematicamente os dígitos verificadores do CPF sem consultar nenhuma API. Ela confirma que o número tem formato válido, mas não verifica se o CPF existe de fato ou quem é o titular. Use como fallback apenas quando a API estiver indisponível — deixando claro ao usuário que a validação completa será feita posteriormente.

Qual TTL (tempo de vida) é recomendado para o cache de respostas da API de CPF?

Para dados cadastrais de CPF, um TTL de 24 horas é razoável: mudanças de nome ou situação cadastral são raras e o Banco de Dados da Receita Federal é atualizado com baixa frequência. Para contextos onde atualização em tempo real é crítica (como KYC regulatório), reduza o TTL para 1-6 horas ou não faça cache da resposta.

A API CPFHub.io bloqueia requisições quando o limite do plano gratuito é atingido?

Não. O CPFHub.io não bloqueia consultas ao atingir o limite mensal: cada consulta excedente é cobrada automaticamente a R$0,15, sem interrupção. Isso significa que o circuit breaker não precisa ser ativado por razão de limite — ele serve apenas para proteger contra falhas de rede ou indisponibilidade temporária da API.

Como o CERT.br recomenda tratar dependências externas em sistemas críticos?

O CERT.br orienta que sistemas críticos não devem depender de um único ponto de falha externo. Aplicar circuit breaker, timeout agressivo e fallback local são práticas alinhadas com esse princípio — garantindo que uma falha em um fornecedor externo não derrube toda a operação interna.


Conclusão

Usar uma API gratuita de CPF não significa aceitar indisponibilidade. Com cache local, circuit breaker, fallback de validação local e monitoramento de saúde, seu sistema mantém funcionalidade mesmo durante períodos de instabilidade da API. Essas estratégias são boas práticas independentemente do plano: elas protegem seu sistema e reduzem custos mesmo em planos pagos.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e construa sua camada de resiliência já na primeira semana de integração.

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