API de CNPJ para enriquecer CRM
Complete contas B2B a partir do CNPJ: cadastro, situação, CNAE, porte e, quando disponíveis, contatos, site, sócios sem CPF e faixa de faturamento.
Um CRM com CNPJ na conta pode ter cadastro completo e, quando disponíveis, telefone, e-mail, site, sócios e faixa de faturamento, com uma ou duas chamadas por registro. O perfil basic (1 crédito) diz se a empresa está ativa e se há contato para buscar; o full (6 créditos) traz o contato. Cada registro sai com a data da base da Receita Federal, hoje agosto/2026.
O fluxo em três passos
-
Peça o basic primeiro
O perfil basic traz cadastro, situação, CNAE, porte e os sinais has_phone, has_email e has_website. Já dá para segmentar e descartar contas inativas.
GET /v1/cnpjs/{cnpj} → data.situacao_cadastral, data.porte, data.has_phone -
Peça o full só onde há contato
Quando has_phone, has_email ou has_website vierem verdadeiros, a consulta full traz os contatos disponíveis, sócios e faixa de faturamento; com meta.suppressed verdadeiro, os contatos vêm omitidos. Se toda conta precisa de sócios ou faixa, peça o full sempre: o basic não tem sinal para eles.
GET /v1/cnpjs/{cnpj}?profile=full → data.telefones, data.email, data.faixa_faturamento -
Grave a data da base junto
meta.data_as_of diz qual foto mensal respondeu. É o campo que decide quando reenriquecer.
conta.enriquecido_em = meta.data_as_of
Código pronto
A lógica é a mesma em qualquer stack: basic para todos, full só onde há contato, e meta.suppressed testado antes de ler os contatos.
Python
import os, requests
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['CNPJIA_KEY']}"
API = "https://api.cnpj.ia.br/v1/cnpjs/"
def enriquecer(cnpj: str) -> dict:
r = S.get(API + cnpj, timeout=10)
r.raise_for_status() # erro vem no envelope Error, sem data
basic = r.json()
d = basic["data"]
registro = {
"razao_social": d["razao_social"],
"situacao": d["situacao_cadastral"]["descricao"],
"porte": d["porte"]["descricao"],
"cnae": d["cnae_fiscal"]["descricao"],
"base": basic["meta"]["data_as_of"],
}
if d["has_phone"] or d["has_email"] or d["has_website"]:
r = S.get(API + cnpj, params={"profile": "full"}, timeout=10)
r.raise_for_status()
resp = r.json()
full, meta = resp["data"], resp["meta"]
if not meta.get("suppressed"): # pedido de remoção ativo omite os contatos
registro.update(
telefones=[t["ddd"] + t["numero"] for t in full.get("telefones") or []],
email=full.get("email"),
site=full.get("site"),
)
fx = full.get("faixa_faturamento")
registro["faixa_faturamento"] = fx["faixa"] if fx else None
registro["suprimido"] = bool(meta.get("suppressed"))
return registro
Node
const headers = { Authorization: `Bearer ${process.env.CNPJIA_KEY}` };
const API = "https://api.cnpj.ia.br/v1/cnpjs/";
async function enriquecer(cnpj) {
const r1 = await fetch(API + cnpj, { headers });
if (!r1.ok) throw new Error(`cnpj.ia.br ${r1.status}`); // erro vem no envelope Error, sem data
const basic = await r1.json();
const d = basic.data;
const registro = {
razaoSocial: d.razao_social,
situacao: d.situacao_cadastral.descricao,
porte: d.porte.descricao,
cnae: d.cnae_fiscal.descricao,
base: basic.meta.data_as_of,
};
if (d.has_phone || d.has_email || d.has_website) {
const r2 = await fetch(`${API}${cnpj}?profile=full`, { headers });
if (!r2.ok) throw new Error(`cnpj.ia.br ${r2.status}`);
const { data: full, meta } = await r2.json();
if (!meta.suppressed) { // pedido de remoção ativo omite os contatos
Object.assign(registro, {
telefones: (full.telefones ?? []).map((t) => t.ddd + t.numero),
email: full.email ?? null,
site: full.site ?? null,
});
}
registro.faixaFaturamento = full.faixa_faturamento?.faixa ?? null;
registro.suprimido = Boolean(meta.suppressed);
}
return registro;
}
Campos mais usados
| Campo | Perfil | Descrição |
|---|---|---|
razao_social | basic | Nome empresarial registrado na Receita Federal. É o único nome sempre presente. |
nome_fantasia | basic | Nome fantasia declarado para o estabelecimento, na Receita Federal. null quando a empresa não declarou. |
situacao_cadastral.descricao | basic | Descrição da situação cadastral, traduzida do código pela tabela de domínio da Receita Federal. |
porte.descricao | basic | Descrição do porte, traduzida do código pela tabela de domínio da Receita Federal. |
cnae_fiscal.descricao | basic | Nome da atividade econômica principal, traduzido do código pela tabela de CNAEs da Receita Federal. |
telefones[].numero | full | Número do telefone, sem DDD e sem pontuação, cadastrado na Receita Federal. |
email | full | Endereço de correio eletrônico do contribuinte, campo da Receita Federal. null quando a empresa não tem e-mail na base. |
site | full | Site institucional da empresa. Enriquecimento da Oportunidados (a Receita Federal não publica site): escolha determinística de uma URL institucional, pela confiança da fonte e depois pela coleta mais recente. |
socios[].nome | full | Nome do sócio pessoa física, ou razão social do sócio pessoa jurídica, como publicado pela Receita Federal. Nunca acompanhado de CPF ou CNPJ do sócio. |
faixa_faturamento.faixa | full | Faixa de faturamento DERIVADA do porte declarado à Receita Federal, em texto. Não é estimativa própria, não é valor apurado e não é faturamento observado. |
faixa_funcionarios | full | Número de funcionários em texto: pode ser um número exato ou um intervalo (por exemplo Entre 12 e 40). Enriquecimento da Oportunidados; null quando não há dado oficial. |
meta.data_as_of | meta | Data da base servida na resposta. A base é atualizada mensalmente; este campo está em toda resposta porque a consulta responde pela foto do mês, não pelo instante da chamada. |
faixa_faturamento é derivada do porte declarado à Receita Federal e carrega origem: "porte". Não é estimativa nem valor apurado; trate como segmento, não como número. faixa_funcionarios é texto e pode vir null.
O que a resposta não traz
- CPF de sócio ou representante, em nenhum perfil.
- Contatos de empresa com pedido de remoção ativo: a resposta vem com
meta.suppressed: true, sem telefones, e-mail, site e sócios, e a chamada custa o preço dobasic. Trate como definitivo e não reconsulte esperando outro resultado. - Score, dívidas ou processos: a API não faz análise de crédito.
O uso dos contatos para abordagem comercial B2B e o que é proibido estão em Privacidade e uso aceitável.
Tier indicado
Free é o plano indicado: este fluxo não usa a busca de empresas.
- 60 créditos por mês
- 3 requisições por minuto
Perguntas frequentes
Toda empresa tem telefone e e-mail na resposta?
Não. Vêm quando constam na base, no perfil full. O perfil basic traz os sinais has_phone, has_email e has_website, que dizem antes se vale pedir o full para aquela conta.
A faixa de faturamento serve para qualificar a conta?
Serve como segmento. Ela é derivada do porte que a empresa declarou à Receita Federal e o próprio campo diz isso em origem: "porte". Não é um valor apurado nem uma estimativa da Oportunidados.
Vem o CPF dos sócios?
Não, em nenhum perfil. O quadro societário traz nome, qualificação, data de entrada, país e faixa etária de cada sócio, e nome e qualificação do representante legal quando houver.
Com que frequência devo reenriquecer?
A base é uma foto mensal da Receita Federal. Grave meta.data_as_of em cada registro e reenriqueça quando GET /v1/status mostrar uma data nova; antes disso a resposta seria a mesma.
Posso usar os contatos para prospecção pelo CRM?
Para abordagem comercial a empresas, com contato do estabelecimento e respeito às regras do canal, sim. Mensagens em massa não solicitadas, revenda da base e tentativa de identificar pessoas físicas são proibidas. A API honra pedidos de remoção de contatos.
Quanto custa enriquecer uma base de contas?
Depende de quantas contas têm contato. O padrão é basic para todas e full só onde has_phone ou has_email vierem verdadeiros; os pesos estão em /precos, junto com a calculadora por volume mensal.
Para continuar
Atualizado em