# Brazil CNPJ Scraper & Bulk Lookup - Receita Federal Data (`neverempty/brazil-cnpj-scraper`) Actor

Bulk CNPJ lookup for Brazilian companies: legal name, status, CNAE, capital, address, phones, Simples/MEI and shareholders from Receita Federal open data. Check digits are verified first, so a typo is never reported as "company not found".

- **URL**: https://apify.com/neverempty/brazil-cnpj-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.84 / 1,000 company returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Brazil CNPJ Scraper & Bulk Lookup

Look up Brazilian companies by CNPJ in bulk and get one row per company: legal name (razão social), trade name
(nome fantasia), registration status (situação cadastral), opening date, main and secondary CNAE activities,
legal nature (natureza jurídica), company size (porte), share capital (capital social), full address, phone
numbers, Simples Nacional and MEI status, the tax regime by year, and the shareholder and officer structure
(quadro societário, QSA). The data is the Receita Federal open CNPJ data, read through the public Minha Receita
API. No API key, no proxy, no scraping of the Receita Federal website.

```json
{
  "cnpjs": ["33.000.167/0001-01", "00.000.000/0001-91", "33592510000154"]
}
```

Paste CNPJs with or without separators, one per line or separated by commas. Alphanumeric CNPJs, issued since
July 2026, are accepted.

### What this does that a plain lookup does not

Everything below was measured against the live source on 2026-09-11, not read from documentation. The sample
counts come from 450 real records the source returned that day; they are kept in this Actor's test fixtures and
recounted by its tests.

#### 1. A typo is never reported as "company not found"

The source answers a malformed number or wrong check digits with HTTP 400 `CNPJ ... inválido`, and a valid
number that is not in the register with HTTP 404 `CNPJ ... não encontrado`. Many tools collapse both into "not
found", so a mistyped digit in your spreadsheet turns into "this supplier does not exist".

Here the check digits are verified before anything is sent:

| What you give | What you get | Charged |
|---|---|---|
| `33.000.167/0001-02` (last digit wrong) | `bad-input`: "its check digits do not match (they should be 01, not 02)" - not sent to the source | No |
| `191` (a spreadsheet dropped the zeros) | `bad-input`, suggesting `00.000.000/0001-91` | No |
| `98.765.432/0001-98` (valid, not registered) | `not-found`, naming the data release it was checked against | No |
| A valid number the source still refuses | `rejected-by-source`, with the source's own message | No |
| The source does not answer (no response, 429, 5xx) | Retried; if it still fails, `failed` with every status seen | No |
| The same CNPJ twice, formatted differently | Looked up once, charged once; the second gets `duplicate-input` | No |

#### 2. Alphanumeric CNPJs (July 2026 onwards)

New CNPJs can contain letters in the first 12 characters, and their check digits are computed from each
character's ASCII code minus 48. A validator that only knows digits calls every new number "invalid". The
Receita Federal example `12.ABC.345/01DE-35` is accepted here, and the source confirmed the same rule: it returns
404 (well-formed, not registered) for that number and 400 once the last digit is changed.

#### 3. Personal data is left out (LGPD)

- **Shareholders who are natural persons are never named.** Their name, masked CPF, age band and country are not
  in the output. For each shareholder you get its type (`person`, `company` or `foreign-person`), its role (for
  example Sócio-Administrador) and the date it joined. Company shareholders, including foreign parents such as
  GOOGLE INTERNATIONAL LLC, are named with their 8-digit CNPJ root and country.
- **Legal representatives are never named.** The register lists the person who represents each shareholder; in
  the sample that included the mother of a shareholder who is a minor. None of these fields are returned.
