# Como implementar verificação de CPF com OCR de documento (foto do CPF)

> Aprenda a implementar verificação de CPF via OCR de documento, extraindo o número automaticamente de uma foto e validando com a API.

**Publicado:** 18/11/2025
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-implementar-verificacao-de-cpf-com-ocr-de-documento

---


Para implementar verificação de CPF com OCR, o fluxo combina Tesseract.js — que extrai o número da foto do documento diretamente no navegador — com a API da CPFHub.io, que confirma os dados do titular em tempo real. Nenhuma imagem sai do dispositivo do usuário, o que simplifica a conformidade com a LGPD, e o resultado chega em ~900ms após a extração do CPF.

## Introdução

A extração automática de dados via OCR (Optical Character Recognition) está se tornando padrão em fluxos de onboarding digital. Em vez de pedir ao usuário que digite manualmente os 11 dígitos do CPF, o sistema pode extrair o número diretamente de uma foto do documento -- seja o cartão CPF físico, o RG ou a CNH.

Essa abordagem reduz erros de digitação, acelera o preenchimento e melhora significativamente a experiência do usuário. O exemplo a seguir usa Tesseract.js no frontend e a API da CPFHub.io para validação final dos dados extraídos.

---

## Visão geral do fluxo

O fluxo de verificação com OCR funciona em quatro etapas:

1. **Captura** -- o usuário fotografa ou faz upload do documento.
2. **Pré-processamento** -- a imagem é tratada para melhorar a qualidade do OCR.
3. **Extração** -- o Tesseract.js lê o texto da imagem e um regex identifica o CPF.
4. **Validação** -- o CPF extraído é validado localmente e confirmado via API.

---

## Captura de imagem do documento

O primeiro passo é permitir que o usuário envie uma foto do documento. Em dispositivos móveis, podemos acessar a câmera diretamente.

```html
<div class="upload-area" id="uploadArea">
 <input
 type="file"
 id="inputDocumento"
 accept="image/*"
 capture="environment"
 style="display: none;"
 />
 <div class="upload-placeholder" id="placeholder">
 <p class="upload-titulo">Envie uma foto do seu documento</p>
 <p class="upload-subtitulo">CPF, RG ou CNH</p>
 <button class="btn-upload" onclick="document.getElementById('inputDocumento').click()">
 Tirar foto ou escolher arquivo
 </button>
 </div>
 <img id="previewImg" class="preview" style="display: none;" alt="Preview do documento" />
</div>

<div class="resultado" id="resultado" style="display: none;">
 <p class="status" id="statusOCR"></p>
 <div class="cpf-encontrado" id="cpfEncontrado" style="display: none;">
 <p>CPF detectado:</p>
 <span class="cpf-numero" id="cpfNumero"></span>
 <button class="btn-confirmar" id="btnConfirmar" onclick="validarCPFExtraido()">
 Confirmar e verificar
 </button>
 </div>
</div>

<style>
 .upload-area {
 max-width: 480px;
 margin: 20px auto;
 border: 2px dashed #ccc;
 border-radius: 12px;
 padding: 40px;
 text-align: center;
 transition: border-color 0.2s;
 }
 .upload-area.drag-over { border-color: #3498db; background: #f0f7ff; }
 .upload-titulo { font-size: 1.2rem; font-weight: 600; margin-bottom: 8px; }
 .upload-subtitulo { color: #666; margin-bottom: 16px; }
 .btn-upload {
 padding: 12px 24px;
 background: #3498db;
 color: #fff;
 border: none;
 border-radius: 8px;
 font-size: 1rem;
 cursor: pointer;
 }
 .btn-upload:hover { background: #2980b9; }
 .preview {
 max-width: 100%;
 border-radius: 8px;
 margin-top: 16px;
 }
 .resultado { max-width: 480px; margin: 16px auto; text-align: center; }
 .cpf-numero {
 display: block;
 font-size: 2rem;
 font-weight: 700;
 letter-spacing: 2px;
 margin: 12px 0;
 color: #2c3e50;
 }
 .btn-confirmar {
 padding: 12px 32px;
 background: #2ecc71;
 color: #fff;
 border: none;
 border-radius: 8px;
 font-size: 1rem;
 cursor: pointer;
 }
</style>
```

---

## Pré-processamento da imagem

Antes de enviar a imagem ao OCR, é importante pré-processá-la para melhorar a taxa de reconhecimento. As operações mais eficazes são converter para escala de cinza e aumentar o contraste.

