# CNPJ Scraper (`aurenic/cnpj-scraper`) Actor

Find Brazilian companies by CNAE + state + municipality and enrich with full Receita Federal data. 55M+ companies, municipality iteration past the 20-result cap, BrasilAPI/Minha Receita/OpenCNPJ failover. Keyless, no browser, no proxy.

- **URL**: https://apify.com/aurenic/cnpj-scraper.md
- **Developed by:** [Aurenic](https://apify.com/aurenic) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Flights Scraper

Scrape Google Flights for live fares, airlines, times, stops, duration, aircraft and CO2 emissions by route and date — keyless, no API key, no browser.

### What does Google Flights Scraper do?

This actor reverse-engineers the search payload that Google Flights inlines into its own public results page. It builds the same base64url Protobuf `tfs` parameter the website uses, fetches the results page with a Chrome TLS fingerprint, and decodes the embedded `ds:1` data blob into structured itinerary records. It covers one-way and round-trip searches across any cabin class, passenger mix and stop ceiling, and returns every itinerary Google inlines for that route — typically 15 to 150 options on a busy route.

### Output fields

**One record per itinerary**

| Field | Type | Description |
|---|---|---|
| origin | string | Origin IATA code from your input |
| destination | string | Destination IATA code from your input |
| departure\_date | string | Departure date (YYYY-MM-DD) |
| return\_date | string | Return date, or null for one-way |
| trip\_type | string | `one-way` or `round-trip` |
| cabin | string | Cabin class priced |
| price | number | Total fare in the requested currency, or null when Google has not pre-computed one |
| currency | string | ISO currency code |
| airlines | array | Unique marketing carrier IATA codes |
| airline\_name | string | Primary operating airline display name |
| flight\_numbers | array | Carrier + number per leg, e.g. `["AA2413"]` |
| stops | integer | Number of stops (0 = non-stop) |
| duration\_minutes | integer | Total journey duration in minutes |
| departure\_time | string | First leg departure, ISO 8601 local time |
| arrival\_time | string | Final leg arrival, ISO 8601 local time |
| legs | array | Per-leg detail (see below) |
| layovers | array | Per-stop airport, city and wait time, or null |
| co2\_emissions\_g | integer | Estimated CO2 for this itinerary, grams |
| co2\_emissions\_typical\_g | integer | Typical CO2 for the route, grams |
| co2\_emissions\_delta\_pct | integer | Percentage difference from typical |
| emissions\_tag | string | `lower`, `typical` or `higher` |
| is\_best | boolean | Whether Google flagged this as a best result |
| result\_group | string | `best` or `other` |
| booking\_token | string | Opaque token Google uses to deep-link this itinerary |
| source\_search | string | Which input search produced this row |
| scraped\_at | string | ISO timestamp |

**Leg object**

| Field | Type | Description |
|---|---|---|
| airline | string | Marketing carrier IATA code |
| flight\_number | string | Flight number |
| operating\_airline | string | Operating carrier, when different |
| departure\_airport | string | Origin IATA |
| arrival\_airport | string | Destination IATA |
| departure\_airport\_name | string | Airport display name |
| arrival\_airport\_name | string | Airport display name |
| departure\_time | string | ISO 8601 local time |
| arrival\_time | string | ISO 8601 local time |
| duration\_minutes | integer | Leg duration |
| aircraft | string | Aircraft type, when Google publishes it |
| legroom | string | Seat pitch, e.g. `31 in` |
| cabin | string | Cabin for this leg |
| overnight | boolean | Whether the leg arrives the next day |
| co2\_emissions\_g | integer | Leg-level CO2 estimate |

### Who is it for?

- **Fare-monitoring teams** tracking a fixed route set daily and alerting on price drops
- **Travel agencies** benchmarking negotiated fares against the public Google Flights price
- **Corporate travel teams** enforcing booking-window policy across a route catalogue
- **Fare-comparison and metasearch products** needing a cheap structured price feed
- **Airline and airport analysts** studying pricing volatility, stop patterns and capacity
- **Data teams** building route-level price history for forecasting or emissions reporting

### Pricing

**Pay per result: $0.15 per 1,000 flight itineraries.**

You are charged only for itineraries actually written to the dataset. A run that returns nothing is not billed.

### How to use it

1. Open the actor in Apify Console.
2. Set your route: either fill in **Origin**, **Destination** and **Departure date**, or use the **Search specs** list to run several routes in one run.
3. For a round trip, fill in **Return date**. Leave it empty for one-way.
4. Choose the cabin class, passenger mix and maximum stops.
5. Set currency, country and language to match the market you want to price.
6. Optionally cap results per search to control cost.
7. Leave the proxy at the default and click **Start**. Export as JSON, CSV or Excel.

**Search spec format:** `JFK>LAX>2026-11-15` for one-way, `JFK>LAX>2026-11-15>2026-11-22` for round-trip.

### Output example

```json
{
  "origin": "JFK",
  "destination": "LAX",
  "departure_date": "2026-11-15",
  "return_date": null,
  "trip_type": "one-way",
  "cabin": "economy",
  "price": 189,
  "currency": "USD",
  "airlines": ["AA"],
  "airline_name": "American Airlines",
  "flight_numbers": ["AA2413"],
  "stops": 0,
  "duration_minutes": 380,
  "departure_time": "2026-11-15T08:15:00",
  "arrival_time": "2026-11-15T11:35:00",
  "legs": [
    {
      "airline": "AA",
      "flight_number": "2413",
      "operating_airline": null,
      "departure_airport": "JFK",
      "arrival_airport": "LAX",
      "departure_airport_name": "John F. Kennedy International Airport",
      "arrival_airport_name": "Los Angeles International Airport",
      "departure_time": "2026-11-15T08:15:00",
      "arrival_time": "2026-11-15T11:35:00",
      "duration_minutes": 380,
      "aircraft": "Airbus A321neo",
      "legroom": "31 in",
      "cabin": "economy",
      "overnight": false,
      "co2_emissions_g": 312000
    }
  ],
  "layovers": null,
  "co2_emissions_g": 312000,
  "co2_emissions_typical_g": 340000,
  "co2_emissions_delta_pct": -8,
  "emissions_tag": "lower",
  "is_best": true,
  "result_group": "best",
  "booking_token": "CjRIQ...",
  "source_search": "JFK->LAX 2026-11-15",
  "scraped_at": "2026-09-28T12:00:00.000Z"
}
```

### Technical details

- **Stack:** Node.js 24, `apify` SDK, `impit` HTTP client with Chrome TLS impersonation, `tough-cookie` for the consent cookie.
- **Transport:** the public Google Flights results page. Google's `GetShoppingResults` RPC endpoint has been gated since August 2026 by an `x-goog-batchexecute-bgr` header that only the page's own JavaScript can produce, so this actor reads the same rows from the page's inline `ds:1` data blob instead.
- **Encoding:** the `tfs` URL parameter is a hand-rolled Protobuf message carrying trip type, per-direction date, origin/destination airports, cabin, stop ceiling and passenger codes. The encoder is 40 lines with no protobuf runtime dependency.
- **Consent:** a pre-accepted `SOCS` cookie is sent on every request, which skips Google's EU consent interstitial.
- **Retries:** a 200 response that carries no `ds:1` blob is a known transient page variant and is retried up to three times with backoff. HTTP 429 and 503 are also retried; other HTTP errors fail fast.
- **Proxy:** defaults to the Apify datacenter pool, which is sufficient when the request carries a Chrome TLS fingerprint. Residential proxy is available by changing the proxy input.

### Known limits

- **Multi-city itineraries are not supported.** Google loads multi-city results client-side through the gated RPC, so the page carries no rows to read. Search each leg separately.
- **Page 1 only.** Google inlines every row it is willing to show for a route on one page. There is no deeper pagination to fetch, so a search returns what Google shows a browser on first load — typically 15 to 150 itineraries.
- **Premium-cabin and child fares are often unpriced.** Google prices parties with children or infants client-side and frequently omits an aggregate fare for premium-cabin round trips. Those rows come through with `price: null` and a populated `booking_token`.
- **Prices are snapshots.** Google's fares move on inventory buckets and can change within minutes. Treat each row as a point-in-time observation.
- **Low-traffic airports may return fewer rows.** The actor sets the on-demand pricing flag Google uses for regional airports, but very thin routes can still come back with little or nothing.
- **Google can change the page shape.** The `ds:1` blob key and the row index layout are reverse-engineered. If Google reshapes the payload, the actor fails loudly with the marker line in the log rather than silently returning wrong data.

### FAQ

**Do I need a Google account or API key?**
No. The actor reads the same public results page a logged-out browser sees. No login, no token, no developer account.

**Why is there no official Google Flights API?**
Google retired the public QPX Express API in April 2018 and now offers airfare data only through enterprise contracts. Scraping the consumer site is the only programmatic route for everyone else.

**Why does my run return zero results?**
Open the run log and find the `ds:1` marker line. If it shows a consent interstitial, a sorry page or a captcha, switch the proxy input to residential and re-run. If it shows `AF_initDataCallback=true` but `key 'ds:1'=false`, Google has reshaped the page and the actor needs an update.

**Can I get business class prices?**
Yes — set the cabin class to business. Note that Google often omits an aggregate price for premium-cabin round trips, so you may see `price: null` on those rows even though the itinerary is returned.

**How many flights do I get per search?**
One page load, which is everything Google inlines: typically 15 to 30 on a thin route, 100 or more on a busy one. There is no way to fetch more for the same search without changing the search.

**Does it support one-way and round-trip?**
Yes, both. Round-trip prices are the combined return fare. Multi-city is not supported — see Known limits.

### Support

Open an issue on the Actor's page for bugs or feature requests.

### Changelog

#### 0.1 — 2026-09-28

- Initial build. Keyless extraction of Google Flights results from the public search page.
- Hand-rolled Protobuf `tfs` encoder (trip type, dates, airports, cabin, stops, passengers) with no protobuf runtime dependency.
- `AF_initDataCallback` `ds:1` payload extraction and full itinerary decoding: price, currency, per-leg airline, flight number, airports, times, aircraft, legroom, cabin, layovers, CO2 and emissions tag.
- SOCS consent cookie sent on every request to skip the EU interstitial.
- Retry on the transient no-`ds:1` page variant, on HTTP 429 and on HTTP 503.
- One-way and round-trip support; multi-city explicitly rejected with a clear message.
- Single-route fields plus a `searches` list for multi-route runs in one execution.
- Defaults to Apify datacenter proxy; residential available via the proxy input.

### Changelog

#### 0.1 — 2026-09-28

- Initial build. Keyless extraction of Google Flights results from the public search page.
- Hand-rolled Protobuf `tfs` encoder (trip type, dates, airports, cabin, stops, passengers) with no protobuf runtime dependency.
- `AF_initDataCallback` `ds:1` payload extraction and full itinerary decoding: price, currency, per-leg airline, flight number, airports, times, aircraft, legroom, cabin, layovers, CO2 and emissions tag.
- SOCS consent cookie sent on every request to skip the EU interstitial.
- Retry on the transient no-`ds:1` page variant, on HTTP 429 and on HTTP 503.
- One-way and round-trip support; multi-city explicitly rejected with a clear message.
- Single-route fields plus a `searches` list for multi-route runs in one execution.
- Defaults to Apify datacenter proxy; residential available via the proxy input.

### Changelog

#### 0.1 — 2026-09-28

- Initial build. Keyless extraction of Google Flights results from the public search page.
- Hand-rolled Protobuf `tfs` encoder (trip type, dates, airports, cabin, stops, passengers) with no protobuf runtime dependency.
- `AF_initDataCallback` `ds:1` payload extraction and full itinerary decoding: price, currency, per-leg airline, flight number, airports, times, aircraft, legroom, cabin, layovers, CO2 and emissions tag.
- SOCS consent cookie sent on every request to skip the EU interstitial.
- Retry on the transient no-`ds:1` page variant, on HTTP 429 and on HTTP 503.
- One-way and round-trip support; multi-city explicitly rejected with a clear message.
- Single-route fields plus a `searches` list for multi-route runs in one execution.
- Defaults to Apify datacenter proxy; residential available via the proxy input.

# Actor input Schema

## `mode` (type: `string`):

What to do.

## `cnaes` (type: `array`):

7-digit CNAE activity codes. Common: 5611201 (restaurants), 6201501 (custom software), 4711301 (supermarkets), 8630504 (medical clinics). Full list at cnae.ibge.gov.br.

## `ufs` (type: `array`):

Brazilian state codes (SP, RJ, MG, BA, etc.). Leave empty for all Brazil.

## `municipios` (type: `array`):

Optional specific cities to query. Leave empty to iterate EVERY municipality in each UF automatically (slow for large states — can hit Apify's default 300s run timeout). Default input uses 5 small SP municipalities so the run completes fast.

## `situacao` (type: `string`):

Filter by cadastral status.

## `iterateMunicipalities` (type: `boolean`):

Automatically iterate every municipality in each UF. This is what turns the 20-results-per-query cap into thousands per CNAE. Municipality list is fetched free from IBGE.

## `enrich` (type: `boolean`):

After discovery, fetch the full Receita Federal record for each CNPJ (phone, email, capital, QSA). Uses BrasilAPI → Minha Receita → OpenCNPJ failover. Adds ~1 request per company.

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

CNPJ numbers to look up. Used in enrich mode. Digits only or formatted.

## `maxPerMunicipality` (type: `integer`):

Companies per municipality query. The public Casa dos Dados endpoint caps at 20.

## `maxItems` (type: `integer`):

Hard cap on records per run. Default 100 keeps test runs under 2 minutes; raise to 10000+ for production bulk extraction.

## `requestDelayMs` (type: `integer`):

Delay between requests. Free mirrors have no published limit; 300ms is polite.

## Actor input object example

```json
{
  "mode": "discover",
  "cnaes": [
    "5611201"
  ],
  "ufs": [
    "SP"
  ],
  "municipios": [
    "Adamantina",
    "Adolfo",
    "Agudos",
    "Americana",
    "Amparo"
  ],
  "situacao": "ATIVA",
  "iterateMunicipalities": true,
  "enrich": true,
  "cnpjs": [],
  "maxPerMunicipality": 20,
  "maxItems": 100,
  "requestDelayMs": 300
}
```

# Actor output Schema

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

No description

# 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 = {
    "cnaes": [
        "5611201"
    ],
    "ufs": [
        "SP"
    ],
    "municipios": [
        "Adamantina",
        "Adolfo",
        "Agudos",
        "Americana",
        "Amparo"
    ],
    "cnpjs": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("aurenic/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 = {
    "cnaes": ["5611201"],
    "ufs": ["SP"],
    "municipios": [
        "Adamantina",
        "Adolfo",
        "Agudos",
        "Americana",
        "Amparo",
    ],
    "cnpjs": [],
}

# Run the Actor and wait for it to finish
run = client.actor("aurenic/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 '{
  "cnaes": [
    "5611201"
  ],
  "ufs": [
    "SP"
  ],
  "municipios": [
    "Adamantina",
    "Adolfo",
    "Agudos",
    "Americana",
    "Amparo"
  ],
  "cnpjs": []
}' |
apify call aurenic/cnpj-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aurenic/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/gdwHp2tQwK6LxY5YW/builds/cN0Jc2LZdpi7wSWFW/openapi.json
