API grátis de consulta CNPJ

Dados de 73.797.761 empresas da Receita Federal em JSON, inclusive o CNPJ alfanumérico.

GET/api/v1/cnpj/{cnpj}

Testar agora

Aceita o alfanumérico, como 12.ABC.345/01DE-35.

Mais bases (opcional)

Junta outras bases públicas à resposta, em bases_extras. Até 5.

GET /api/v1/cnpj/00000000000191

Este teste roda sem chave, com 10 consultas por minuto. Com a sua chave, use este endereço.

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.

consulta.shbash consulta.sh
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'
consulta.shbash consulta.sh
# --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"
consulta.jsCole no console do navegador (F12) ou rode: node consulta.mjs
// 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);
}
consulta.mjsnode consulta.mjs
// 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;
}
consulta.tsnode consulta.ts (Node 22.18 ou mais novo; antes disso: npx tsx consulta.ts)
// 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;
  });
consulta.pypip install requests && python consulta.py
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"])
consulta.phpphp consulta.php
<?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";
consulta.gogo run consulta.go
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)
}
Consulta.javajava Consulta.java
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\":\"([^\"]*)\""));
    }
}
Consulta.csdotnet run Consulta.cs (.NET 10; em versões antigas, cole em Program.cs de um projeto console)
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;
consulta.rbruby consulta.rb
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"]}"
src/main.rscargo run
// 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(())
}
consulta.ktkotlinc consulta.kt -include-runtime -d consulta.jar && java -jar consulta.jar
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\":\"([^\"]*)\""))
}
main.swiftswiftc -parse-as-library main.swift (ou: swift main.swift)
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)
}
consulta.dartdart run consulta.dart
// 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']}');
}
consulta.ps1powershell -File consulta.ps1
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
}
Code.gsExtensões > Apps Script, cole em Code.gs, salve e use =CNPJ("00000000000191") em uma célula
/**
 * 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. 1. Entre com o e-mailSem senha: você recebe um link de acesso. É grátis e não pede cartão.
  2. 2. Crie a chave no painelEla aparece uma vez só, no formato cnpjfy_xxxxxx_... Guarde em um cofre ou variável de ambiente.
  3. 3. Mande no cabeçalhoAuthorization: Bearer SUA_CHAVE em cada pedido. Também vale x-api-key: SUA_CHAVE.
Terminal
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

Endpoints da API v1
RotaO 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/infoTotal de empresas e data da base da Receita e de cada base extra.
GET /api/v1/openapi.jsonEste contrato em OpenAPI 3.1. Não pede chave.

Parâmetros

Parâmetros de /api/v1/cnpj/{cnpj}
NomeOndeDescrição
cnpjcaminho14 caracteres, com ou sem máscara (00.000.000/0001-91). Aceita letras: 12.ABC.345/01DE-35.
incluirconsultaOpcional. 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

Campos da resposta de /api/v1/cnpj/{cnpj}
CampoTipoDescrição
cnpjtextoCNPJ com 14 caracteres, sem máscara, em maiúsculas.
cnpj_formatadotextoCNPJ com a máscara 00.000.000/0000-00.
tipo_cnpjtextoalfanumerico quando há letras (formato emitido desde 31/07/2026).
razao_socialtexto ou nullRazão social.
nome_fantasiatexto ou nullNome fantasia.
estabelecimentotexto ou nullMatriz ou filial.
situacaoobjetoSituação cadastral.
situacao_especialobjeto ou nullSituação especial, quando há.
aberturatexto ou nullInício da atividade.
natureza_juridicaobjetoNatureza jurídica.
portetexto ou nullPorte informado à Receita.
capital_socialnúmero ou nullCapital social em reais.
atividade_principalobjeto ou nullAtividade econômica principal (CNAE).
atividades_secundariaslistaDemais atividades econômicas (CNAE).
enderecoobjetoEndereço do estabelecimento.
contatoobjetoE-mail e telefones cadastrados.
simplesobjetoOpção pelo Simples Nacional.
meiobjetoOpção pelo MEI.
ente_federativotexto ou nullEnte federativo responsável (órgãos públicos).
qualificacao_responsavelobjeto ou nullQualificação do responsável pela empresa (ex.: Diretor).
socioslista ou nullQuadro de sócios e administradores. null quando omitido (veja socios_omitidos). O documento já vem mascarado pela Receita.
socios_omitidosobjetoSó aparece quando socios é null.
bases_extras_omitidasobjetoAparece 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_extrasobjetoSó 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.
fonteobjeto
consultado_emtextoHora (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.

Bases extras
BaseO que é
cnoObras (CNO). Obras de construção cadastradas no CNO (Cadastro Nacional de Obras) para o CNPJ.
rntrcTransporte (RNTRC). Registro de transportador rodoviário de carga publicado pela ANTT.
ceisSanções CEIS. Sanções do CEIS (empresas inidôneas e suspensas), da CGU.
cnepSanções CNEP. Sanções do CNEP (empresas punidas), da CGU.
cepimImpedimentos CEPIM. Impedimentos do CEPIM (entidades privadas sem fins lucrativos impedidas), da CGU.
acordos_lenienciaAcordos de leniência. Acordos de leniência e seus efeitos.
licitacoesLicitações. Licitações desde 2013 em que o CNPJ participou, venceu ou recebeu empenho.
contratosContratos públicos. Contratos com o governo federal desde 2013, com itens e aditivos.
conveniosConvênios. Convênios e ordens bancárias em que o CNPJ é convenente.
emendas_documentosDocumentos de emendas. Documentos de emendas parlamentares com o CNPJ como favorecido, desde 2013.
emendas_parlamentaresEmendas parlamentares. Recebimentos de emendas parlamentares em nome do CNPJ.
favorecidos_pjFavorecidos PJ. Cadastro de favorecidos pessoa jurídica do Portal da Transparência.
notas_fiscaisNotas fiscais. Notas fiscais desde 2013 em que o CNPJ é emitente ou destinatário.
renuncias_fiscaisRenú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.

Códigos de motivo da validação
motivoQuando
caracteres_invalidosA entrada tem caracteres que não existem em CNPJ.
tamanho_invalidoDepois de tirar ponto, barra, hífen e espaços, não sobram 14 caracteres.
digito_nao_numericoUm dos dois dígitos verificadores (os dois últimos caracteres) não é número.
caracteres_repetidosTodos os caracteres são iguais.
digito_verificador_incorretoOs 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"
  }
}
Erros da API
StatuscodigoQuandomensagem
400cnpj_invalidoOs 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.
400parametro_invalidoincluir 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, ...
401chave_ausenteO 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.
401chave_invalidaA 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.
404nao_encontradoO 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.
405metodo_nao_permitidoQualquer método que não seja GET ou OPTIONS.Esta API só aceita GET.
429limite_excedidoMais 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.
429cota_diaria_excedidaMais 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. ...
502fonte_indisponivelA fonte dos dados deu erro ou não respondeu em 8 segundos.A fonte dos dados não respondeu. Tente de novo em instantes.
503fonte_ocupadaA 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.
503auth_indisponivelNã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.

Seu appSite, planilha ou sistema
CNPJfyConfere a chave, normaliza, guarda em cache
Receita FederalBase aberta, via OpenCNPJ

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.