---
title: "GET /v1/cnpjs"
description: "Lista empresas por UF, município, CNAE, porte, situação cadastral, Simples, MEI, natureza jurídica, capital social e data de abertura. Paginação por cursor, cobrança por empresa retornada, perfil basic."
canonical: "https://cnpj.ia.br/docs/busca-empresas"
date_modified: 2026-09-02
source: "cnpj.ia.br"
---

docs · base agosto/2026 Atualizado em 02/09/2026

# GET /v1/cnpjs

Lista empresas por UF, município, CNAE, porte, situação cadastral, Simples, MEI, natureza jurídica, capital social e data de abertura. Paginação por cursor, cobrança por empresa retornada, perfil basic.

`GET /v1/cnpjs` lista empresas que atendem a um conjunto de filtros. Cada página traz até 20 empresas no perfil `basic` e custa 1 crédito por empresa retornada; página vazia custa 0. A paginação é por cursor opaco, com ordenação fixa por CNPJ. A busca exige um plano pago: no Free ela responde `403 insufficient_plan`.

## O que retorna

`data` é um array de empresas no perfil `basic`, os mesmos campos da [consulta por CNPJ](https://cnpj.ia.br/docs/consulta-cnpj) sem contatos e sócios. Para os contatos de uma empresa da lista, faça a consulta individual em `full`; os sinais `has_phone`, `has_email` e `has_website` já vêm na busca e dizem para quais vale a pena.

`meta` traz, além dos campos de toda resposta, três específicos da busca:

- `next_cursor`: o cursor da página seguinte, ou `null` na última.
- `page_size`: quantas empresas vieram nesta página. É campo de resposta; não existe parâmetro para pedir páginas maiores.
- `total_count_capped`: texto com a contagem de empresas que atendem aos filtros. Abaixo do teto de exibição é exata; a partir dele vem com o sufixo `+` (por exemplo `"10000+"`) e informa um mínimo. `null` quando não foi calculada. É uma contagem com teto, nunca uma estimativa.

## Filtros

Todos os filtros são opcionais e se combinam com E lógico. Não há busca por texto livre nem ordenação escolhida pelo cliente; a ordem é sempre por CNPJ, o que torna a paginação estável.

| Parâmetro | Tipo | Valores |
| --- | --- | --- |
| `uf` | string | Sigla da unidade federativa (`PR`, `SP`) |
| `municipio` | string | Código IBGE do município, sete dígitos (`4106902` = Curitiba) |
| `cnae` | string | CNAE fiscal: subclasse de sete dígitos sem pontuação (`1091102`) ou um prefixo (classe, grupo ou divisão) |
| `porte` | string | `ME`, `EPP` ou `DEMAIS`; a correspondência com os códigos da Receita Federal está em [Campos](https://cnpj.ia.br/docs/campos#enum-porte) |
| `situacao` | string | `ATIVA`, `SUSPENSA`, `INAPTA`, `BAIXADA` ou `NULA`; os códigos correspondentes estão em [Campos](https://cnpj.ia.br/docs/campos#enum-situacao_cadastral) |
| `simples` | boolean | Optante do Simples Nacional |
| `mei` | boolean | Optante do MEI |
| `natureza` | string | Código da natureza jurídica, quatro dígitos |
| `capital_min`, `capital_max` | number | Faixa de capital social em reais |
| `abertura_de`, `abertura_ate` | date | Faixa de data de início de atividade, `AAAA-MM-DD` |
| `cursor` | string | Cursor opaco devolvido em `meta.next_cursor` da página anterior; os demais filtros não podem mudar |

Filtro desconhecido ou valor fora do enum responde `400 invalid_filter` sem custo. Na busca, porte e situação são filtrados pelos nomes acima; na resposta, os mesmos campos vêm como código e descrição da Receita Federal, listados em [Campos da resposta](https://cnpj.ia.br/docs/campos).

## Exemplo de requisição

#### curl

```bash
# padarias ativas em Curitiba (CNAE 1091-1/02), optantes do Simples
curl "https://api.cnpj.ia.br/v1/cnpjs?uf=PR&municipio=4106902&cnae=1091102&situacao=ATIVA&simples=true" \
  -H "Authorization: Bearer $CNPJIA_KEY"

# próxima página
curl "https://api.cnpj.ia.br/v1/cnpjs?uf=PR&municipio=4106902&cnae=1091102&situacao=ATIVA&simples=true&cursor=<meta.next_cursor>" \
  -H "Authorization: Bearer $CNPJIA_KEY"
```

#### Python

```python
import os, requests

HEADERS = {"Authorization": f"Bearer {os.environ['CNPJIA_KEY']}"}
filtros = {"uf": "PR", "municipio": "4106902", "cnae": "1091102", "situacao": "ATIVA", "simples": "true"}

def paginas(filtros: dict):
    cursor = None
    while True:
        params = {**filtros, **({"cursor": cursor} if cursor else {})}
        r = requests.get("https://api.cnpj.ia.br/v1/cnpjs", params=params, headers=HEADERS, timeout=10)
        r.raise_for_status()
        body = r.json()
        yield from body["data"]
        cursor = body["meta"].get("next_cursor")
        if not cursor:
            break

for empresa in paginas(filtros):
    print(empresa["cnpj"], empresa["razao_social"])
```

#### Node

```javascript
const headers = { Authorization: `Bearer ${process.env.CNPJIA_KEY}` };
const filtros = { uf: "PR", municipio: "4106902", cnae: "1091102", situacao: "ATIVA", simples: "true" };

async function* paginas(filtros) {
  let cursor;
  do {
    const url = new URL("https://api.cnpj.ia.br/v1/cnpjs");
    Object.entries({ ...filtros, ...(cursor && { cursor }) }).forEach(([k, v]) => url.searchParams.set(k, v));
    const res = await fetch(url, { headers });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const { data, meta } = await res.json();
    yield* data;
    cursor = meta.next_cursor;
  } while (cursor);
}

for await (const e of paginas(filtros)) console.log(e.cnpj, e.razao_social);
```

## Paginação e custo

Repita a chamada com os mesmos filtros, passando em `cursor` o valor de `meta.next_cursor` da página anterior, até ele vir `null`. Mudar um filtro no meio invalida o cursor. Cada página cobra 1 crédito por empresa retornada, então percorrer todas as empresas custa tantos créditos quantas empresas houver; `total_count_capped` na primeira página dá esse número quando está abaixo do teto, e um mínimo quando vem com `+`.

## Erros

| Status | Código | Quando |
| --- | --- | --- |
| 400 | `invalid_filter` | Filtro desconhecido, valor fora da tabela ou cursor inválido |
| 401 | `invalid_api_key`, `key_expired` | Chave ausente, revogada ou vencida |
| 402 | `quota_exceeded` | Franquia e pacotes esgotados |
| 403 | `insufficient_plan` | Plano sem busca (Free); o menor plano com busca é o Starter |
| 429 | `rate_limited` | Requisições por minuto da conta excedidas |
| 503 | `maintenance` | Janela de atualização da base |
| 504 | `upstream_timeout` | Tempo limite na consulta; nada é cobrado |

## Relacionados

- [Referência gerada de `searchCnpjs`](https://cnpj.ia.br/docs/referencia/search-cnpjs).
- [Gerar filtro](https://cnpj.ia.br/docs/referencia/generate-filter): `POST /v1/filters/generate` transforma uma descrição em linguagem natural nos filtros desta busca; é a ferramenta `gerar_filtro` do [MCP](https://cnpj.ia.br/docs/mcp).
- [Créditos, franquia e rate limit](https://cnpj.ia.br/docs/creditos-e-limites).

## Perguntas frequentes

**Posso buscar por nome da empresa?**

Não no v0. A busca é por filtros estruturados: UF, município, CNAE, porte, situação, Simples, MEI, natureza jurídica, capital e data de abertura. Se você tem o nome mas não o CNPJ, a consulta individual é o caminho quando o CNPJ for conhecido; texto livre está fora do contrato atual.

**Por que a busca não vem no plano Free?**

Porque uma única busca pode percorrer milhares de empresas e a franquia do Free é feita para testar a integração. A busca está em todos os planos pagos; no Free ela responde `403 insufficient_plan` sem consumir crédito.

**Como sei quanto uma busca vai custar antes de rodar?**

Pela primeira página: `meta.total_count_capped` traz quantas empresas atendem aos filtros, exato abaixo do teto de exibição e um mínimo quando vem com `+`, e a cobrança é por empresa retornada. Se o número passar do que você quer gastar, refine os filtros antes de paginar.

**A ordem dos resultados muda entre páginas?**

Não. A ordenação é fixa por CNPJ e a paginação usa cursor, então uma empresa não aparece duas vezes nem some entre páginas enquanto os filtros forem os mesmos. Mudar um filtro invalida o cursor.

**A busca devolve telefone e e-mail?**

Não. A busca devolve o perfil `basic` de cada empresa, com os sinais `has_phone`, `has_email` e `has_website`. Para os contatos, consulte a empresa em `full`; o sinal evita pagar o custo do `full` por uma empresa sem contato.