```javascript
function preprocessarImagem(imgElement) {
 const canvas = document.createElement('canvas');
 const ctx = canvas.getContext('2d');

 canvas.width = imgElement.naturalWidth;
 canvas.height = imgElement.naturalHeight;

 // Desenhar imagem original
 ctx.drawImage(imgElement, 0, 0);

 // Converter para escala de cinza com aumento de contraste
 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
 const data = imageData.data;

 for (let i = 0; i < data.length; i += 4) {
 // Escala de cinza ponderada (luminosidade)
 const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;

 // Binarizacao com threshold adaptativo
 const threshold = 128;
 const valor = gray > threshold ? 255 : 0;

 data[i] = valor; // R
 data[i + 1] = valor; // G
 data[i + 2] = valor; // B
 // Alpha permanece inalterado
 }

 ctx.putImageData(imageData, 0, 0);
 return canvas;
}
```

Essa binarização simples funciona bem para documentos impressos com fundo claro. Para documentos mais complexos, considere usar técnicas de threshold adaptativo como o método de Otsu.

---

## Extração de CPF com Tesseract.js

O Tesseract.js é uma biblioteca JavaScript que executa OCR diretamente no navegador, sem necessidade de backend. Vamos usá-lo para extrair texto da imagem e identificar o CPF com regex.

```javascript
// Importar via CDN no HTML:
// <script src="https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js"></script>

async function extrairCPFDaImagem(canvasProcessado) {
 const statusEl = document.getElementById('statusOCR');
 const resultado = document.getElementById('resultado');
 resultado.style.display = 'block';
 statusEl.textContent = 'Analisando documento...';

 try {
 const worker = await Tesseract.createWorker('por', 1, {
 logger: (info) => {
 if (info.status === 'recognizing text') {
 const pct = Math.round(info.progress * 100);
 statusEl.textContent = `Lendo documento... ${pct}%`;
 }
 }
 });

 const { data: { text } } = await worker.recognize(canvasProcessado);
 await worker.terminate();

 // Procurar CPF no texto extraido
 const cpfEncontrado = encontrarCPF(text);

 if (cpfEncontrado) {
 statusEl.textContent = 'CPF encontrado no documento!';
 document.getElementById('cpfEncontrado').style.display = 'block';
 document.getElementById('cpfNumero').textContent = formatarCPF(cpfEncontrado);
 } else {
 statusEl.textContent =
 'Nao foi possivel encontrar um CPF no documento. Tente novamente com melhor iluminacao.';
 }
 } catch (err) {
 statusEl.textContent = 'Erro ao processar imagem. Tente novamente.';
 }
}

function encontrarCPF(texto) {
 // Remover espacos e caracteres especiais que o OCR pode inserir
 const textoLimpo = texto.replace(/[oO]/g, '0').replace(/[lI]/g, '1');

 // Padroes comuns de CPF em documentos
 const padroes = [
 /(\d{3})[.\s]?(\d{3})[.\s]?(\d{3})[-.\s]?(\d{2})/g, // 000.000.000-00
 /(\d{11})/g // 00000000000
 ];

 for (const padrao of padroes) {
 const match = textoLimpo.match(padrao);
 if (match) {
 for (const candidato of match) {
 const digits = candidato.replace(/\D/g, '');
 if (digits.length === 11 && validarDigitosCPF(digits)) {
 return digits;
 }
 }
 }
 }
 return null;
}

function formatarCPF(digits) {
 return `${digits.slice(0,3)}.${digits.slice(3,6)}.${digits.slice(6,9)}-${digits.slice(9)}`;
}

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]);
}
```

---

## Validação via API

Após extrair e validar localmente o CPF, o próximo passo é confirmar os dados com a API da CPFHub.io — uma chamada GET retorna nome, data de nascimento e gênero do titular, permitindo cruzar as informações com o documento apresentado.

```javascript
async function validarCPFExtraido() {
 const cpfNumero = document.getElementById('cpfNumero').textContent;
 const digits = cpfNumero.replace(/\D/g, '');
 const statusEl = document.getElementById('statusOCR');

 statusEl.textContent = 'Verificando CPF na base de dados...';

 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) {
 statusEl.innerHTML = `
 <strong>CPF verificado com sucesso!</strong><br/>
 Nome: ${json.data.name}<br/>
 Nascimento: ${json.data.birthDate}
 `;
 } else {
 statusEl.textContent = 'CPF nao encontrado na base. Verifique o documento.';
 }
 } catch (err) {
 clearTimeout(timeoutId);
 statusEl.textContent = err.name === 'AbortError'
 ? 'Tempo esgotado. Tente novamente.'
 : 'Erro na verificacao. Tente novamente.';
 }
}
```

---

## Integrando captura e processamento

Agora vamos conectar todos os componentes no fluxo de upload.

