PJ.bio dados públicos de CNPJ

API do PJ.bio

A mesma base que serve as fichas do site, em JSON: cadastro da Receita, endereço normalizado, coordenadas e código do IBGE do município. Feita para as ferramentas da casa consumirem.

Contrato OpenAPI 3.1: https://pj.bio/openapi.yaml

Começar Autenticação Formato Consultar CNPJ Buscar empresas Coordenadas Selo "verificado" Webhook Status Erros Limites O que os campos significam

Começar

  1. Peça uma chave a quem administra o PJ.bio. Não há autoatendimento: a chave é emitida no painel, com os escopos que aquela ferramenta precisa.
  2. Guarde a chave no .env do seu projeto, nunca no código versionado.
  3. Confira com GET /api/v1/status antes de escrever qualquer integração.

Base atual: ainda não importada nesta instalação. A Receita publica uma vez por mês e a base inteira é substituída na importação.

Autenticação

Toda rota exige chave. Mande no cabeçalho Authorization:

curl -H "Authorization: Bearer pjbio_7f3a91c0_9d2e…" \
     "https://pj.bio/api/v1/status"

Se o seu proxy não deixa mexer no Authorization, use X-API-Key com o mesmo valor. Não há outra forma — chave em query string ficaria gravada em log de servidor e em histórico de proxy.

Escopos

A chave carrega só o que foi concedido a ela. Campo fora do escopo não vem — e a resposta não avisa, para não virar um mapa do que existe. Confira o que a sua chave tem em /api/v1/status.

EscopoLiberaObservação
cnpj Consultar CNPJ Ficha completa de um CNPJ: cadastro, endereço, coordenadas e atividades.
busca Buscar e listar empresas Listagem por nome, cidade, estado e atividade, com paginação.
socios Ver o quadro societário Nome do sócio é dado pessoal. Só conceda a quem precisa de fato.
contato Ver telefone e e-mail No site esses campos exigem conta. Conceder aqui contorna esse portão.
geo Gerar coordenadas de endereços Os 111 milhões de endereços do CNEFE. Não expõe empresa nem pessoa: só CEP, rua e coordenada.
selo Ler o selo "verificado pelo PJ.bio" Se o dono provou que responde pelo CNPJ (reivindicação aprovada). É o que as outras plataformas exibem como "verificado".

Formato

Sucesso sempre em dados; listagem traz meta junto:

{
  "dados": { … },
  "meta":  { "fonte": "Receita Federal", "competencia": "2026-08-01" }
}

Erro sempre em erro, com um código estável para você testar:

{
  "erro": { "codigo": "nao_encontrado", "mensagem": "…" }
}

Teste erro.codigo, nunca o texto de erro.mensagem — a mensagem é para humano em log e pode mudar de redação sem aviso.

GET /api/v1/cnpj/{cnpj}

escopo cnpj

A ficha completa de um estabelecimento. Aceita com ou sem máscara (08430449000100 ou 08.430.449/0001-00), em qualquer caixa. Aqui não há recorte por situação: baixada, suspensa e inapta respondem igual.

CNPJ alfanumérico. Desde julho de 2026 a Receita emite CNPJ com letras nas 12 primeiras posições (00.000.000/E08G-12); só os dois dígitos verificadores continuam numéricos. A API aceita e devolve os dois formatos. Se a sua ferramenta valida CNPJ com \d{14}, ela vai rejeitar empresas que existem.

Tamanho errado (inclusive 13 dígitos — o zero à esquerda que caiu) e dígito verificador que não confere respondem 422 cnpj_invalido, com a mensagem dizendo qual dos dois. 404 nao_encontrado é só para CNPJ bem formado que não está na competência importada.

