# Como implementar dead letter queue para consultas de CPF que falharam

> Aprenda a implementar uma dead letter queue para tratar consultas de CPF que falharam e garantir que nenhuma validação seja perdida.

**Publicado:** 26/12/2025
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/implementar-dead-letter-queue-consultas-cpf-falharam

---


Uma dead letter queue (DLQ) garante que nenhuma consulta de CPF seja perdida silenciosamente em caso de timeout, erro de rede ou indisponibilidade temporária da API. Com a CPFHub.io, a integração é feita via `GET https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key`; ao capturar as falhas em uma DLQ com Redis ou Bull, você cria uma camada de resiliência que reprocessa automaticamente as validações que falharam.

## Introdução

Em qualquer integração com APIs externas, falhas são inevitáveis. Timeouts, erros de rede e indisponibilidades temporárias podem impedir que uma consulta de CPF seja concluída. Sem um mecanismo para capturar e reprocessar essas falhas, consultas são perdidas silenciosamente — e com elas, validações críticas para o seu negócio.

---

## O que é uma dead letter queue

Uma dead letter queue é uma fila secundária que recebe mensagens que não puderam ser processadas com sucesso após um número definido de tentativas. Em vez de descartar a consulta, ela é movida para a DLQ para análise e reprocessamento posterior.

O fluxo típico:

1. A consulta de CPF é enfileirada na fila principal.
2. O worker tenta processar (chamar a API).
3. Se falhar, tenta novamente (retry).
4. Após N tentativas sem sucesso, move para a DLQ.
5. A equipe analisa a DLQ e decide como tratar.

---

## Quando uma consulta vai para a DLQ

| Cenário | Tentativas recomendadas | Motivo da DLQ |
| --- | --- | --- |
| Timeout | 3 | API temporariamente lenta |
| Erro de rede | 3 | Problema de conectividade |
| Status 500/503 | 3 | Erro no servidor da API |
| Status 401 | 1 | Chave inválida (não adianta retentativa) |
| Status 400 | 1 | CPF mal formatado (não adianta retentativa) |

---

## Implementação com Redis

```python
import requests
import json
import time
import redis
from datetime import datetime

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

FILA_PRINCIPAL = 'fila:consulta_cpf'
FILA_DLQ = 'fila:consulta_cpf:dlq'
MAX_TENTATIVAS = 3

headers = {
 'x-api-key': 'SUA_CHAVE_DE_API',
 'Accept': 'application/json'
}

def enfileirar_consulta(cpf, contexto=None):
 mensagem = {
 'cpf': cpf,
 'tentativa': 0,
 'contexto': contexto or {},
 'criado_em': datetime.utcnow().isoformat()
 }
 r.lpush(FILA_PRINCIPAL, json.dumps(mensagem))

def processar_fila():
 while True:
 item = r.brpop(FILA_PRINCIPAL, timeout=5)
 if not item:
 continue

 mensagem = json.loads(item[1])
 cpf = mensagem['cpf']
 tentativa = mensagem['tentativa'] + 1

 try:
 response = requests.get(
 f'https://api.cpfhub.io/cpf/{cpf}',
 headers=headers,
 timeout=15
 )

 if response.status_code == 200:
 resultado = response.json()
 salvar_resultado(cpf, resultado, mensagem['contexto'])
 print(f'OK: {cpf} (tentativa {tentativa})')
 continue

 elif response.status_code in [400, 401]:
 # Erros que nao melhoram com retry
 mover_para_dlq(mensagem, f'http_{response.status_code}', tentativa)
 continue

 except requests.exceptions.Timeout:
 pass
 except requests.exceptions.ConnectionError:
 time.sleep(5)

 # Retentativa ou DLQ
 if tentativa >= MAX_TENTATIVAS:
 mover_para_dlq(mensagem, 'max_tentativas', tentativa)
 else:
 mensagem['tentativa'] = tentativa
 mensagem['ultima_tentativa'] = datetime.utcnow().isoformat()
 r.lpush(FILA_PRINCIPAL, json.dumps(mensagem))
 # Backoff exponencial
 time.sleep(2 ** tentativa)

def mover_para_dlq(mensagem, motivo, tentativa):
 mensagem['dlq_motivo'] = motivo
 mensagem['dlq_tentativas'] = tentativa
 mensagem['dlq_timestamp'] = datetime.utcnow().isoformat()
 r.lpush(FILA_DLQ, json.dumps(mensagem))
 print(f'DLQ: {mensagem["cpf"]} - {motivo} (apos {tentativa} tentativas)')

def salvar_resultado(cpf, resultado, contexto):
 r.setex(
 f'cpf:resultado:{cpf}',
 86400,
 json.dumps(resultado)
 )
```

---

## Implementação com Bull (Node.js)

```javascript
const Queue = require('bull');

const filaConsulta = new Queue('consulta-cpf', {
 redis: { host: 'localhost', port: 6379 },
 defaultJobOptions: {
 attempts: 3,
 backoff: {
 type: 'exponential',
 delay: 2000
 },
 removeOnComplete: true,
 removeOnFail: false
 }
});

// Fila DLQ
const filaDLQ = new Queue('consulta-cpf-dlq', {
 redis: { host: 'localhost', port: 6379 }
});

// Processador principal
filaConsulta.process(async (job) => {
 const { cpf, contexto } = job.data;

 const response = await fetch(`https://api.cpfhub.io/cpf/${cpf}`, {
 method: 'GET',
 headers: {
 'x-api-key': process.env.CPFHUB_API_KEY,
 'Accept': 'application/json'
 },
 signal: AbortSignal.timeout(15000)
 });

 if (response.status === 401) {
 // Mover direto para DLQ (sem retry)
 await filaDLQ.add({
 cpf,
 contexto,
 motivo: 'api_key_invalida',
 tentativas: job.attemptsMade + 1
 });
 return;
 }

 if (!response.ok) {
 throw new Error(`HTTP ${response.status}`);
 }

 return response.json();
});

