Validação de CPF com Deno: consumo nativo da API SmartCPF via fetch
Como validar CPFs em Deno usando fetch nativo para consumir a API SmartCPF v2 com autenticação X-API-KEY.

Validar CPFs em aplicações backend é uma necessidade recorrente em sistemas brasileiros — onboarding, KYC, prevenção a fraude, conformidade. A API SmartCPF resolve isso devolvendo não apenas a validade do dígito verificador, mas dados cadastrais completos (nome, data de nascimento, situação cadastral, e-mails, telefones, endereços) com uma única chamada HTTP. Este artigo mostra como consumir o endpoint GET /v2/individuals/{cpf} diretamente com o fetch nativo do Deno, sem dependências externas, mantendo o runtime leve e a superfície de ataque mínima.
O Deno inclui fetch global compatível com o padrão Web Fetch API, o que elimina a necessidade de instalar axios, node-fetch ou undici. A autenticação na SmartCPF é feita exclusivamente via header X-API-KEY — nada de Authorization: Bearer, query string ou cookies. O exemplo abaixo cobre montagem da URL com o parâmetro package, envio do header obrigatório, tratamento de códigos de erro HTTP (401, 403, 404, 422, 5xx) e parsing tipado da resposta JSON.
Conceito central: fetch nativo + tipagem estrita
Deno roda TypeScript out-of-the-box e expõe fetch como API global, igual ao navegador. Isso significa que você escreve código que roda tanto no edge quanto no servidor sem polyfills. A tipagem da resposta da SmartCPF permite validar o contrato em tempo de compilação e evitar any espalhado pelo código. O endpoint aceita o parâmetro package (ex: package=complete) para controlar o nível de detalhe do retorno — o que evita over-fetching e economiza créditos.
Vantagens de usar fetch nativo no Deno
1. Zero dependências externas
Não há package.json, node_modules ou lockfile. O binário único do Deno já traz tudo o que você precisa para requisições HTTP/HTTPS com TLS moderno.
2. Tipagem TypeScript nativa
Interfaces como SmartCPFResponse e SmartCPFMeta são verificadas em tempo de compilação (deno check), reduzindo bugs de runtime causados por mudanças no contrato da API.
3. Compatível com Web Standards O mesmo código roda em Cloudflare Workers, Deno Deploy, Vercel Edge Functions e browsers — portabilidade real sem adaptações.
4. Controle fino de timeout e redirectfetch aceita signal (AbortController) para timeouts customizados e redirect: 'manual' para lidar com 3xx sem seguir cegamente — útil em ambientes com proxy corporativo.
5. Streaming de resposta para payloads grandesresponse.body é um ReadableStream; você pode processar JSON incrementalmente com TextDecoderStream se o pacote complete trouxer muitos registros.
6. Segurança por padrão
Deno exige flags explícitas (--allow-net=api.smartcpf.com.br) para acesso de rede. Isso impede vazamento acidental de credenciais em scripts maliciosos ou dependências comprometidas.
Desafios do fetch nativo no Deno
1. Sem retry automático
Diferente de axios-retry ou ky, o fetch nativo não tem retry built-in. Você precisa implementar backoff exponencial manualmente se a API retornar 429 ou 5xx transitório.
2. Tratamento de erro HTTP não é exceçãofetch só rejeita em falha de rede (DNS, TLS, conexão recusada). Códigos 4xx/5xx resolvem a Promise com ok: false. Esquecer de checar response.ok é a causa mais comum de bugs silenciosos.
3. JSON parsing falha silenciosamente em corpo vazioawait response.json() lança SyntaxError se o corpo estiver vazio ou não for JSON válido (ex: HTML de página de erro do WAF). Sempre valide content-type antes de parsear.
4. Ausência de interceptors globais
Não há equivalente a axios.interceptors.request.use para injetar X-API-KEY automaticamente em todas as chamadas. O padrão recomendado é uma wrapper function ou classe client tipada.
5. Certificados customizados exigem flag CLI
Em ambientes com CA corporativa, você precisa passar --ca-file ou --unsafely-ignore-certificate-errors na invocação do Deno — não dá para configurar via código.
Como implementar com a SmartCPF
Abaixo, um client mínimo tipado que encapsula a chamada, injeta o header X-API-KEY, trata erros HTTP e retorna um discriminated union (Success | Error) para forçar o consumidor a lidar com os dois casos. O parâmetro package é opcional e defaulta a complete.
// smartcpf-client.ts
// Client tipado para consumir GET /v2/individuals/{cpf} da SmartCPF
// Requer flag --allow-net=api.smartcpf.com.br ao rodar com Deno
// ============================================
// Tipos da resposta (subconjunto relevante)
// ============================================
interface SmartCPFMeta {
searchType: string;
consultId: string;
user: string;
message: string;
ip: string;
success: boolean;
apiVersion: string;
date: string; // ISO 8601
executionTimeMs: number;
balance: number;
}
interface Phone {
number: string;
blockedTelemarketing: boolean;
operator: string;
type: string;
whatsapp: boolean;
}
interface SmartCPFResult {
cpf: string;
name: string;
birthDate: string; // DD/MM/YYYY
age: number;
motherName: string;
gender: "M" | "F";
status: string;
emails: string[];
phones: Phone[];
addresses: unknown[]; // estrutura omitida para brevidade
}
interface SmartCPFSuccessResponse {
meta: SmartCPFMeta;
result: SmartCPFResult;
success: true;
errors: never[];
}
interface SmartCPFErrorResponse {
meta: SmartCPFMeta;
result: null;
success: false;
errors: string[];
}
// Union discriminada pelo campo `success`
type SmartCPFResponse = SmartCPFSuccessResponse | SmartCPFErrorResponse;
// ============================================
// Erros de domínio tipados
// ============================================
class SmartCPFHttpError extends Error {
constructor(
public readonly status: number,
public readonly body: unknown,
message: string
) {
super(message);
this.name = "SmartCPFHttpError";
}
}
class SmartCPFNetworkError extends Error {
constructor(public readonly cause: Error) {
super(`Falha de rede ao chamar SmartCPF: ${cause.message}`);
this.name = "SmartCPFNetworkError";
}
}
// ============================================
// Client
// ============================================
const BASE_URL = "https://api.smartcpf.com.br/v2";
interface ClientOptions {
apiKey: string;
/** Timeout total em ms (default: 10_000) */
timeoutMs?: number;
/** Pacote de dados: 'basic' | 'complete' | 'custom' (default: 'complete') */
defaultPackage?: "basic" | "complete" | "custom";
}
export function createSmartCPFClient(options: ClientOptions) {
const { apiKey, timeoutMs = 10_000, defaultPackage = "complete" } = options;
const headers = new Headers({
"X-API-KEY": apiKey,
"Accept": "application/json",
"User-Agent": `smartcpf-deno-client/1.0 (deno/${Deno.version.deno})`,
});
async function fetchWithTimeout(
url: string,
init: RequestInit,
timeout: number
): Promise<Response> {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
try {
return await fetch(url, { ...init, signal: controller.signal });
} finally {
clearTimeout(id);
}
}
return {
/**
* Consulta um CPF na SmartCPF.
* @param cpf CPF apenas dígitos (ex: '11111111111') ou formatado
* @param pkg Override do pacote para esta chamada
*/
async consultarCPF(
cpf: string,
pkg: "basic" | "complete" | "custom" = defaultPackage
): Promise<SmartCPFResponse> {
// Normaliza CPF: remove pontuação
const cleanCpf = cpf.replace(/\D/g, "");
if (cleanCpf.length !== 11) {
throw new Error(`CPF inválido: deve conter 11 dígitos (recebido: ${cleanCpf.length})`);
}
const url = `${BASE_URL}/individuals/${cleanCpf}?package=${pkg}`;
let response: Response;
try {
response = await fetchWithTimeout(url, { method: "GET", headers }, timeoutMs);
} catch (err) {
if (err instanceof DOMException && err.name === "AbortError") {
throw new SmartCPFNetworkError(new Error(`Timeout após ${timeoutMs}ms`));
}
throw new SmartCPFNetworkError(err as Error);
}
// Content-Type guard: evita parsear HTML de WAF/página de erro
const contentType = response.headers.get("content-type") ?? "";
if (!contentType.includes("application/json")) {
const text = await response.text().catch(() => "");
throw new SmartCPFHttpError(
response.status,
text,
`Resposta não-JSON (content-type: ${contentType || "ausente"})`
);
}
let data: SmartCPFResponse;
try {
data = (await response.json()) as SmartCPFResponse;
} catch (err) {
throw new SmartCPFHttpError(
response.status,
null,
`Falha ao parsear JSON: ${(err as Error).message}`
);
}
// SmartCPF retorna success: false no body para 404/422/etc, mas HTTP 200
// Também pode vir HTTP 401/403/500 com body de erro
if (!response.ok || data.success === false) {
const msg = data.errors?.join("; ") ?? data.meta?.message ?? "Erro desconhecido";
throw new SmartCPFHttpError(response.status, data, msg);
}
return data;
},
/** Validação rápida de dígito verificador (algoritmo local, sem rede) */
validarDigitos(cpf: string): boolean {
const nums = cpf.replace(/\D/g, "");
if (nums.length !== 11 || /^(\d)\1{10}$/.test(nums)) return false;
const calc = (base: number) =>
nums
.slice(0, base)
.split("")
.reduce((sum, d, i) => sum + Number(d) * (base + 1 - i), 0) % 11;
const d1 = calc(9) < 2 ? 0 : 11 - calc(9);
const d2 = calc(10) < 2 ? 0 : 11 - calc(10);
return d1 === Number(nums[9]) && d2 === Number(nums[10]);
},
};
}
// ============================================
// Exemplo de uso (rode com: deno run --allow-net=api.smartcpf.com.br --allow-env smartcpf-client.ts)
// ============================================
if (import.meta.main) {
const apiKey = Deno.env.get("SMARTCPf_API_KEY");
if (!apiKey) {
console.error("Defina a variável de ambiente SMARTCPf_API_KEY");
Deno.exit(1);
}
const client = createSmartCPFClient({ apiKey });
// Exemplo: CPF de teste (formato livre)
const cpfTeste = "111.111.111-11";
// Validação local rápida antes de gastar crédito
if (!client.validarDigitos(cpfTeste)) {
console.log("CPF inválido (dígito verificador) — não consultando API");
Deno.exit(0);
}
try {
const resposta = await client.consultarCPF(cpfTeste, "complete");
// TypeScript estreita o tipo para SmartCPFSuccessResponse aqui
console.log("=== Consulta realizada ===");
console.log(`ConsultID: ${resposta.meta.consultId}`);
console.log(`Saldo restante: ${resposta.meta.balance} créditos`);
console.log(`Tempo de execução: ${resposta.meta.executionTimeMs} ms`);
console.log(`Nome: ${resposta.result.name}`);
console.log(`CPF: ${resposta.result.cpf}`);
console.log(`Status: ${resposta.result.status}`);
console.log(`E-mails: ${resposta.result.emails.join(", ") || "—"}`);
console.log(
`Telefones: ${resposta.result.phones.map((p) => p.number).join(", ") || "—"}`
);
} catch (err) {
if (err instanceof SmartCPFHttpError) {
console.error(`Erro HTTP ${err.status}: ${err.message}`);
// Exemplo: err.status === 401 -> chave inválida/expirada
// err.status === 403 -> pacote não permitido para esta chave
// err.status === 404 -> CPF não encontrado na base
// err.status === 422 -> CPF malformado
// err.status === 429 -> rate limit (implemente retry com backoff)
} else if (err instanceof SmartCPFNetworkError) {
console.error(`Erro de rede: ${err.message}`);
// Exemplo: DNS, TLS, timeout, conexão recusada
} else {
console.error(`Erro inesperado: ${(err as Error).message}`);
}
Deno.exit(1);
}
}
Resposta da API
{
"meta": {
"searchType": "CPF G",
"consultId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user": "Fulano de Tal",
"message": "CPF encontrado com sucesso",
"ip": "127.0.0.1",
"success": true,
"apiVersion": "v2",
"date": "2026-08-17T14:32:10.123Z",
"executionTimeMs": 74.1,
"balance": 9.77
},
"result": {
"cpf": "111.111.111-11",
"name": "João da Silva",
"birthDate": "10/09/1992",
"age": 33,
"motherName": "Maria da Silva",
"gender": "M",
"status": "Regular",
"emails": ["[email protected]"],
"phones": [
{
"number": "(55) 99999-9999",
"blockedTelemarketing": false,
"operator": "VIVO",
"type": "CELULAR",
"whatsapp": false
}
],
"addresses": []
},
"success": true,
"errors": []
}
O fluxo demonstrado resolve na prática:
- Validação local de dígito (
validarDigitos) evita gastar crédito em CPFs obviamente inválidos antes de chamar a rede. - Header
X-API-KEYinjetado automaticamente pelo client elimina erro humano de esquecer autenticação ou usar método errado. consultIdnometarastreia cada consulta nos logs da SmartCPF — essencial para auditoria e suporte.balancedevolvido em tempo real permite implementar alerta de saldo baixo (ex:< 5créditos) no seu painel administrativo.executionTimeMsexpõe latência real da API para SLOs internos e monitoramento de performance.- Union discriminada (
success: true | false) força o TypeScript a exigir tratamento dos dois ramos, eliminando acessos aresultnulo em caso de erro.
Por que usar a SmartCPF
- Latência previsível: média sub-100 ms no endpoint
completecom infra no Brasil, adequado para fluxos síncronos de onboarding. - Integração sem SDK: REST puro sobre HTTPS, compatível com qualquer runtime (Deno, Node, Go, Python, edge functions) — zero vendor lock-in.
- Modelo de créditos transparente:
balanceno response e dashboard com extrato detalhado; você paga apenas pelo que consulta, sem mensalidade mínima. - Escalabilidade elástica: a API absorve picos de Black Friday ou campanhas de marketing sem necessidade de provisionar capacidade antecipada.
- Dados enriquecidos em uma chamada: além do status cadastral, retorna e-mails, telefones com operadora/tipo/WhatsApp e endereços — evita múltiplas integrações com bureaus distintos.
Conclusão
Consumir a SmartCPF com o fetch nativo do Deno entrega uma solução enxuta, tipada e portável para validação e enriquecimento de CPF. O client mostrado aqui cobre autenticação correta (X-API-KEY), timeout configurável, validação local de dígito, parsing seguro de JSON e tratamento estruturado de erros HTTP e de domínio — tudo em menos de 150 linhas sem dependências externas. Adapte o defaultPackage e o timeout ao seu SLA, implemente retry com backoff exponencial para 429/5xx se precisar de resiliência extra, e você tem uma integração pronta para produção. Crie sua conta na SmartCPF e faça sua primeira consulta em minutos.
Sobre a redação
Redação Smart CPF
Autor do blog
Mais artigos

Locadoras de veículos: validando CPF de locatários antes da retirada
Como integrar validação de CPF no fluxo de locação para reduzir fraudes, agilizar check-in e garantir conformidade legal.

Validação de CPF em Go: exemplo prático com net/http
Aprenda a validar e consultar CPF em Go usando a API SmartCPF v2 com net/http padrão, autenticação X-API-KEY e tratamento de erros robusto.

Integrando a SmartCPF em runtime Bun: guia rápido
Como consumir a API SmartCPF v2 com Bun nativo: autenticação X-API-KEY, tratamento de erro e parsing de resposta.

Como validar CPF com Node.js e a API SmartCPF
Tutorial prático de validação de CPF em Node.js usando a API SmartCPF v2 com autenticação X-API-KEY.
Pronto para validar CPFs em segundos?
Consultas gratuitas para testar agora. Acesso imediato à documentação.