- **Individual entrepreneurs (Empresário Individual, including MEI) are people, and their rows are delivered and
  charged with the personal fields left empty.** Their legal name is their own name. In an unfiltered sample of
  200 registrations from Rio de Janeiro, 115 of 200 were registered to an individual person (60 of them MEI), and
  55 of those had the owner's full, unmasked CPF in the legal name (for example "NAME SURNAME 12345678901").
  For every registration whose legal nature is an individual person (Empresário Individual including MEI, or the
  4xxx codes such as election candidates), the row still comes back with `status` = `ok` and **is charged like
  any other company row**, but the legal name, phone numbers, street, number, complement and postal code are
  `null` and `personalDataWithheld` is `true`. The trade name is returned only when it shares no word with the
  owner's name. On those rows these columns keep their real values: registration status and `isActive`, main and
  secondary CNAE activities, opening date, city, state and neighborhood, legal nature and company size, Simples
  Nacional status and dates, and whether it is an MEI (`mei`, `meiOptedOn`, `meiExcludedOn`). If what you need
  is the owner's name or phone number, this Actor does not provide it for these registrations.
- Any 11-digit CPF-shaped number is also removed from company names.
- A company's registered name is returned as it is registered, even when it is built from a partner's name
  (for example "NAME SURNAME LTDA"): that happened in 9 of 450 sampled records. It is the company's legal name,
  not a list of its people, and the partner is still not named in the shareholder list.

#### 4. Values the raw data gets wrong

- **CNAE codes lose their leading zero.** The source stores them as numbers, so `0600-0/01` (oil and gas
  extraction) arrives as `600001`; 4 of 450 sampled records were affected. Codes are returned as 7-digit strings
  plus the formatted `0600-0/01`.
- **Share capital of 0 means "not declared".** 57 of 450 sampled records hold 0, including 37 limited companies,
  which by law have capital. 0 is returned as `null`, not as a company worth nothing.
- **Phones.** 284 of 450 sampled records have a usable first phone number. Values such as `00` are dropped. The
  `+55` form is added only for 10- or 11-digit numbers.
- **Email is not available.** The source publishes no email address: 0 of 450 sampled records had one. There is
  no email column rather than an always-empty one.

#### 5. The source is a free public service, so it is used gently

Minha Receita is a volunteer-run public API with no service-level guarantee. Lookups are made one at a time, never
in parallel, with a 0.3-second pause between them. HTTP 429 and 5xx answers are retried after 2, 5, 10 and 20
seconds, or after the time the source asks for. In a test of 60 back-to-back lookups every answer was HTTP 200 in
about 0.27 seconds each; no rate limit was hit. In production a lookup takes about 0.6 seconds including the
pause, so one run looks up at most 3,000 CNPJs (about half an hour), which keeps it inside Apify's default
one-hour run limit. Anything beyond 3,000 is not looked up and is listed in an uncharged row saying how many.

### What you get

Company: `cnpj`, `cnpjFormatted`, `cnpjRoot`, `establishmentType` (headquarters or branch), `legalName`,
`tradeName`, `personalDataWithheld`, `legalNatureCode`, `legalNature`, `companySize`, `companySizeCode`,
`shareCapitalBrl`, `openedOn`.

Status: `registrationStatus` (ATIVA, BAIXADA, INAPTA, SUSPENSA, NULA), `registrationStatusCode`, `isActive`,
`registrationStatusSince`, `registrationStatusReason`, `specialSituation`, `specialSituationSince`.

Activities: `mainActivityCode`, `mainActivityCodeFormatted`, `mainActivity`, `secondaryActivities` (code,
formatted code, description), `secondaryActivityCount`.

Tax: `simplesNacional`, `simplesOptedOn`, `simplesExcludedOn`, `mei`, `meiOptedOn`, `meiExcludedOn`,
`latestTaxRegime` (for example LUCRO REAL, LUCRO PRESUMIDO), `latestTaxRegimeYear`, `taxRegimeHistory`.

Address and contact: `streetType`, `street`, `number`, `complement`, `neighborhood`, `postalCode`, `city`,
`cityIbgeCode`, `state`, `cityAbroad`, `countryAbroad`, `phone1`, `phone1International`, `phone2`,
`phone2International`, `fax`.

