Transparência: sou fundador da CPFHub, a API usada nos exemplos. Os conceitos (validação local, tratamento de erros, LGPD) valem para qualquer API de consulta de CPF.
Se você tem um formulário de cadastro, um checkout ou um backoffice no Brasil, cedo ou tarde precisa conferir se o CPF digitado existe e bate com o nome informado. Neste tutorial mostro como usar uma API de consulta de CPF em Node.js e Python: validar os dígitos localmente, fazer a consulta, tratar erros e decidir quando vale a pena consultar a Receita Federal em tempo real.
O que uma API de consulta de CPF retorna
Na consulta simples você envia o CPF e recebe os dados cadastrais básicos do titular:
-
nome completo (
nameenameUpper, em maiúsculas, bom para comparar); -
gênero (
gender:"M","F"ounullquando a base não tem a informação); -
data de nascimento (
birthDateno formato DD/MM/AAAA, maisday,montheyearseparados, úteis para calcular idade).
Exemplo de resposta (dados fictícios):
{
"success": true,
"data": {
"cpf": "12345678909",
"name": "Fulano de Tal",
"nameUpper": "FULANO DE TAL",
"gender": "M",
"birthDate": "15/06/1990",
"day": 15,
"month": 6,
"year": 1990
}
}
Passo 1: valide os dígitos antes de chamar a API
Todo CPF tem dois dígitos verificadores calculados pelo módulo 11. Validar isso localmente é gratuito, instantâneo e evita requisições inúteis (CPF digitado errado, tudo repetido como 111.111.111-11 etc.).
Node.js:
function cpfValido(entrada) {
const cpf = String(entrada).replace(/\D/g, "");
if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) return false;
const digito = (base) => {
let soma = 0;
for (let i = 0; i < base.length; i++) {
soma += Number(base[i]) * (base.length + 1 - i);
}
const resto = (soma * 10) % 11;
return resto === 10 ? 0 : resto;
};
const d1 = digito(cpf.slice(0, 9));
const d2 = digito(cpf.slice(0, 10));
return d1 === Number(cpf[9]) && d2 === Number(cpf[10]);
}
Python:
import re
def cpf_valido(entrada: str) -> bool:
cpf = re.sub(r"\D", "", str(entrada))
if len(cpf) != 11 or cpf == cpf[0] * 11:
return False
def digito(base: str) -> int:
soma = sum(int(n) * (len(base) + 1 - i) for i, n in enumerate(base))
resto = (soma * 10) % 11
return 0 if resto == 10 else resto
return digito(cpf[:9]) == int(cpf[9]) and digito(cpf[:10]) == int(cpf[10])
Atenção: dígito válido não significa que o CPF existe ou pertence a quem digitou. Para isso serve a consulta.
Passo 2: consulta simples (GET)
Na CPFHub, a consulta simples é GET https://api.cpfhub.io/cpf/{cpf}, autenticada pelo header x-api-key. O CPF pode ir com ou sem máscara. Custa 1 crédito por CPF encontrado; CPF não encontrado (404) não consome crédito. O tempo típico é de ~150 ms (tempo típico, não SLA), e a documentação recomenda timeout de cerca de 10 s.
Os erros que você deve tratar:
| Status | Significado | O que fazer |
|---|---|---|
| 400 | CPF sem 11 dígitos | Corrigir, não repetir |
| 401 | API Key ausente ou inválida | Conferir o header x-api-key
|
| 403 | Créditos esgotados ou conta bloqueada | Recarregar créditos, não repetir |
| 404 | CPF não encontrado na base | Resultado normal |
| 422 | Dígito verificador inválido | Corrigir, não repetir |
| 429 | Limite por minuto (30/min no padrão) | Esperar o header Retry-After
|
| 500/503 | Falha passageira | Repetir com espera crescente |
Um detalhe: em erros de autenticação o campo error vem como string; nos demais, como objeto com message. Leia os dois formatos.
Node.js (18+, fetch nativo):
const API_KEY = process.env.CPFHUB_API_KEY; // nunca no front-end
async function consultarCpf(cpf) {
if (!cpfValido(cpf)) throw new Error("CPF inválido (dígitos)");
const res = await fetch(`https://api.cpfhub.io/cpf/${cpf.replace(/\D/g, "")}`, {
headers: { "x-api-key": API_KEY },
signal: AbortSignal.timeout(10_000),
});
const body = await res.json().catch(() => ({}));
const msg = typeof body.error === "string" ? body.error : body.error?.message;
if (res.ok) return body.data;
if (res.status === 404) return null; // não encontrado, sem custo
if (res.status === 429) {
throw new Error(`Limite por minuto; tente em ${res.headers.get("retry-after")} s`);
}
if (res.status === 403) throw new Error(`Sem créditos ou conta bloqueada: ${msg}`);
throw new Error(`Erro ${res.status}: ${msg}`);
}
const dados = await consultarCpf("123.456.789-09");
console.log(dados?.name, dados?.gender, dados?.birthDate);
Python (requests):
import os
import requests
API_KEY = os.environ["CPFHUB_API_KEY"]
def consultar_cpf(cpf: str):
if not cpf_valido(cpf):
raise ValueError("CPF inválido (dígitos)")
numero = "".join(c for c in cpf if c.isdigit())
r = requests.get(
f"https://api.cpfhub.io/cpf/{numero}",
headers={"x-api-key": API_KEY},
timeout=10,
)
try:
body = r.json()
except ValueError:
body = {}
err = body.get("error")
msg = err if isinstance(err, str) else (err or {}).get("message")
if r.ok:
return body["data"]
if r.status_code == 404:
return None # não encontrado, sem custo
if r.status_code == 429:
raise RuntimeError(f"Limite por minuto; tente em {r.headers.get('Retry-After')} s")
if r.status_code == 403:
raise RuntimeError(f"Sem créditos ou conta bloqueada: {msg}")
raise RuntimeError(f"Erro {r.status_code}: {msg}")
dados = consultar_cpf("123.456.789-09")
if dados:
print(dados["name"], dados["gender"], dados["birthDate"])
Se o timeout estourar do seu lado, lembre que a consulta pode ter sido concluída e cobrada no servidor; repetir cobra de novo.
Passo 3: consulta em tempo real na Receita Federal (POST)
Quando você precisa da situação cadastral ou de um comprovante, use POST https://api.cpfhub.io/cpf/realtime. Ela consulta a Receita Federal no momento da chamada e exige CPF + data de nascimento (DD/MM/AAAA ou DDMMAAAA). Custa 1,5 crédito por consulta bem-sucedida, em qualquer situação cadastral. O tempo típico é de cerca de 1 segundo (não é SLA), mas a documentação pede timeout de pelo menos 60 s.
A resposta traz name, birthDate, situation (REGULAR, PENDENTE DE REGULARIZAÇÃO, SUSPENSA, CANCELADA, NULA ou TITULAR FALECIDO), deathYear e, quando disponíveis, controlCode, emissionDate, emissionTime, validationUrl (conferência no site da Receita) e validationHtmlUrl (comprovante).
Sobre deathYear: o campo sempre vem, como inteiro ou null. null significa que não há óbito registrado (ou o valor recebido era inválido). Não trate null como prova de que a pessoa está viva. E só REGULAR indica CPF sem pendências.
Node.js:
async function consultarReceita(cpf, dataNascimento) {
const res = await fetch("https://api.cpfhub.io/cpf/realtime", {
method: "POST",
headers: { "x-api-key": API_KEY, "content-type": "application/json" },
body: JSON.stringify({ cpf, birthDate: dataNascimento }),
signal: AbortSignal.timeout(60_000),
});
const body = await res.json().catch(() => ({}));
if (res.ok) return body.data; // { situation, deathYear, controlCode, ... }
// 422: dígito inválido ou a Receita recusou (ex.: data divergente) — sem custo
// 429: 5/min no padrão; 502/503: repetir com espera crescente — sem custo
const msg = typeof body.error === "string" ? body.error : body.error?.message;
throw new Error(`Erro ${res.status}: ${msg}`);
}
Python:
def consultar_receita(cpf: str, data_nascimento: str):
r = requests.post(
"https://api.cpfhub.io/cpf/realtime",
headers={"x-api-key": API_KEY},
json={"cpf": cpf, "birthDate": data_nascimento},
timeout=60,
)
body = r.json() if r.content else {}
if r.ok:
return body["data"]
err = body.get("error")
msg = err if isinstance(err, str) else (err or {}).get("message")
raise RuntimeError(f"Erro {r.status_code}: {msg}")
d = consultar_receita("12345678909", "15/06/1990")
print(d["situation"], d["deathYear"])
Duas cautelas: o POST não é idempotente, então depois de um timeout local confira o saldo em GET /quota antes de repetir; e o link validationHtmlUrl é público (abre nome, CPF e situação), então guarde-o só no back-end e fora dos logs.
Quando usar cada consulta
- Simples (1 crédito): confirmar nome, gênero e idade no cadastro ou checkout. Rápida e barata.
- Tempo real (1,5 crédito): quando você precisa da situação cadastral na Receita ou de um comprovante (crédito, contratos, cadastro regulado).
-
Lote (
POST /cpf/bulk, 1 crédito por CPF encontrado): listas de até 10.000 CPFs, processadas em segundo plano — por exemplo, higienizar uma base antiga.
O plano gratuito inclui 50 créditos por mês, sem cartão, o que dá para testar tudo isso. Detalhes de cada endpoint estão na documentação da CPFHub.
LGPD: consulte com finalidade
CPF e data de nascimento são dados pessoais. Antes de integrar qualquer API de consulta de CPF:
- tenha uma finalidade legítima e uma base legal (ex.: execução de contrato, prevenção a fraude no cadastro);
- não guarde mais do que precisa: se só precisa saber se o nome confere, guarde o resultado da checagem, não a resposta inteira;
- mantenha a API Key e as respostas no back-end, e não registre CPFs completos em logs.
Bônus: consulta de CPF por agentes de IA (MCP)
Se você usa Claude, Cursor ou outro cliente MCP, a CPFHub tem um servidor MCP remoto em https://api.cpfhub.io/mcp (autenticado pelo mesmo x-api-key), com as tools lookup_cpf, lookup_cpf_realtime e get_quota_info. Para clientes sem suporte a HTTP existe o pacote local @cpfhub/mcp (via npx), que hoje só tem lookup_cpf funcionando.
Resumo
- Valide os dígitos localmente.
- Use a consulta simples para nome, gênero e data de nascimento.
- Use a consulta em tempo real quando precisar da situação cadastral na Receita.
- Trate 404 como resultado normal, respeite 429 e não repita POST depois de timeout.
- Colete e guarde só o necessário.
Para testar, crie uma conta gratuita em cpfhub.io e gere sua chave. Dúvidas sobre a consulta CPF via API? Deixe nos comentários.
Top comments (0)