Guias · Por apipj (MASTER BI TECNOLOGIA LTDA) · atualizado em · 5 min de leitura · Código executado contra a API em (Node.js 18.20, Node.js 24.16)

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 conferirCampos da respostaRegra sugerida
1CNPJ bem formadoGET /v1/valida/{cnpj}: valido, erroRecusar na digitação. É aberta e não gasta cota
2Situação cadastralsituacao_cadastral: codigo, descricao, data, motivo02 ATIVA segue. 01 NULA e 08 BAIXADA: bloquear. 03 SUSPENSA e 04 INAPTA: revisão manual, lendo o motivo
3Situação especialsituacao_especial: descricao, dataQualquer valor preenchido vai para revisão, mesmo com a empresa ATIVA
4Idadedata_inicio_atividadeAberta há menos de 180 dias: revisão
5Atividadecnae_principal, cnaes_secundariosAlgum CNAE compatível com o que você vai comprar; se nenhum for, revisão
6Natureza, porte e regimenatureza_juridica, porte, capital_social, simples, meiRegistrar e usar na sua política de crédito e de limite de pedido
7Sócios e quem assinasocios: nome, qualificacao, data_entrada; qualificacao_responsavelQuem assina deve aparecer no quadro, ou apresentar procuração
8Endereço e contatoendereco, telefones, emailBater 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-JUDICIAL e INTERVENCAO vêm em situacao_especial.descricao, com situacao_cadastral ainda 02 ATIVA 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 por INCORPORACAO. O texto vem em situacao_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 (ordem 0001) 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/status e para.
  • mudaram é a lista que importa: fornecedores que passaram de aprovar para revisar ou bloquear desde 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 porMinuto o limite do seu plano (10 no gratuito); se mesmo assim vier um 429, consultar espera o Retry-After e 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.