Shareholders: `shareholderCount`, `shareholderNaturalPersons`, `shareholderCompanies`,
`shareholderForeignNationals`, `shareholders`.

Every row also has `status`, `note`, `input` (what you typed), `sourceDataMonth` (the Receita Federal release
the source holds, for example `2026-08`), `sourceUrl` and `checkedAt`.

Only rows with `status` = `ok` are charged. Rows that explain an invalid number, a number not in the register, a
refusal, a failed lookup, a repeated input or an input that was not looked up are free.

### Input

| Field | What it does |
|---|---|
| `cnpjs` | CNPJ numbers, with or without `.` `/` `-`. Several per line are split on commas, semicolons and line breaks. Up to 3,000 per run. If left empty, Petrobras, Banco do Brasil and Vale are looked up, and the log says so. |
| `maxRetries` | Attempts per CNPJ in total when the source does not answer (1 to 5, default 4). |

`cnpjs` carries no default value, so nothing is ever added to your list that you did not type.

### Limits worth knowing

- The data is as current as the Receita Federal release the source has loaded (`sourceDataMonth`). A company
  registered after that release comes back as `not-found`, and the note says so.
- Values are returned as the source holds them. Share capital matched a second public CNPJ API for Vale, Banco do
  Brasil and Itaú Unibanco; for Petrobras the two differed by R$ 490.52 (205,431,960,000 here against
  205,431,960,490.52 there).
- The shareholder list is the one the source holds for the current release. Another API can list more names for
  the same company (for Banco do Brasil 41 here against 88 there), for example by keeping people who have left.

### Source and licence

Receita Federal do Brasil publishes the CNPJ register as open data under the Brazilian Access to Information Law
(Lei de Acesso à Informação). Minha Receita (`minhareceita.org`, open-source code on Codeberg) consolidates those
files and serves one company per request. This Actor makes plain HTTPS requests to that API. It does not use the
Receita Federal website, its CAPTCHA-protected pages or the bulk download files.

### Support

Found a CNPJ that comes back wrong? Open an issue in the Issues tab with the number and what you expected.

# Actor input Schema

## `cnpjs` (type: `array`):

One CNPJ per line, with or without separators: 33.000.167/0001-01 and 33000167000101 are the same company. Alphanumeric CNPJs issued since July 2026 are accepted. Check digits are verified before anything is sent, so a mistyped number comes back as an invalid input with the reason, never as "company not found". A number given twice is looked up and charged once. Up to 3,000 per run, so one run finishes within Apify's default one-hour limit; any beyond that are listed in an uncharged row as not looked up.

## `maxRetries` (type: `integer`):

How many times to try one lookup in total when the source does not answer (no response, HTTP 429 or 5xx). Waits 2, 5, 10 and 20 seconds between attempts, or what the source asks for.

## Actor input object example

```json
{
  "cnpjs": [
    "33.000.167/0001-01",
    "00.000.000/0001-91",
    "33.592.510/0001-54"
  ],
  "maxRetries": 4
}
```

# Actor output Schema

## `results` (type: `string`):

One row per CNPJ: the company's registration data, or an uncharged row saying why there is none (invalid number, not in the registry, source did not answer, repeated input).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "cnpjs": [
        "33.000.167/0001-01",
        "00.000.000/0001-91",
        "33.592.510/0001-54"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/brazil-cnpj-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "cnpjs": [
        "33.000.167/0001-01",
        "00.000.000/0001-91",
        "33.592.510/0001-54",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/brazil-cnpj-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "cnpjs": [
    "33.000.167/0001-01",
    "00.000.000/0001-91",
    "33.592.510/0001-54"
  ]
}' |
apify call neverempty/brazil-cnpj-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/brazil-cnpj-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ot3XbQS2Llk1Ml6zi/builds/ap1ueD1dPW01Zd4Hx/openapi.json