```javascript
const inputDocumento = document.getElementById('inputDocumento');
const previewImg = document.getElementById('previewImg');
const placeholder = document.getElementById('placeholder');

inputDocumento.addEventListener('change', async function (e) {
 const file = e.target.files[0];
 if (!file) return;

 // Exibir preview
 const reader = new FileReader();
 reader.onload = async function (ev) {
 previewImg.src = ev.target.result;
 previewImg.style.display = 'block';
 placeholder.style.display = 'none';

 // Aguardar imagem carregar para obter dimensoes
 previewImg.onload = async function () {
 const canvasProcessado = preprocessarImagem(previewImg);
 await extrairCPFDaImagem(canvasProcessado);
 };
 };
 reader.readAsDataURL(file);
});

// Suporte a drag and drop
const uploadArea = document.getElementById('uploadArea');

uploadArea.addEventListener('dragover', function (e) {
 e.preventDefault();
 this.classList.add('drag-over');
});

uploadArea.addEventListener('dragleave', function () {
 this.classList.remove('drag-over');
});

uploadArea.addEventListener('drop', function (e) {
 e.preventDefault();
 this.classList.remove('drag-over');
 const file = e.dataTransfer.files[0];
 if (file && file.type.startsWith('image/')) {
 inputDocumento.files = e.dataTransfer.files;
 inputDocumento.dispatchEvent(new Event('change'));
 }
});
```

---

## Dicas para melhorar a taxa de reconhecimento

### Orientações ao usuário

Exiba instruções claras antes da captura:

- "Posicione o documento em uma superfície plana."
- "Evite sombras sobre o documento."
- "Certifique-se de que todos os números estejam visíveis."

### Redimensionamento inteligente

Imagens muito grandes tornam o OCR lento; imagens muito pequenas reduzem a precisão. Redimensione para uma largura máxima de 1600 pixels antes do processamento.

```javascript
function redimensionar(canvas, maxLargura = 1600) {
 if (canvas.width <= maxLargura) return canvas;

 const escala = maxLargura / canvas.width;
 const novoCanvas = document.createElement('canvas');
 novoCanvas.width = maxLargura;
 novoCanvas.height = canvas.height * escala;

 const ctx = novoCanvas.getContext('2d');
 ctx.drawImage(canvas, 0, 0, novoCanvas.width, novoCanvas.height);
 return novoCanvas;
}
```

### Fallback para digitação manual

Se o OCR não conseguir identificar o CPF após duas tentativas, ofereça a opção de digitação manual. Nunca bloqueie o fluxo por causa de uma falha no reconhecimento.

---

## Considerações de privacidade

O processamento OCR com Tesseract.js acontece inteiramente no navegador do usuário -- nenhuma imagem é enviada para servidores externos. Isso é um grande diferencial em termos de privacidade e conformidade com a LGPD.

Porém, é fundamental informar ao usuário que a foto do documento será processada apenas localmente e não será armazenada. Adicione uma nota de privacidade visível próxima à área de upload. A [ANPD](https://www.gov.br/anpd/) recomenda que o titular seja informado de forma clara sobre qualquer tratamento de dados pessoais, incluindo processamento local.

---

## Perguntas frequentes

### O que é necessário para implementar verificação de CPF com OCR?
Você precisa do Tesseract.js para extração no frontend e de uma chave de API da CPFHub.io para validação. O Tesseract.js roda inteiramente no navegador — sem backend — e a chamada à API é uma requisição GET para `https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key`. O resultado chega em ~900ms.

### 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 orienta que dados de identificação devem ser tratados com o princípio da necessidade.

### O que fazer quando o OCR não consegue extrair o CPF do documento?
Ofereça sempre um fallback de digitação manual — nunca bloqueie o fluxo por falha no reconhecimento. Dicas para melhorar a taxa de acerto: pedir ao usuário que reposicione o documento com boa iluminação, redimensionar a imagem para máximo 1600px de largura antes do OCR e tentar mais de uma vez antes de sugerir digitação manual.

### Leia também

- [IA generativa e fraudes de identidade: por que validação de CPF é mais importante que nunca](https://cpfhub.io/blog/ia-generativa-e-fraudes-de-identidade-por-que-validacao-de-cpf-e-mais-importante-que-nunca)
- [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)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)

---

## Conclusão

A verificação de CPF com OCR transforma uma tarefa manual e propensa a erros em um processo automatizado e fluido. Ao combinar Tesseract.js para extração no navegador com a API da CPFHub.io para validação final, você entrega uma experiência de onboarding rápida, precisa e em conformidade com a LGPD.

A API da CPFHub.io complementa o OCR com dados enriquecidos -- nome, data de nascimento e gênero -- permitindo confirmar que o CPF extraído corresponde ao titular do documento.

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