curl -H "Authorization: Bearer $PJ.BIO_TOKEN" \
     "https://pj.bio/api/v1/cnpj/08430449000100"
{
  "dados": {
    "cnpj": "08430449000100",
    "cnpj_formatado": "08.430.449/0001-00",
    "tipo": "matriz",
    "razao_social": "PADARIA E CONFEITARIA REIS MAGOS LTDA",
    "nome_fantasia": "PADARIA REIS MAGOS",
    "situacao": { "codigo": "02", "descricao": "ATIVA", "data": "2006-11-13", "motivo": null },
    "abertura": { "data": "2006-11-13", "anos": 19 },
    "porte": { "codigo": "01", "descricao": "ME" },
    "natureza_juridica": { "codigo": "2062", "descricao": "Sociedade Empresária Limitada" },
    "capital_social": "50000.00",
    "simples": { "optante": true, "mei": false },
    "atividade_principal": { "codigo": "4721102", "descricao": "Padaria e confeitaria…" },
    "atividades_secundarias": [ … ],
    "endereco": {
      "logradouro": "AV ENGENHEIRO ROBERTO FREIRE",
      "numero": "1912",
      "bairro": "CAPIM MACIO",
      "cep": "59082095",
      "municipio": "NATAL",
      "uf": "RN",
      "municipio_codigo_rfb": "1761",
      "municipio_codigo_ibge": "2408102",
      "completo": "Av Engenheiro Roberto Freire, 1912 — Capim Macio — Natal / RN"
    },
    "localizacao": {
      "latitude": -5.8712,
      "longitude": -35.1998,
      "precisao": "numero",
      "fonte": "cnefe",
      "atualizada_em": "2026-09-01T12:00:00-03:00"
    },
    "unidades": { "total": 3 },
    "ficha": { "url": "https://pj.bio/padaria…", "reivindicada": true }
  }
}

localizacao pode ser null. 92% das empresas ativas têm coordenada; o resto não tem, e a API prefere dizer "não sei" a devolver o centro da cidade como se fosse a porta.

GET /api/v1/empresas

escopo busca

Listagem filtrada. Só empresas ativas — não é opção: os índices do recorte são parciais nessa condição, e afrouxá-la faria cada consulta varrer dezenas de milhões de linhas.

ParâmetroAceitaPara quê
qtexto, até 120Busca no nome e no fantasia (índice de texto completo).
uf2 letrasRecorte por estado.
municipiocódigo RFB (4 díg.) ou slug1761 ou natal-rn. Implica a UF.
cnae7 dígitosAtividade principal.
porte01, 03, 05ME, EPP, demais.
meibooleanoOptante pelo MEI.
simplesbooleanoOptante pelo Simples.
paginainteiro ≥ 1Padrão 1. Para chegar a uma página específica.
depois_deo meta.proximo anteriorCursor. Para percorrer o recorte inteiro, sem teto. Não combina com pagina.
por_pagina1 a 100Padrão 30.
curl -H "Authorization: Bearer $PJ.BIO_TOKEN" \
     "https://pj.bio/api/v1/empresas?municipio=natal-rn&cnae=4721102&por_pagina=50"
{
  "dados": [
    {
      "cnpj": "08430449000100",
      "razao_social": "PADARIA E CONFEITARIA REIS MAGOS LTDA",
      "nome_fantasia": "PADARIA REIS MAGOS",
      "situacao": "ATIVA",
      "municipio": "NATAL",
      "uf": "RN",
      "atividade_principal": "4721102",
      "abertura": "2006-11-13",
      "ficha": "https://pj.bio/padaria…"
    }
  ],
  "meta": {
    "pagina": 1, "por_pagina": 50,
    "proximo": "02205724000369",
    "total": 10000, "total_exato": false,
    "somente_ativas": true
  }
}

Percorrer um recorte inteiro

Passe o meta.proximo de cada resposta em depois_de na seguinte. Quando vier null, acabou. Cada página custa o mesmo que a primeira, não importa a profundidade — é o modo certo para enriquecer um cadastro:

