Como validar fornecedor pelo CNPJ: checklist, regras e código
Antes de cadastrar um fornecedor ou pagar a primeira nota, vale confirmar que o CNPJ existe, está ativo e é de quem você acha que está contratando. Os dados cadastrais públicos da Receita Federal respondem boa parte disso numa consulta só. Este guia liga cada item do checklist a um campo da resposta completa de GET /v1/cnpj/{cnpj}, traz as regras em código e mostra o que fica de fora.
Checklist do fornecedor
| # | O que conferir | Campos da resposta | Regra sugerida |
|---|---|---|---|
| 1 | CNPJ bem formado | GET /v1/valida/{cnpj}: valido, erro | Recusar na digitação. É aberta e não gasta cota |
| 2 | Situação cadastral | situacao_cadastral: codigo, descricao, data, motivo | 02 ATIVA segue. 01 NULA e 08 BAIXADA: bloquear. 03 SUSPENSA e 04 INAPTA: revisão manual, lendo o motivo |
| 3 | Situação especial | situacao_especial: descricao, data | Qualquer valor preenchido vai para revisão, mesmo com a empresa ATIVA |
| 4 | Idade | data_inicio_atividade | Aberta há menos de 180 dias: revisão |
| 5 | Atividade | cnae_principal, cnaes_secundarios | Algum CNAE compatível com o que você vai comprar; se nenhum for, revisão |
| 6 | Natureza, porte e regime | natureza_juridica, porte, capital_social, simples, mei | Registrar e usar na sua política de crédito e de limite de pedido |
| 7 | Sócios e quem assina | socios: nome, qualificacao, data_entrada; qualificacao_responsavel | Quem assina deve aparecer no quadro, ou apresentar procuração |
| 8 | Endereço e contato | endereco, telefones, email | Bater com o contrato, a nota e o cadastro bancário |
Os detalhes que mais pegam:
- A situação especial aparece em empresa ativa. Valores como
RECUPERACAO JUDICIAL,FALIDO,EM LIQUIDACAO,LIQUIDACAO EXTRA-JUDICIALeINTERVENCAOvêm emsituacao_especial.descricao, comsituacao_cadastralainda02ATIVA em parte dos casos. Quem olha só a situação cadastral deixa passar. - O motivo explica a situação. Uma INAPTA por
OMISSAO DE DECLARACOESé um caso diferente de uma BAIXADA porINCORPORACAO. O texto vem emsituacao_cadastral.motivo.descricao, exatamente como a Receita publica. - A idade é do estabelecimento.
data_inicio_atividadeé a abertura daquele CNPJ: uma filial nova de uma empresa antiga também cai na regra dos 180 dias. Nesse caso, consulte a matriz (ordem0001) e compare. - Empresário individual e MEI costumam não ter quadro de sócios. O nome do titular está na própria
razao_social. - O CPF do sócio vem mascarado (no formato
***123456**), como na base pública. Para comparar uma pessoa, use o nome e a qualificação.
Código: triagem automática
O módulo abaixo, para Node.js 18 ou mais novo e sem dependências, consulta o CNPJ e devolve aprovar, revisar ou bloquear com os motivos. As regras são uma sugestão de política, não uma regra legal: ajuste prazos e listas ao que a sua empresa decidir.
import { readFile, writeFile } from 'node:fs/promises';
const BASE = 'https://apipj.com.br/v1';
const DIA = 24 * 60 * 60 * 1000;
const espera = (ms) => new Promise((ok) => setTimeout(ok, ms));
// Consulta um CNPJ; null se não estiver na carga do mês. No limite por minuto, espera e repete.
export async function consultar(cnpj) {
for (;;) {
const r = await fetch(`${BASE}/cnpj/${encodeURIComponent(cnpj)}`, {
headers: { Authorization: `Bearer ${process.env.APIPJ_KEY}` },
});
if (r.status === 404) return null;
if (r.status === 429) {
const erro = await r.json();
if (erro.code === 'quota_exceeded') throw new Error('Cota do mês esgotada');
await espera(Number(r.headers.get('retry-after') || 60) * 1000);
continue;
}
if (!r.ok) throw new Error(`apipj: HTTP ${r.status}`);
return r.json();
}
}
const normalizar = (s) => String(s || '').normalize('NFD').replace(/[̀-ͯ]/g, '').toUpperCase().replace(/\s+/g, ' ').trim();
export function avaliarFornecedor(e, { cnaesAceitos = [], diasMinimos = 180, assinante = null, hoje = new Date() } = {}) {
const bloquear = [];
const revisar = [];
const sit = e.situacao_cadastral;
const motivo = sit.motivo.codigo && sit.motivo.codigo !== '00' ? ` (${sit.motivo.descricao})` : '';
if (sit.codigo === '01' || sit.codigo === '08') bloquear.push(`situação ${sit.descricao} desde ${sit.data}${motivo}`);
else if (sit.codigo !== '02') revisar.push(`situação ${sit.descricao} desde ${sit.data}${motivo}`);
if (e.situacao_especial.descricao) {
revisar.push(`situação especial ${e.situacao_especial.descricao} desde ${e.situacao_especial.data}`);
}
const dias = Math.floor((hoje - Date.parse(e.data_inicio_atividade)) / DIA);
if (!(dias >= diasMinimos)) revisar.push(Number.isNaN(dias) ? 'sem data de início de atividade' : `aberta há ${dias} dias`);
const cnaes = [e.cnae_principal, ...e.cnaes_secundarios].map((c) => c.codigo).filter(Boolean);
if (cnaesAceitos.length && !cnaes.some((c) => cnaesAceitos.some((p) => c.startsWith(p)))) {
revisar.push(`nenhum CNAE começa com ${cnaesAceitos.join(' ou ')}`);
}
if (assinante) {
const nome = normalizar(assinante);
const noQuadro = e.socios.some((s) => normalizar(s.nome) === nome);
if (!noQuadro && !normalizar(e.razao_social).includes(nome)) revisar.push('quem assina não está no quadro de sócios');
}
const decisao = bloquear.length ? 'bloquear' : revisar.length ? 'revisar' : 'aprovar';
return { decisao, motivos: [...bloquear, ...revisar] };
}Usando, para um fornecedor de software (CNAE começando com 62 ou 63):
import { consultar, avaliarFornecedor } from './fornecedor.mjs';
for (const cnpj of ['11378117000120', '00000000E08G12']) {
const empresa = await consultar(cnpj);
console.log(cnpj, empresa ? avaliarFornecedor(empresa, { cnaesAceitos: ['62', '63'] }) : 'fora da carga do mês');
}Saída real, em 10/10/2026:
11378117000120 { decisao: 'aprovar', motivos: [] }
00000000E08G12 {
decisao: 'revisar',
motivos: [ 'aberta há 71 dias', 'nenhum CNAE começa com 62 ou 63' ]
}O segundo CNPJ é o primeiro alfanumérico emitido pela Receita, uma filial de banco aberta em 31/07/2026: cai na regra da idade e na da atividade, como deveria para quem compra software. Para incluir a checagem de quem assina, passe assinante: 'Nome Completo'.
Reconsulta mensal da carteira
Os dados abertos do CNPJ mudam uma vez por mês, então reconsultar a carteira todo dia só gasta cota. O caminho econômico é perguntar a GET /v1/status, que é aberto e não consome cota, qual é a carga em uso, e reconsultar todos os fornecedores só quando ela mudar. Acrescente ao fornecedor.mjs:
// Reconsulta a carteira só quando a API já está numa carga nova; guarda a última decisão de cada CNPJ.
export async function reconsultarCarteira(cnpjs, { arquivo = 'carteira.json', porMinuto = 10, ...regras } = {}) {
const { dataset_month: mes } = await (await fetch(`${BASE}/status`)).json();
const estado = JSON.parse(await readFile(arquivo, 'utf8').catch(() => '{"fornecedores":{}}'));
if (estado.mes === mes) return { mes, reconsultados: 0, mudaram: [] };
const mudaram = [];
for (const cnpj of cnpjs) {
const empresa = await consultar(cnpj);
const atual = empresa ? avaliarFornecedor(empresa, regras) : { decisao: 'revisar', motivos: ['fora da carga do mês'] };
const antes = estado.fornecedores[cnpj];
if (antes && antes.decisao !== atual.decisao) mudaram.push({ cnpj, de: antes.decisao, ...atual });
estado.fornecedores[cnpj] = atual;
await espera(60000 / porMinuto); // respeita o limite por minuto do plano
}
await writeFile(arquivo, JSON.stringify({ ...estado, mes }, null, 2));
return { mes, reconsultados: cnpjs.length, mudaram };
}- Agende para rodar todo dia (cron, agendador de tarefas, um job do seu sistema). Ela só consulta quando a carga muda; nos outros dias, faz uma chamada aberta a
/v1/statuse para. mudaramé a lista que importa: fornecedores que passaram deaprovarpararevisaroubloqueardesde a carga anterior, com os motivos.- Custo: uma consulta por fornecedor por mês, inclusive os que voltam 404. O plano gratuito tem 50.000 consultas por mês.
- Tempo: o tamanho da carteira dividido pelo limite por minuto do plano. Use em
porMinutoo limite do seu plano (10 no gratuito); se mesmo assim vier um429,consultarespera oRetry-Aftere repete.
O que a API não cobre
A consulta de CNPJ mostra o cadastro público da empresa. Ela não traz:
- Certidão de débitos. A regularidade fiscal federal se comprova com a certidão emitida pela Receita Federal e pela PGFN. Estados e municípios têm as certidões deles.
- Protestos. Ficam nos cartórios de protesto, não na base do CNPJ.
- Processos judiciais. Ficam nos tribunais. A situação especial registra casos como falência, recuperação judicial, liquidação e intervenção, não processos em geral.
- Inscrição estadual. Vem do cadastro de cada estado.
- Mudanças de hoje. A base é a carga mensal dos dados abertos; para prova formal da situação cadastral, use o comprovante emitido no site da Receita, como explica a API de CNPJ.
Para fornecedores críticos, a consulta pelo CNPJ é o primeiro filtro, não a análise inteira.
Perguntas frequentes
A consulta conta como validação de fornecedor para auditoria?
Ela registra o que a Receita publicava na carga do mês (o campo atualizado_em diz qual). Guarde a resposta e a data junto do cadastro. Se a sua auditoria exige documento oficial, emita o comprovante e as certidões nos sites dos órgãos.
Dá para validar o regime tributário do fornecedor?
Dá para ver a opção pelo Simples Nacional e pelo MEI e as datas. O guia da API do Simples Nacional e MEI mostra como ler esses campos e quando conferir no portal oficial.
E se o fornecedor tiver CNPJ alfanumérico?
Funciona igual: a consulta aceita os dois formatos, e o exemplo acima já usa um. Confira se o seu cadastro aceita letras no campo de CNPJ; o guia do CNPJ alfanumérico tem o checklist.