// Listener de falha final
filaConsulta.on('failed', async (job, err) => {
 if (job.attemptsMade >= job.opts.attempts) {
 await filaDLQ.add({
 cpf: job.data.cpf,
 contexto: job.data.contexto,
 motivo: err.message,
 tentativas: job.attemptsMade
 });
 console.log(`DLQ: ${job.data.cpf} - ${err.message}`);
 }
});

// Enfileirar consulta
async function solicitarConsulta(cpf, contexto = {}) {
 await filaConsulta.add({ cpf, contexto });
}
```

---

## Monitorando a DLQ

Crie um endpoint para acompanhar as mensagens na DLQ:

```python
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/dlq/status')
def dlq_status():
 tamanho = r.llen(FILA_DLQ)
 mensagens = []
 for i in range(min(tamanho, 20)): # ultimas 20
 msg = r.lindex(FILA_DLQ, i)
 if msg:
 mensagens.append(json.loads(msg))

 # Agrupar por motivo
 motivos = {}
 for msg in mensagens:
 motivo = msg.get('dlq_motivo', 'desconhecido')
 motivos[motivo] = motivos.get(motivo, 0) + 1

 return jsonify({
 'total_dlq': tamanho,
 'por_motivo': motivos,
 'ultimas_mensagens': mensagens[:10]
 })
```

---

## Reprocessando a DLQ

Após resolver o problema (renovar a chave, aguardar o restabelecimento da conectividade), reprocesse as mensagens:

```python
def reprocessar_dlq(filtro_motivo=None):
 tamanho = r.llen(FILA_DLQ)
 reprocessados = 0
 mantidos = 0

 mensagens_manter = []

 for _ in range(tamanho):
 item = r.rpop(FILA_DLQ)
 if not item:
 break

 mensagem = json.loads(item)

 if filtro_motivo and mensagem.get('dlq_motivo') != filtro_motivo:
 mensagens_manter.append(item)
 mantidos += 1
 continue

 # Resetar tentativas e reenviar para fila principal
 mensagem['tentativa'] = 0
 mensagem['reprocessado_em'] = datetime.utcnow().isoformat()
 r.lpush(FILA_PRINCIPAL, json.dumps(mensagem))
 reprocessados += 1

 # Devolver mensagens que nao foram reprocessadas
 for msg in mensagens_manter:
 r.lpush(FILA_DLQ, msg)

 print(f'Reprocessados: {reprocessados} | Mantidos: {mantidos}')

# Reprocessar apenas falhas por tentativas esgotadas
reprocessar_dlq('max_tentativas')
```

---

## Alertas para a DLQ

Configure alertas quando a DLQ começar a crescer. O guia de boas práticas de segurança em APIs da [OWASP](https://owasp.org/) recomenda monitorar ativamente filas de erro para detectar padrões anômalos:

* **Threshold de tamanho** -- Alerta se a DLQ tiver mais de 10 mensagens.

* **Taxa de crescimento** -- Alerta se mais de 5 mensagens entrarem na DLQ em 1 hora.

* **Mensagens antigas** -- Alerta se houver mensagens na DLQ há mais de 24 horas.

---

## Perguntas frequentes

### O que é necessário para implementar validação de CPF neste contexto?
A validação de CPF exige uma chamada à API com o número do documento e a chave de autenticação. A CPFHub.io retorna o status do CPF, nome do titular e data de nascimento em ~900ms, permitindo a verificação em tempo real durante o cadastro ou transação.

### A API CPFHub.io funciona para todos os volumes de consulta?
Sim. O plano gratuito oferece 50 consultas por mês sem cartão de crédito — ideal para testes e projetos pequenos. Para volumes maiores, o plano Pro inclui 1.000 consultas mensais por R$149. Se o limite for ultrapassado, a API não bloqueia: cobra R$0,15 por consulta adicional.

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

### Leia também

- [Diferença entre validação de CPF e consulta de CPF: quando usar cada uma](https://cpfhub.io/blog/diferenca-entre-validacao-de-cpf-e-consulta-de-cpf-quando-usar-cada-uma)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [Onboarding digital em fintechs: como validar CPF em menos de 30 segundos](https://cpfhub.io/blog/onboarding-digital-em-fintechs-como-validar-cpf-em-menos-de-30-segundos)
- [KYC no Brasil: quais setores são obrigados a validar CPF por lei](https://cpfhub.io/blog/kyc-no-brasil-quais-setores-sao-obrigados-a-validar-cpf-por-lei)

---

## Conclusão

Uma dead letter queue garante que nenhuma consulta de CPF seja perdida silenciosamente. Ao capturar falhas, permitir reprocessamento e gerar alertas, você cria uma camada de resiliência que mantém a integridade dos seus processos de validação mesmo diante de falhas temporárias.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece hoje mesmo.