GET /api/v1/empresas?uf=RN&cnae=4721102&por_pagina=100
GET /api/v1/empresas?uf=RN&cnae=4721102&por_pagina=100&depois_de=02205724000369
GET /api/v1/empresas?uf=RN&cnae=4721102&por_pagina=100&depois_de=08430449000100
…  até "proximo": null

total_exato: false quer dizer "pelo menos isso". A contagem para em 10.000: contar de verdade um recorte de milhões custa a leitura do índice inteiro para produzir um número que ninguém pagina até o fim.

Pelo mesmo motivo a paginação por número para em 10.000 resultados (paginacao_profunda) — e, mesmo antes do teto, página funda custa mais, porque o banco lê e descarta todas as anteriores. Para varrer, use depois_de; para chegar a um ponto específico, estreite por uf e municipio.

A listagem nunca traz sócio, telefone, e-mail ou coordenada, mesmo que a chave tenha esses escopos. Cem linhas por página com dado pessoal dentro deixa de ser paginação e vira exportação de base. Para isso, use /cnpj/{cnpj} um a um.

Coordenadas

escopo geo

Os 111 milhões de endereços do CNEFE (IBGE, Censo 2022) que já estão neste banco. Não há chamada a serviço de fora, não há custo por consulta e não há teto diário — o que no Google seria fatura, aqui é índice.

Toda resposta traz precisao, e ela decide o que o número vale: numero é a porta; logradouro é a rua certa com ponto aproximado; cep é algum ponto da via. Se você vai medir distância ou desenhar raio de atendimento, trate os dois últimos como erro de dezenas a centenas de metros.

GET /api/v1/geo/cep/{cep}

A via daquele CEP. Precisão cep por definição — sem número não há imóvel.

GET /api/v1/geo/endereco

Parâmetros: cep (obrigatório), numero, logradouro. Com número tenta a porta; sem achar, cai para a via e diz que caiu.

curl -H "Authorization: Bearer $PJ.BIO_GEO" \
     "https://pj.bio/api/v1/geo/endereco?cep=59025580&numero=588"
{
  "dados": {
    "latitude": -5.78493,
    "longitude": -35.20833,
    "precisao": "numero",
    "fonte": "cnefe",
    "endereco": {
      "cep": "59025580",
      "logradouro": "PRACA ANDRE DE ALBUQUERQUE",
      "numero": "588",
      "bairro": "CIDADE ALTA",
      "municipio_ibge": "2408102",
      "uf": "RN"
    }
  },
  "meta": { "fonte": "CNEFE — IBGE, Censo 2022" }
}

No degrau cep o campo numero vem null. A linha que casou só pelo CEP é de um vizinho qualquer da mesma via: o logradouro e o bairro valem, o número não. Devolvê-lo seria afirmar um endereço errado.

POST /api/v1/geo/lote

Até 100 endereços numa consulta só — é o caso de uso real: enriquecer um cadastro inteiro de uma vez.

{
  "enderecos": [
    { "cep": "59025580", "numero": "588" },
    { "cep": "01310100", "logradouro": "Avenida Paulista" }
  ]
}

A saída tem a mesma ordem e o mesmo tamanho da entrada, com null onde não achou. Case por posição. Se devolvesse só os encontrados, o alinhamento quebraria em silêncio e a coordenada de uma linha viraria a de outra — erro que não estoura e contamina o cadastro inteiro.

Selo "verificado pelo PJ.bio"

escopo selo (vem por padrão em toda chave nova)

O selo afirma uma coisa só: o dono provou que responde por este CNPJ — a reivindicação com documento e selfie, revisada por uma pessoa. Não é "cadastro confere com a Receita": isso vence a cada competência e não diz nada sobre quem está do outro lado. É o que a sua plataforma exibe ao lado de um cadastro como dono verificado.

A verdade mora aqui, não na sua plataforma. O selo nasce e morre dentro do PJ.bio: cai quando a reivindicação é revertida, quando a conta do dono é bloqueada e quando o CNPJ some da Receita. Guarde uma cópia só para renderizar (pjbio_verificado_em) e renove pelo lote, de madrugada. Não há cache: o dono aprovado às 15h aparece verificado às 15h01.

