API grátis de consulta CNPJ
Dados de 73.797.761 empresas da Receita Federal em JSON, inclusive o CNPJ alfanumérico.
/api/v1/cnpj/{cnpj}{
"razao_social": "BANCO DO BRASIL SA",
"situacao": {
"descricao": "Ativa"
},
"natureza_juridica": {
"codigo": "2038",
"descricao": "Sociedade de Economia Mista"
},
"capital_social": 120000000000,
"fonte": {
"via": "OpenCNPJ",
"base": "2026-11-01"
}
}Testar agora
A resposta aparece aqui
Exemplos em 17 linguagens
Todos com a chave em Authorization: Bearer SUA_CHAVE e, em caso de erro, mostrando erro.mensagem.
curl --silent --show-error --fail-with-body "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191" \
-H "Authorization: Bearer SUA_CHAVE"
# com o jq, só os dois campos (ou erro.mensagem, se a consulta falhar):
curl -s -H "Authorization: Bearer SUA_CHAVE" "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191" | jq -r 'if .erro then "Erro: " + .erro.mensagem else .razao_social + " - " + .situacao.descricao end'# --body mostra só o JSON; --check-status devolve erro no terminal quando o status não é 2xx
http --body --check-status -A bearer -a SUA_CHAVE GET "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191"// A API libera CORS, mas uma chave no front-end público fica visível a qualquer visitante: em produção, chame do seu servidor.
const cnpj = "00000000000191";
try {
const resposta = await fetch(`https://cnpjfy.walisson.dev/api/v1/cnpj/${cnpj}`, {
headers: { Authorization: "Bearer SUA_CHAVE" },
});
const dados = await resposta.json();
if (!resposta.ok) throw new Error(dados.erro.mensagem);
console.log(`${dados.razao_social} - ${dados.situacao.descricao}`);
} catch (e) {
console.error("Erro:", e.message);
}// Node 18 ou mais novo: fetch já vem de fábrica. Uso: node consulta.mjs [CNPJ]
const cnpj = process.argv[2] ?? "00000000000191";
try {
const resposta = await fetch(`https://cnpjfy.walisson.dev/api/v1/cnpj/${encodeURIComponent(cnpj)}`, {
headers: { Authorization: "Bearer SUA_CHAVE" },
signal: AbortSignal.timeout(10_000),
});
const dados = await resposta.json();
if (!resposta.ok) throw new Error(dados.erro.mensagem);
console.log(`${dados.razao_social} - ${dados.situacao.descricao}`);
} catch (e) {
console.error("Erro:", e.message);
process.exitCode = 1;
}// Os tipos abaixo cobrem os campos usados aqui; o schema completo está em /api/v1/openapi.json.
type Empresa = {
cnpj: string;
razao_social: string | null;
situacao: { descricao: string | null; data: string | null };
capital_social: number | null;
};
type ErroDaApi = { erro: { codigo: string; status: number; mensagem: string; documentacao: string } };
async function consultar(cnpj: string): Promise<Empresa> {
const resposta = await fetch(`https://cnpjfy.walisson.dev/api/v1/cnpj/${encodeURIComponent(cnpj)}`, {
headers: { Authorization: "Bearer SUA_CHAVE" },
});
const dados: Empresa | ErroDaApi = await resposta.json();
if ("erro" in dados) throw new Error(dados.erro.mensagem);
return dados;
}
consultar("00000000000191")
.then((e) => console.log(`${e.razao_social} - ${e.situacao.descricao}`))
.catch((e: Error) => {
console.error("Erro:", e.message);
process.exitCode = 1;
});import requests
resposta = requests.get(
"https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191",
headers={"Authorization": "Bearer SUA_CHAVE"},
timeout=10,
)
dados = resposta.json()
if not resposta.ok:
raise SystemExit(f"Erro: {dados['erro']['mensagem']}")
print(dados["razao_social"], "-", dados["situacao"]["descricao"])<?php
$ch = curl_init("https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ["Authorization: Bearer SUA_CHAVE"],
]);
$corpo = curl_exec($ch);
if ($corpo === false) {
fwrite(STDERR, "Erro: " . curl_error($ch) . "\n");
exit(1);
}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$dados = json_decode($corpo, true);
if ($status !== 200) {
fwrite(STDERR, "Erro: " . $dados["erro"]["mensagem"] . "\n");
exit(1);
}
echo $dados["razao_social"] . " - " . $dados["situacao"]["descricao"] . "\n";package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type resposta struct {
RazaoSocial string `json:"razao_social"`
Situacao struct {
Descricao string `json:"descricao"`
} `json:"situacao"`
Erro struct {
Mensagem string `json:"mensagem"`
} `json:"erro"`
}
func main() {
cliente := http.Client{Timeout: 10 * time.Second}
pedido, _ := http.NewRequest("GET", "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191", nil)
pedido.Header.Set("Authorization", "Bearer SUA_CHAVE")
res, err := cliente.Do(pedido)
if err != nil {
fmt.Fprintln(os.Stderr, "Erro:", err)
os.Exit(1)
}
defer res.Body.Close()
var dados resposta
if err := json.NewDecoder(res.Body).Decode(&dados); err != nil {
fmt.Fprintln(os.Stderr, "Erro:", err)
os.Exit(1)
}
if res.StatusCode != http.StatusOK {
fmt.Fprintln(os.Stderr, "Erro:", dados.Erro.Mensagem)
os.Exit(1)
}
fmt.Println(dados.RazaoSocial, "-", dados.Situacao.Descricao)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// Java 11 ou mais novo. O JDK não traz leitor de JSON; aqui um regex basta para dois campos.
// Em um projeto de verdade, use Jackson ou Gson.
public class Consulta {
static String campo(String json, String padrao) {
Matcher m = Pattern.compile(padrao).matcher(json);
return m.find() ? m.group(1) : "";
}
public static void main(String[] args) throws Exception {
HttpClient cliente = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
HttpRequest pedido = HttpRequest.newBuilder(URI.create("https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191"))
.header("Authorization", "Bearer SUA_CHAVE")
.timeout(Duration.ofSeconds(10)).GET().build();
HttpResponse<String> resposta = cliente.send(pedido, HttpResponse.BodyHandlers.ofString());
if (resposta.statusCode() != 200) {
System.err.println("Erro: " + campo(resposta.body(), "\"mensagem\":\"([^\"]*)\""));
System.exit(1);
}
System.out.println(campo(resposta.body(), "\"razao_social\":\"([^\"]*)\"") + " - "
+ campo(resposta.body(), "\"situacao\":\\{\"descricao\":\"([^\"]*)\""));
}
}using System.Text.Json;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
http.DefaultRequestHeaders.Add("Authorization", "Bearer SUA_CHAVE");
var resposta = await http.GetAsync("https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191");
using var doc = JsonDocument.Parse(await resposta.Content.ReadAsStringAsync());
var raiz = doc.RootElement;
if (!resposta.IsSuccessStatusCode)
{
Console.Error.WriteLine($"Erro: {raiz.GetProperty("erro").GetProperty("mensagem").GetString()}");
return 1;
}
Console.WriteLine($"{raiz.GetProperty("razao_social").GetString()} - {raiz.GetProperty("situacao").GetProperty("descricao").GetString()}");
return 0;require "net/http"
require "json"
# Ruby 3 ou mais novo
resposta = Net::HTTP.get_response(
URI("https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191"),
{ "Authorization" => "Bearer SUA_CHAVE" }
)
dados = JSON.parse(resposta.body)
abort("Erro: #{dados["erro"]["mensagem"]}") unless resposta.is_a?(Net::HTTPSuccess)
puts "#{dados["razao_social"]} - #{dados["situacao"]["descricao"]}"// Cargo.toml:
// reqwest = { version = "0.12", features = ["blocking", "json"] }
// serde_json = "1"
use serde_json::Value;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let resposta = reqwest::blocking::Client::new()
.get("https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191")
.bearer_auth("SUA_CHAVE")
.send()?;
let ok = resposta.status().is_success();
let dados: Value = resposta.json()?;
if !ok {
eprintln!("Erro: {}", dados["erro"]["mensagem"].as_str().unwrap_or("erro desconhecido"));
std::process::exit(1);
}
println!(
"{} - {}",
dados["razao_social"].as_str().unwrap_or(""),
dados["situacao"]["descricao"].as_str().unwrap_or("")
);
Ok(())
}import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration
import kotlin.system.exitProcess
// Sem biblioteca de JSON, um regex pega os dois campos. Em um projeto de verdade, use kotlinx.serialization.
fun campo(json: String, padrao: String) = Regex(padrao).find(json)?.groupValues?.get(1) ?: ""
fun main() {
val cliente = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build()
val pedido = HttpRequest.newBuilder(URI.create("https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191"))
.header("Authorization", "Bearer SUA_CHAVE")
.timeout(Duration.ofSeconds(10)).GET().build()
val resposta = cliente.send(pedido, HttpResponse.BodyHandlers.ofString())
val corpo = resposta.body()
if (resposta.statusCode() != 200) {
System.err.println("Erro: " + campo(corpo, "\"mensagem\":\"([^\"]*)\""))
exitProcess(1)
}
println(campo(corpo, "\"razao_social\":\"([^\"]*)\"") + " - " + campo(corpo, "\"situacao\":\\{\"descricao\":\"([^\"]*)\""))
}import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking // Linux
#endif
struct Resposta: Decodable {
struct Situacao: Decodable { let descricao: String? }
struct Erro: Decodable { let mensagem: String }
let razao_social: String?
let situacao: Situacao?
let erro: Erro?
}
var pedido = URLRequest(url: URL(string: "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191")!)
pedido.setValue("Bearer SUA_CHAVE", forHTTPHeaderField: "Authorization")
do {
let (dados, resposta) = try await URLSession.shared.data(for: pedido)
let corpo = try JSONDecoder().decode(Resposta.self, from: dados)
if (resposta as? HTTPURLResponse)?.statusCode != 200 {
print("Erro:", corpo.erro?.mensagem ?? "erro desconhecido")
exit(1)
}
print(corpo.razao_social ?? "", "-", corpo.situacao?.descricao ?? "")
} catch {
print("Erro:", error.localizedDescription)
exit(1)
}// dart pub add http
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
Future<void> main() async {
final resposta = await http
.get(
Uri.parse('https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191'),
headers: {'Authorization': 'Bearer SUA_CHAVE'},
)
.timeout(const Duration(seconds: 10));
final dados = jsonDecode(utf8.decode(resposta.bodyBytes)) as Map<String, dynamic>;
if (resposta.statusCode != 200) {
stderr.writeln('Erro: ${dados['erro']['mensagem']}');
exit(1);
}
print('${dados['razao_social']} - ${dados['situacao']['descricao']}');
}try {
$dados = Invoke-RestMethod -Uri "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191" -TimeoutSec 10 `
-Headers @{ Authorization = "Bearer SUA_CHAVE" }
"$($dados.razao_social) - $($dados.situacao.descricao)"
}
catch {
# em erro HTTP, o corpo (JSON da API) vem em ErrorDetails.Message (PowerShell 7) ou no stream da resposta (5.1)
$mensagem = $_.Exception.Message
$corpo = $_.ErrorDetails.Message
if (-not $corpo -and $_.Exception.Response) {
$corpo = (New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())).ReadToEnd()
}
if ($corpo) { try { $mensagem = ($corpo | ConvertFrom-Json).erro.mensagem } catch { } }
[Console]::Error.WriteLine("Erro: $mensagem")
exit 1
}/**
* Razão social e situação de um CNPJ, pela API do CNPJfy.
* Na planilha: =CNPJ("00.000.000/0001-91")
* @param {string} cnpj CNPJ com ou sem máscara.
* @return {string} Ex.: BANCO DO BRASIL SA - Ativa
* @customfunction
*/
function CNPJ(cnpj) {
const limpo = String(cnpj).replace(/[^0-9A-Za-z]/g, "");
const resposta = UrlFetchApp.fetch("https://cnpjfy.walisson.dev/api/v1/cnpj/" + limpo, {
headers: { Authorization: "Bearer SUA_CHAVE" },
muteHttpExceptions: true,
});
const dados = JSON.parse(resposta.getContentText());
if (resposta.getResponseCode() !== 200) return "Erro: " + dados.erro.mensagem;
return dados.razao_social + " - " + dados.situacao.descricao;
}Documentação
Sua chave
Todas as rotas pedem uma chave grátis. Cada chave faz 60 consultas por minuto e 1.000 por dia. São 3 passos, uns 2 minutos.
- 1. Entre com o e-mailSem senha: você recebe um link de acesso. É grátis e não pede cartão.
- 2. Crie a chave no painelEla aparece uma vez só, no formato cnpjfy_xxxxxx_... Guarde em um cofre ou variável de ambiente.
- 3. Mande no cabeçalhoAuthorization: Bearer SUA_CHAVE em cada pedido. Também vale x-api-key: SUA_CHAVE.
curl "https://cnpjfy.walisson.dev/api/v1/cnpj/00000000000191" \
-H "Authorization: Bearer SUA_CHAVE"Troque SUA_CHAVE pelo valor do painel. Guarde a chave no servidor, nunca em um site público. Se ela vazar, revogue no painel: o bloqueio vale em até 1 minuto. Quem já tem conta entra aqui.
Autenticação
Todas as rotas, menos /api/v1/openapi.json, exigem a chave em Authorization: Bearer SUA_CHAVE ou em x-api-key: SUA_CHAVE. As respostas saem com Cache-Control: private, no-store: a CDN nunca serve um 200 a quem não tem chave.
Endpoints e parâmetros
| Rota | O que faz |
|---|---|
| GET /api/v1/cnpj/{cnpj} | Dados cadastrais de uma empresa. Confere os dígitos verificadores antes de consultar a fonte. |
| GET /api/v1/empresas?cnae=&uf= | Empresas ativas de uma atividade num estado, com paginação por cursor. Não traz MEIs, e-mail nem telefone. |
| GET /api/v1/validar/{cnpj} | Só confere formato e dígitos verificadores, localmente. Nunca consulta a fonte. |
| GET /api/v1/info | Total de empresas e data da base da Receita e de cada base extra. |
| GET /api/v1/openapi.json | Este contrato em OpenAPI 3.1. Não pede chave. |
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
| cnpj | caminho | 14 caracteres, com ou sem máscara (00.000.000/0001-91). Aceita letras: 12.ABC.345/01DE-35. |
| incluir | consulta | Opcional. Bases extras separadas por vírgula, até 5. Exemplo: ?incluir=cno,rntrc. |
Resposta de exemplo
Exemplo ilustrativo: os dados da empresa são os da Receita Federal; o nome do sócio é fictício (NOME DO SÓCIO) e o CPF vem mascarado, como a Receita publica.
{
"cnpj": "00000000000191",
"cnpj_formatado": "00.000.000/0001-91",
"tipo_cnpj": "numerico",
"razao_social": "BANCO DO BRASIL SA",
"nome_fantasia": "DIRECAO GERAL",
"estabelecimento": "matriz",
"situacao": {
"descricao": "Ativa",
"data": "2005-11-03",
"motivo": null
},
"situacao_especial": null,
"abertura": "1966-08-01",
"natureza_juridica": {
"codigo": "2038",
"descricao": "Sociedade de Economia Mista"
},
"porte": "Demais",
"capital_social": 120000000000,
"atividade_principal": {
"codigo": "6422100",
"codigo_formatado": "6422-1/00",
"descricao": "Bancos múltiplos, com carteira comercial"
},
"atividades_secundarias": [
{
"codigo": "6499999",
"codigo_formatado": "6499-9/99",
"descricao": "Outras atividades de serviços financeiros não especificadas anteriormente"
}
],
"endereco": {
"tipo_logradouro": "QUADRA",
"logradouro": "SAUN QUADRA 5 BLOCO B TORRE I, II, III",
"numero": "SN",
"complemento": "ANDAR T I SL S101 A S1602",
"bairro": "ASA NORTE",
"cep": "70040912",
"cep_formatado": "70040-912",
"municipio": "BRASILIA",
"codigo_municipio_rf": "9701",
"uf": "DF",
"pais": null,
"cidade_exterior": null
},
"contato": {
"email": "secex@bb.com.br",
"telefones": [
{
"numero": "(61) 3493-9002",
"fax": false
}
]
},
"simples": {
"optante": false,
"data_opcao": "2007-07-01",
"data_exclusao": "2007-07-01"
},
"mei": {
"optante": false,
"data_opcao": null,
"data_exclusao": null
},
"ente_federativo": null,
"qualificacao_responsavel": {
"codigo": "10",
"descricao": "Diretor"
},
"socios": [
{
"nome": "NOME DO SÓCIO",
"documento": "***345856**",
"tipo": "Pessoa Física",
"qualificacao": "Diretor",
"entrada": "2020-07-10",
"faixa_etaria": "41 a 50 anos",
"pais": null,
"representante": null
}
],
"fonte": {
"nome": "Receita Federal do Brasil",
"via": "OpenCNPJ",
"via_url": "https://opencnpj.org",
"base": "2026-10-14",
"atualizado_em": "2026-10-14T09:48:02Z"
},
"consultado_em": "2026-11-01T12:00:00Z"
}Campos da resposta
| Campo | Tipo | Descrição |
|---|---|---|
| cnpj | texto | CNPJ com 14 caracteres, sem máscara, em maiúsculas. |
| cnpj_formatado | texto | CNPJ com a máscara 00.000.000/0000-00. |
| tipo_cnpj | texto | alfanumerico quando há letras (formato emitido desde 31/07/2026). |
| razao_social | texto ou null | Razão social. |
| nome_fantasia | texto ou null | Nome fantasia. |
| estabelecimento | texto ou null | Matriz ou filial. |
| situacao | objeto | Situação cadastral. |
| situacao_especial | objeto ou null | Situação especial, quando há. |
| abertura | texto ou null | Início da atividade. |
| natureza_juridica | objeto | Natureza jurídica. |
| porte | texto ou null | Porte informado à Receita. |
| capital_social | número ou null | Capital social em reais. |
| atividade_principal | objeto ou null | Atividade econômica principal (CNAE). |
| atividades_secundarias | lista | Demais atividades econômicas (CNAE). |
| endereco | objeto | Endereço do estabelecimento. |
| contato | objeto | E-mail e telefones cadastrados. |
| simples | objeto | Opção pelo Simples Nacional. |
| mei | objeto | Opção pelo MEI. |
| ente_federativo | texto ou null | Ente federativo responsável (órgãos públicos). |
| qualificacao_responsavel | objeto ou null | Qualificação do responsável pela empresa (ex.: Diretor). |
| socios | lista ou null | Quadro de sócios e administradores. null quando omitido (veja socios_omitidos). O documento já vem mascarado pela Receita. |
| socios_omitidos | objeto | Só aparece quando socios é null. |
| bases_extras_omitidas | objeto | Aparece no lugar de bases_extras, quando ?incluir= é usado e os sócios foram omitidos (as bases extras podem trazer nome de pessoa física). |
| bases_extras | objeto | Só aparece quando ?incluir= é usado e os sócios não foram omitidos (senão vem bases_extras_omitidas). Cada chave é uma base pedida; o valor é o que a fonte entrega (null se a empresa não aparece nela). As datas do RNTRC vêm em AAAA-MM-DD. |
| fonte | objeto | |
| consultado_em | texto | Hora (UTC) em que a resposta foi montada. |
Bases do parâmetro incluir
Cada base entra em bases_extras com o conteúdo que a fonte entrega (null se a empresa não aparece nela). As datas do RNTRC, que a ANTT publica em DD/MM/AAAA, vêm em AAAA-MM-DD.
| Base | O que é |
|---|---|
| cno | Obras (CNO). Obras de construção cadastradas no CNO (Cadastro Nacional de Obras) para o CNPJ. |
| rntrc | Transporte (RNTRC). Registro de transportador rodoviário de carga publicado pela ANTT. |
| ceis | Sanções CEIS. Sanções do CEIS (empresas inidôneas e suspensas), da CGU. |
| cnep | Sanções CNEP. Sanções do CNEP (empresas punidas), da CGU. |
| cepim | Impedimentos CEPIM. Impedimentos do CEPIM (entidades privadas sem fins lucrativos impedidas), da CGU. |
| acordos_leniencia | Acordos de leniência. Acordos de leniência e seus efeitos. |
| licitacoes | Licitações. Licitações desde 2013 em que o CNPJ participou, venceu ou recebeu empenho. |
| contratos | Contratos públicos. Contratos com o governo federal desde 2013, com itens e aditivos. |
| convenios | Convênios. Convênios e ordens bancárias em que o CNPJ é convenente. |
| emendas_documentos | Documentos de emendas. Documentos de emendas parlamentares com o CNPJ como favorecido, desde 2013. |
| emendas_parlamentares | Emendas parlamentares. Recebimentos de emendas parlamentares em nome do CNPJ. |
| favorecidos_pj | Favorecidos PJ. Cadastro de favorecidos pessoa jurídica do Portal da Transparência. |
| notas_fiscais | Notas fiscais. Notas fiscais desde 2013 em que o CNPJ é emitente ou destinatário. |
| renuncias_fiscais | Renúncias fiscais. Renúncias fiscais, habilitações e imunidades ou isenções desde 2013. |
Validar sem consultar
/api/v1/validar/{cnpj} responde 200 mesmo quando o CNPJ é inválido: o que importa está em valido e motivo.
| motivo | Quando |
|---|---|
| caracteres_invalidos | A entrada tem caracteres que não existem em CNPJ. |
| tamanho_invalido | Depois de tirar ponto, barra, hífen e espaços, não sobram 14 caracteres. |
| digito_nao_numerico | Um dos dois dígitos verificadores (os dois últimos caracteres) não é número. |
| caracteres_repetidos | Todos os caracteres são iguais. |
| digito_verificador_incorreto | Os dígitos verificadores não batem com o cálculo da raiz e da ordem. |
Erros
Todo erro é JSON, no mesmo formato, com um código estável para você tratar no seu programa.
{
"erro": {
"codigo": "chave_invalida",
"status": 401,
"mensagem": "A chave é inválida ou foi revogada. Confira o valor ou crie outra em /painel.",
"documentacao": "https://cnpjfy.walisson.dev/api#erros"
}
}| Status | codigo | Quando | mensagem |
|---|---|---|---|
| 400 | cnpj_invalido | Os 14 caracteres ou os dígitos verificadores não conferem. A fonte nem é consultada. | O CNPJ informado não é válido. Confira os 14 caracteres e os dígitos verificadores. |
| 400 | parametro_invalido | incluir com base desconhecida ou com mais de 5 bases; em /validar, entrada vazia ou com mais de 40 caracteres. | O parâmetro incluir aceita: cno, rntrc, ceis, ... |
| 401 | chave_ausente | O pedido veio sem chave. Envie Authorization: Bearer SUA_CHAVE (ou x-api-key). | Envie sua chave no cabeçalho Authorization: Bearer SUA_CHAVE. Crie uma grátis em /painel. |
| 401 | chave_invalida | A chave não existe, está errada ou foi revogada no painel. | A chave é inválida ou foi revogada. Confira o valor ou crie outra em /painel. |
| 404 | nao_encontrado | O CNPJ é válido, mas não está na base da Receita Federal publicada pela OpenCNPJ (empresa muito nova, por exemplo). | Não há empresa com este CNPJ na base da Receita Federal de DD/MM/AAAA. |
| 405 | metodo_nao_permitido | Qualquer método que não seja GET ou OPTIONS. | Esta API só aceita GET. |
| 429 | limite_excedido | Mais de 60 consultas em um minuto com a mesma chave. Vem com Retry-After. | Muitas consultas seguidas com esta chave. Tente de novo em N segundos. |
| 429 | cota_diaria_excedida | Mais de 1.000 consultas no dia com a mesma chave. Renova à meia-noite (horário de Cuiabá). Vem com Retry-After. | A cota de 1.000 consultas por dia desta chave acabou. ... |
| 502 | fonte_indisponivel | A fonte dos dados deu erro ou não respondeu em 8 segundos. | A fonte dos dados não respondeu. Tente de novo em instantes. |
| 503 | fonte_ocupada | A fonte dos dados está limitando o acesso. Vem com Retry-After: 30. | A fonte dos dados está limitando o acesso agora. Tente de novo em 30 segundos. |
| 503 | auth_indisponivel | Não deu para conferir a chave naquele momento (falha passageira). Tente de novo. | Não consegui conferir a chave agora. Tente de novo em instantes. |
Limites e cache
- Limite por chave: 60 consultas por minuto (janela deslizante) e 1.000 por dia, somando /cnpj, /empresas, /validar e /info. O dia vai até a meia-noite de Cuiabá. Todas as respostas trazem RateLimit-Policy (60;w=60, 1000;w=86400), RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset; no 429 vem também Retry-After.
- Cache: a fonte é consultada no máximo uma vez por dia por CNPJ (cache do servidor). As respostas da API não são guardadas por CDN, porque exigem chave.
- Data da base: 01/11/2026. Mostrada em fonte.base e em /api/v1/info.
- CORS aberto: Access-Control-Allow-Origin: *, com os cabeçalhos Authorization, Content-Type e x-api-key. Só GET e OPTIONS.
Como funciona
Seu app chama o CNPJfy com a sua chave; ele confere o CNPJ, pede os dados à OpenCNPJ (a base aberta da Receita Federal) e devolve um JSON limpo, em português, com cache de 1 dia.
Para IAs e ferramentas
O contrato da API e um resumo do site estão em arquivos que agentes e ferramentas leem sozinhos.
Créditos
Os dados são da Receita Federal do Brasil, entregues pela base aberta OpenCNPJ. Se o seu produto mostra esses dados, vale citar as duas fontes: ajuda quem lê a saber de onde eles vêm. Veja também como o CNPJfy trata os dados. Logos das linguagens: devicon (MIT) e Simple Icons (CC0).
Perguntas frequentes
A API é grátis?
Sim. Não cobramos nada e não existe plano pago. Você só precisa criar uma chave grátis com o seu e-mail. A fonte dos dados, a OpenCNPJ, também é gratuita.
Preciso de chave de API?
Sim, todas as rotas da v1 (menos o openapi.json) pedem uma chave. Crie a sua em /painel: entra com o e-mail, sem senha e sem cartão. Envie no cabeçalho Authorization: Bearer SUA_CHAVE ou em x-api-key. Sem chave, a resposta é 401 chave_ausente.
Posso usar em um produto comercial?
A fonte responde isso no FAQ dela: "Sim. O uso é gratuito, inclusive para fins comerciais. Não oferecemos suporte dedicado." (opencnpj.org). Por aqui, o limite é de 60 consultas por minuto e 1.000 por dia, por chave. Para volumes grandes, use os downloads e o BigQuery que a OpenCNPJ publica, em vez da API.
Posso chamar a API direto do navegador?
A API libera CORS, então funciona. Mas uma chave colocada no JavaScript de um site público fica visível para qualquer visitante, que pode gastar a sua cota. Em produção, chame do seu servidor e guarde a chave em variável de ambiente. Se ela vazar, revogue no painel: o bloqueio vale em até 1 minuto.
De quando são os dados?
Da base da Receita Federal de 01/11/2026, publicada pela OpenCNPJ em ciclos (não é tempo real). Cada resposta traz a data em fonte.base, e /api/v1/info mostra a data da base e a de cada base extra. Uma empresa aberta depois dessa data ainda não aparece.
E os dados pessoais (LGPD)?
A API não acrescenta nenhum dado pessoal ao que a Receita já publica. O CPF dos sócios já vem mascarado. Quando a OpenCNPJ anonimizou a empresa, ou quando o titular pediu a remoção no CNPJfy, o quadro de sócios sai da resposta (socios vem null e socios_omitidos explica o motivo). Quem quer pedir a remoção usa a página /remover-dados.
Qual é o limite de consultas?
Por chave: 60 consultas por minuto (janela deslizante) e 1.000 por dia, somando /cnpj, /empresas, /validar e /info. Passou disso, a resposta é 429 com o cabeçalho Retry-After. Os dados ficam em cache por 1 dia no servidor, então repetir o mesmo CNPJ não toca a fonte. A OpenCNPJ não tem cota fixa, mas pode bloquear por um tempo um uso muito alto e constante.
