Telas de erro bem projetadas em fluxos de consulta de CPF orientam o usuário sobre como resolver o problema sem abandonar o processo. Com a API da CPFHub.io, os erros mais comuns são de formato inválido, CPF não encontrado e timeout de rede — cada um merece uma mensagem e uma ação diferentes para manter a experiência fluida.
Introdução
Erros acontecem. O CPF pode ser digitado incorretamente, a conexão pode cair no meio da consulta, a API pode retornar um resultado inesperado. O que diferencia uma boa experiência de uma péssima é como o sistema comunica e trata esses erros.
Telas de erro bem projetadas não apenas informam o problema — elas orientam o usuário sobre como resolver, mantêm a confiança no sistema e evitam o abandono do fluxo.
Taxonomia de erros em consultas de CPF
Antes de projetar as telas, é fundamental entender os diferentes tipos de erro que podem ocorrer. Cada tipo exige uma abordagem visual e textual diferente.
Erros do usuário (4xx)
- CPF com formato inválido -- menos de 11 dígitos ou caracteres não numéricos.
- Dígito verificador incorreto -- o CPF não passa na validação matemática.
- CPF não encontrado -- o número é válido matematicamente, mas não existe na base.
Erros do sistema (5xx)
- Timeout -- a consulta excedeu o tempo limite.
- Erro de rede -- o dispositivo perdeu conexão.
- Erro interno da API -- o servidor retornou um erro inesperado.
Erros de autenticação
- API key inválida -- a chave de autenticação está incorreta ou expirada.
- Cota mensal atingida -- o plano gratuito permite 50 consultas por mês; ao superar esse limite, cada consulta adicional é cobrada a R$0,15 sem interrupção do serviço.
Princípios de design para telas de erro
1. Seja específico, não genérico
Em vez de "Ocorreu um erro", diga exatamente o que aconteceu. "O CPF informado tem apenas 10 dígitos. Verifique se não faltou um número" é infinitamente mais útil.
2. Ofereça uma ação clara
Toda tela de erro deve ter pelo menos um botão de ação: "Tentar novamente", "Corrigir CPF" ou "Falar com suporte". Nunca deixe o usuário em um beco sem saída.
3. Use tom amigável, não técnico
Evite códigos de erro (HTTP 500) ou jargões técnicos. O usuário não precisa saber que houve um timeout na conexão TCP — ele precisa saber que pode tentar novamente em alguns segundos.
4. Diferencie visualmente a severidade
Erros do usuário (corrigíveis) devem ter tom amarelo/laranja. Erros do sistema (não corrigíveis pelo usuário) devem ter tom vermelho, com opção de contato com suporte.
5. Preserve o contexto
Nunca limpe o campo de CPF quando um erro ocorre. O usuário pode precisar apenas corrigir um dígito, e perder todo o preenchimento é frustrante.
Componente de erro reutilizável
Vamos criar um componente de erro flexível que pode ser usado em diferentes cenários.
<div class="erro-container" id="erroContainer" style="display: none;">
<div class="erro-icone" id="erroIcone"></div>
<h3 class="erro-titulo" id="erroTitulo"></h3>
<p class="erro-mensagem" id="erroMensagem"></p>
<div class="erro-acoes" id="erroAcoes"></div>
</div>
<style>
.erro-container {
max-width: 400px;
margin: 20px auto;
padding: 24px;
border-radius: 12px;
text-align: center;
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.erro-container.warning {
background: #fff9e6;
border: 1px solid #f0c800;
}
.erro-container.danger {
background: #fff0f0;
border: 1px solid #e74c3c;
}
.erro-container.info {
background: #f0f7ff;
border: 1px solid #3498db;
}
.erro-icone { font-size: 2.5rem; margin-bottom: 12px; }
.erro-titulo { font-size: 1.1rem; margin-bottom: 8px; color: #2c3e50; }
.erro-mensagem { font-size: 0.95rem; color: #555; margin-bottom: 16px; line-height: 1.5; }
.erro-btn {
padding: 10px 24px;
border: none;
border-radius: 8px;
font-size: 0.95rem;
cursor: pointer;
margin: 0 4px;
}
.erro-btn-primary { background: #3498db; color: #fff; }
.erro-btn-primary:hover { background: #2980b9; }
.erro-btn-secondary { background: #eee; color: #333; }
.erro-btn-secondary:hover { background: #ddd; }
</style>
Exibindo erros específicos
Agora vamos criar uma função que configura o componente de erro de acordo com o tipo de problema.
function exibirErro(tipo, detalhes = {}) {
const container = document.getElementById('erroContainer');
const icone = document.getElementById('erroIcone');
const titulo = document.getElementById('erroTitulo');
const mensagem = document.getElementById('erroMensagem');
const acoes = document.getElementById('erroAcoes');
container.style.display = 'block';
const erros = {
formato_invalido: {
classe: 'warning',
iconeTexto: '!',
titulo: 'CPF incompleto',
mensagem: `O CPF precisa ter 11 digitos. Voce digitou ${detalhes.digitosInformados || '?'}.
Verifique se nao faltou algum numero.`,
acoes: [
{ texto: 'Corrigir', classe: 'erro-btn erro-btn-primary', acao: 'focarInput' }
]
},
digito_invalido: {
classe: 'warning',
iconeTexto: '!',
titulo: 'CPF invalido',
mensagem: 'Os digitos verificadores nao conferem. Verifique se digitou corretamente, ' +
'especialmente os dois ultimos numeros.',
acoes: [
{ texto: 'Corrigir', classe: 'erro-btn erro-btn-primary', acao: 'focarInput' }
]
},
nao_encontrado: {
classe: 'info',
iconeTexto: '?',
titulo: 'CPF nao encontrado',
mensagem: 'O CPF informado e valido, mas nao foi localizado na base de dados. ' +
'Verifique se digitou corretamente.',
acoes: [
{ texto: 'Tentar novamente', classe: 'erro-btn erro-btn-primary', acao: 'focarInput' },
{ texto: 'Preencher manualmente', classe: 'erro-btn erro-btn-secondary', acao: 'modoManual' }
]
},
timeout: {
classe: 'danger',
iconeTexto: '!',
titulo: 'A consulta demorou demais',
mensagem: 'A verificacao excedeu o tempo limite. Isso pode acontecer por instabilidade ' +
'na conexao. Tente novamente em alguns segundos.',
acoes: [
{ texto: 'Tentar novamente', classe: 'erro-btn erro-btn-primary', acao: 'retentar' }
]
},
rede: {
classe: 'danger',
iconeTexto: '!',
titulo: 'Sem conexao',
mensagem: 'Nao foi possivel conectar ao servidor. Verifique sua conexao com a internet ' +
'e tente novamente.',
acoes: [
{ texto: 'Tentar novamente', classe: 'erro-btn erro-btn-primary', acao: 'retentar' }
]
},
cota_atingida: {
classe: 'info',
iconeTexto: 'i',
titulo: 'Cota de consultas atingida',
mensagem: 'O limite mensal do plano atual foi atingido. ' +
'Consultas adicionais continuam funcionando a R$0,15 cada, ou faça upgrade para o plano Pro.',
acoes: [
{ texto: 'Ver planos', classe: 'erro-btn erro-btn-primary', acao: 'verPlanos' },
{ texto: 'Preencher manualmente', classe: 'erro-btn erro-btn-secondary', acao: 'modoManual' }
]
}
};
const config = erros[tipo];
if (!config) return;
container.className = `erro-container ${config.classe}`;
icone.textContent = config.iconeTexto;
titulo.textContent = config.titulo;
mensagem.textContent = config.mensagem;
acoes.innerHTML = '';
config.acoes.forEach(acao => {
const btn = document.createElement('button');
btn.className = acao.classe;
btn.textContent = acao.texto;
btn.onclick = () => executarAcao(acao.acao);
acoes.appendChild(btn);
});
}
function executarAcao(acao) {
const container = document.getElementById('erroContainer');
switch (acao) {
case 'focarInput':
container.style.display = 'none';
document.getElementById('cpf').focus();
break;
case 'retentar':
container.style.display = 'none';
consultarCPF();
break;
case 'modoManual':
container.style.display = 'none';
habilitarPreenchimentoManual();
break;
case 'verPlanos':
window.open('https://www.cpfhub.io/', '_blank');
break;
}
}
Integrando o tratamento de erros na consulta
Veja como utilizar o componente de erro na função de consulta à API.
async function consultarCPF() {
const input = document.getElementById('cpf');
const digits = input.value.replace(/\D/g, '');
// Validacao de formato
if (digits.length < 11) {
exibirErro('formato_invalido', { digitosInformados: digits.length });
return;
}
// Validacao de digitos verificadores
if (!validarDigitosCPF(digits)) {
exibirErro('digito_invalido');
return;
}
// Ocultar erros anteriores
document.getElementById('erroContainer').style.display = 'none';
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const res = await fetch(`https://api.cpfhub.io/cpf/${digits}`, {
headers: {
'x-api-key': 'SUA_CHAVE_DE_API',
'Accept': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeoutId);
const json = await res.json();
if (json.success) {
exibirSucesso(json.data);
} else {
exibirErro('nao_encontrado');
}
} catch (err) {
clearTimeout(timeoutId);
if (err.name === 'AbortError') {
exibirErro('timeout');
} else {
exibirErro('rede');
}
}
}
function validarDigitosCPF(cpf) {
if (/^(\d)\1{10}$/.test(cpf)) return false;
let soma = 0;
for (let i = 0; i < 9; i++) soma += parseInt(cpf[i]) * (10 - i);
let resto = (soma * 10) % 11;
if (resto === 10) resto = 0;
if (resto !== parseInt(cpf[9])) return false;
soma = 0;
for (let i = 0; i < 10; i++) soma += parseInt(cpf[i]) * (11 - i);
resto = (soma * 10) % 11;
if (resto === 10) resto = 0;
return resto === parseInt(cpf[10]);
}
Retry automático com backoff exponencial
Para erros de rede e timeout, implementar retry automático com backoff exponencial melhora a experiência sem exigir ação do usuário.
async function consultarComRetry(digits, tentativas = 3) {
for (let i = 0; i < tentativas; i++) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const res = await fetch(`https://api.cpfhub.io/cpf/${digits}`, {
headers: {
'x-api-key': 'SUA_CHAVE_DE_API',
'Accept': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeoutId);
return await res.json();
} catch (err) {
clearTimeout(timeoutId);
if (i < tentativas - 1) {
const delay = Math.pow(2, i) * 500; // 500ms, 1000ms, 2000ms
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw err;
}
}
}
}
Telas de erro acessíveis
A acessibilidade nas telas de erro é frequentemente negligenciada. A OWASP e as diretrizes WCAG recomendam que mensagens de erro sejam perceptíveis por todos os usuários, incluindo os que utilizam tecnologias assistivas. Alguns pontos essenciais:
ARIA roles e live regions
<div
class="erro-container"
role="alert"
aria-live="assertive"
aria-atomic="true"
>
<!-- Conteudo do erro -->
</div>
O atributo role="alert" garante que leitores de tela anunciem o erro imediatamente. O aria-live="assertive" interrompe qualquer leitura em andamento para comunicar o erro.
Foco programático
Quando um erro aparece, mova o foco para o container de erro para que o usuário de teclado saiba que algo mudou.
function exibirErro(tipo, detalhes) {
// ... configurar erro ...
container.style.display = 'block';
container.setAttribute('tabindex', '-1');
container.focus();
}
Contraste de cores
Não dependa apenas de cor para comunicar o tipo de erro. Use ícones, títulos descritivos e bordas como complemento visual, garantindo que usuários com daltonismo identifiquem o problema.
Monitoramento de erros
Além de exibir erros para o usuário, é fundamental monitorá-los no backend para identificar padrões e melhorar o sistema.
function reportarErro(tipo, detalhes) {
// Enviar para sistema de analytics ou monitoramento
if (typeof navigator.sendBeacon === 'function') {
navigator.sendBeacon('/api/analytics/errors', JSON.stringify({
tipo,
detalhes,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href
}));
}
}
Monitore métricas como taxa de erro por tipo, tempo médio até a resolução e taxa de abandono após erro para priorizar melhorias.
Perguntas frequentes
Quais são os erros mais comuns em consultas de CPF via API?
Os mais frequentes são formato inválido (o usuário digitou menos de 11 dígitos), dígito verificador incorreto (número válido matematicamente mas digitado errado) e timeout de rede. CPF não encontrado é raro quando a validação matemática é feita antes da chamada à API.
A API da CPFHub.io retorna HTTP 429 quando a cota é atingida?
Não. A CPFHub.io nunca retorna HTTP 429 nem bloqueia o serviço. Ao superar o limite do plano gratuito (50 consultas/mês), cada consulta adicional é cobrada a R$0,15 e a API continua respondendo normalmente. Não é necessário criar um estado de erro específico para esse caso — apenas informar o usuário sobre o custo adicional.
Como preservar o CPF digitado quando um erro ocorre?
Nunca limpe o campo de CPF ao exibir a mensagem de erro. Use apenas a função de foco (focus()) para levar o cursor de volta ao input, permitindo que o usuário corrija apenas o dígito errado sem redigitar tudo.
O que fazer quando a API retorna success: false?
Exiba uma mensagem informativa indicando que o CPF é válido matematicamente mas não foi encontrado na base, e ofereça a opção de preenchimento manual do formulário. Esse estado é diferente de um erro técnico e deve ter um tom de informação (azul/info), não de alerta (vermelho/danger).
Conclusão
Telas de erro bem projetadas são tão importantes quanto telas de sucesso. Elas determinam se o usuário vai persistir no fluxo ou abandonar. Ao ser específico sobre o problema, oferecer ações claras, usar tom amigável e preservar o contexto, você transforma momentos de frustração em oportunidades de confiança.
A API da CPFHub.io foi projetada para simplificar o tratamento de erros: respostas JSON padronizadas, latência de aproximadamente 900ms e comportamento previsível em qualquer volume de consultas — sem bloqueios inesperados.
Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e comece hoje mesmo.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
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.