GET /api/v1/selo/{cnpj}

{
  "dados": {
    "cnpj": "61708158000105",
    "existe": true,
    "verificado": true,
    "desde": "2026-09-01T15:03:10-03:00",
    "situacao": { "codigo": "02", "descricao": "ATIVA" },
    "ficha": "https://pj.bio/padaria-puro-pao-61708158000105"
  },
  "meta": { "selo": "O dono provou, com documento e selfie revisados por uma pessoa, que responde por este CNPJ." }
}

POST /api/v1/selo/lote

Até 100 CNPJs em {"cnpjs": [...]} — para reconciliar um cadastro inteiro (134 mil CNPJs são 1.341 chamadas). A saída tem a mesma ordem e o mesmo tamanho da entrada; um CNPJ malformado derruba o lote inteiro com 422 cnpj_invalido, porque em reconciliação automatizada silenciar uma posição é o que faz o desalinhamento passar despercebido. meta.pedidos e meta.verificados resumem o lote.

Webhook: o PJ.bio avisa quando o selo muda

configurado por plataforma em /painel/plataformas · exige uma chave viva com o escopo selo

O lote de madrugada garante consistência; o webhook garante o "na hora": o dono aprovado às 15h aparece verificado no SuperTur às 15h01. O aviso é magro — diz que o selo do CNPJ mudou e por quê, e a sua plataforma re-consulta /api/v1/selo/{cnpj}. Assim o payload nunca vira verdade, a ordem de entrega não importa e uma entrega atrasada não sobrescreve uma mais nova.

Dispara em: reivindicação aprovada, dono bloqueado ou desbloqueado, CNPJ que sumiu da Receita na importação ou voltou.

POST {sua URL}
Content-Type: application/json
X-PJ.bio-Signature: sha256=9f2c…        ← HMAC-SHA256 do corpo, com o segredo do painel
X-PJ.bio-Evento: selo.mudou
X-PJ.bio-Entrega: 3f8e2c1a-7b4d-4e0f-9a6c-2d5b8e1f0a7c

{"cnpj":"61708158000105","evento":"selo.mudou","id":"3f8e2c1a-7b4d-4e0f-9a6c-2d5b8e1f0a7c","motivo":"aprovacao","ocorrido_em":"2026-09-14T15:03:10-03:00"}

Do lado de lá, três regras

// Laravel, na sua plataforma
$corpo = $request->getContent();
$esperada = 'sha256=' . hash_hmac('sha256', $corpo, config('cnpjbio.webhook_segredo'));

abort_unless(hash_equals($esperada, (string) $request->header('X-PJ.bio-Signature')), 401);

$evento = json_decode($corpo, true);
if (Cache::add('cnpjbio:entrega:' . $evento['id'], 1, now()->addDays(7))) {
    ReconsultarSelo::dispatch($evento['cnpj']);   // GET /api/v1/selo/{cnpj} e grava pjbio_verificado_em
}

return response()->noContent(200);

O botão "Enviar evento de teste" do painel manda um selo.teste com os mesmos cabeçalhos — é o jeito de conferir URL e segredo antes de qualquer reivindicação real. As últimas 20 entregas de cada chave ficam visíveis no painel, com status HTTP, tentativas e a resposta que a sua plataforma devolveu.

GET /api/v1/status

qualquer chave válida

Diz quem é a chave, o que ela pode e de que competência é a base. Serve para conferir a credencial num deploy sem gastar consulta — e é a primeira coisa a checar quando um campo esperado não veio (quase sempre: escopo que a chave não tem).

{
  "dados": {
    "chave": {
      "nome": "SuperTur — produção",
      "prefixo": "pjbio_7f3a91c0",
      "escopos": ["cnpj", "busca"],
      "limite_por_minuto": 60
    },
    "base": { "fonte": "Receita Federal", "competencia": "2026-08-01" }
  }
}

Erros

HTTPCódigoO que fazer
401sem_credencialFaltou o cabeçalho. Não repita a chamada.
401credencial_invalidaChave errada ou truncada. Não repita.
403credencial_revogadaA chave foi desligada. Pare de tentar e peça outra.
403plataforma_desativadaA empresa dona da chave foi desativada — todas as chaves dela param juntas. Não adianta trocar de chave; fale com quem opera o PJ.bio.
403escopo_ausenteA chave não tem o escopo da rota. Peça a ampliação.
404nao_encontradoCNPJ não existe na competência importada.
422cnpj_invalidoNão são 14 dígitos.
422cep_invalidoNão são 8 dígitos.
422parametros_invalidosConfira a tabela de parâmetros.
422municipio_desconhecidoUse o código RFB de 4 dígitos ou o slug de /cidade.
422paginacao_profundaEstreite o filtro; a paginação não vai tão fundo.
429limite_excedidoRespeite o Retry-After do cabeçalho.
500erro_internoRegistrado do nosso lado. Repita com recuo exponencial.

Limites

O teto é por chave, não por IP: as ferramentas rodam em servidor e várias podem sair do mesmo endereço — limitar por IP puniria a chave comportada por causa da vizinha. Toda resposta traz o estado atual:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57

No 429 vem também Retry-After, em segundos. Respeitá-lo é mais rápido que insistir: cada tentativa recusada consome a mesma janela.

O que os campos significam

localizacao.precisao — leia antes de plotar

A coordenada vem do cruzamento com o CNEFE do IBGE (Censo 2022), sem chamada a serviço externo. Ela nunca vem sozinha:

Se você vai medir distância ou desenhar raio de atendimento, trate cep e logradouro como erro de dezenas a centenas de metros. Ignorar esse campo é o jeito mais fácil de produzir um relatório errado com aparência de exato.

Os dois códigos de município

municipio_codigo_rfb tem 4 dígitos e é o do cadastro fiscal. municipio_codigo_ibge tem 7 e é o que cruza com censo, malha territorial e qualquer base pública de fora. São números diferentes para o mesmo município: trocar um pelo outro não dá erro, dá resultado errado calado.

capital_social é string

Decimal com duas casas, em texto ("50000.00"). Em ponto flutuante o valor perde centavo na ida e na volta — e quem consome capital social costuma estar somando.

Datas

YYYY-MM-DD nos campos de data; ISO 8601 com fuso (-03:00) nos de instante.

Sócios

Com o escopo socios, a ficha traz socios (o quadro inteiro, até 1.000 — o site mostra 30) e socios_total. Cada sócio vem com nome, documento, qualificacao, data_entrada, tipo (fisica, juridica ou estrangeiro), faixa_etaria, pais e, quando existe, representante_legal (quem assina pelo sócio PJ, menor ou incapaz).

MEI, empresário individual e produtor rural não têm sócio — são 45 milhões de CNPJs, e a Receita não publica linha nenhuma para eles no arquivo de sócios. Nesses casos socios vem vazio e responsavel traz o titular (nome, papel, natureza), extraído da razão social.

documento é o que a Receita publica: CPF mascarado (***038787**, só os seis dígitos do meio) para pessoa física, e só a raiz do CNPJ (8 dígitos) para pessoa jurídica. Não dá para reconstruir o CPF a partir daqui — e não é para dar.

Sócios e LGPD

Nome de sócio é dado pessoal. Quem pede remoção pelo portal de privacidade sai da base na importação seguinte — e some da API junto, porque a API lê a mesma tabela que o site. Se você copiar sócios para o seu banco, reconcilie: cópia que ninguém atualiza transforma um pedido atendido aqui em dado vivo aí.

Ainda não tem chave?

Fale com quem administra o PJ.bio. Diga qual ferramenta vai consumir, com que frequência, e se precisa de sócios ou contato — os dois escopos sensíveis só são concedidos quando há motivo declarado.