# Google Maps Local Leads — Email, Phone, Website & Brazil CNPJ (`paulovitor18/google-maps-local-leads`) Actor

Turn any Google Maps search into a contact-ready lead list: name, category, phone, website, email, full address and GPS. For Brazilian businesses it also resolves the CNPJ and the public registry. Pay per result.

- **URL**: https://apify.com/paulovitor18/google-maps-local-leads.md
- **Developed by:** [MoreLock](https://apify.com/paulovitor18) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

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

## Google Maps Local Leads — Email, Phone, Website & Brazil CNPJ

Turn any Google Maps search into a call-ready list. Type what you sell and where, and get back every local business with its phone, official website, email, full address with postal code and GPS — the contact details a salesperson actually dials, not just a name on a map.

Works on any Google Maps search worldwide. For businesses in Brazil it adds a layer no global scraper has: the **CNPJ** and the public company registry (legal name, registration status, size, activity, partners), read from the company's own website and resolved against the federal registry.

### Google Maps local leads in practice: what comes back per business

An agency selling to restaurants searches "restaurants" in "Florianopolis, Brazil" and gets a list its sales team can call today: name, category, rating, phone, website, email and socials — plus official Brazilian registry data when the website publishes a CNPJ. A real lead returned by the Actor:

A real row from run `1ikprscsHjTx6wcxC` (Austin, TX), trimmed to the fields most people use:

```json
{
  "name": "Corner Restaurant",
  "category": "Restaurant",
  "address": "110 E 2nd St, Austin, TX 78701",
  "postal_code": "78701",
  "region": "TX",
  "phone": "(512) 608-4488",
  "phone_digits": "5126084488",
  "website": "https://www.cornerrestaurantaustin.com/",
  "email": "forms@tambourine.com",
  "instagram": "https://instagram.com/corneratx",
  "facebook": "https://www.facebook.com/CornerATX/",
  "rating": 4.8,
  "reviews_count": 18272,
  "lat": 30.2642247,
  "lng": -97.7434837,
  "hydrated": true,
  "registry_enriched": false,
  "registry_note": "no_cnpj_on_site",
  "collected_at": "2026-09-07T01:20:50.643Z"
}
```

And a Brazilian row from run `VxDTZvv7c4AwZaMyL`, where the registry layer fires:

```json
{
  "name": "Drogaria Catarinense",
  "address": "R. Bocaiúva, 2375 - Centro, Florianópolis - SC, 88015-530, Brazil",
  "postal_code": "88015-530",
  "region": "SC",
  "phone": "+55 48 3037-4581",
  "website": "https://www.drogariacatarinense.com.br/",
  "email": "atendimento@drogariacatarinense.com.br",
  "cnpj": "84.683.481/0151-07",
  "legal_name": "CIA LATINO AMERICANA DE MEDICAMENTOS",
  "trade_name": "DROGARIA CATARINENSE",
  "registration_status": "ATIVA",
  "primary_activity": "4771701 - Comércio varejista de produtos farmacêuticos, sem manipulação de fórmulas",
  "registry_enriched": true,
  "match_confidence": "high",
  "registry_source": "brasilapi"
}
```

Every row also carries `place_id`, `ftid`, `gmid`, `place_url`, `sponsored`, `price_range`, `company_size`, `share_capital` and `partners`.

### Overview: how Google Maps lead extraction works

You give it a search term and a location — "restaurants" in "Austin, TX" — and it does what a rep would do by hand, faster. It reads the Maps result list, then opens each business's listing to pull the phone, the official website, the full street address and the exact coordinates. When the business has a site, it visits it once looking for an email address and social profiles.

A run of the defaults, measured on 2026-09-06: **25 businesses in 3 minutes**, 24 of them with both phone and website, all 25 with a postal code, 15 with an email address read from the company site.

The Brazilian layer runs on the same pass. If the site publishes a CNPJ, the number is validated by its check digits and resolved against the public registry, so the row arrives carrying the legal entity behind the storefront. On a Florianópolis pharmacy search, 4 of 14 businesses came back fully resolved — including `Drogaria Catarinense`, whose site maps to `CIA LATINO AMERICANA DE MEDICAMENTOS`, status `ATIVA`.

Outside Brazil that layer simply finds nothing, and **you are not charged for it**. That is the honest case, not a failure.

### Features: contact details, full address and Brazil CNPJ enrichment

- **Contact details, not just listings:** phone in both display and digits-only form, official website, email, Instagram, WhatsApp and Facebook when the site publishes them.
- **Full address, parsed:** street address, postal code and region as separate fields — Brazilian CEP, US ZIP and ZIP+4, Canadian and UK codes.
- **Brazilian registry enrichment:** CNPJ read from the company site, validated by check digits, resolved to legal name, registration status, company size, share capital, primary activity and partners.
- **Chain guard:** when one CNPJ shows up across several storefronts in the same run, it came from a shared corporate site — those rows are flagged `match_confidence: low` and the enrichment is charged **once**, not once per branch.
- **Honest abstention:** no website, or a site with no email and no CNPJ, means the field stays empty and that enrichment is **free**.
- **A block is never sold as an empty result:** if Google serves a blocked page, the run escalates to a residential route and, if that comes back blind too, **fails saying so** instead of handing you an empty dataset that reads like "no businesses here".
- **Survives platform migration:** progress is checkpointed, so a run moved to another worker resumes instead of restarting — and never delivers or bills the same business twice.

### Input example: search term and location

```json
{
  "search_term": "restaurants",
  "location": "Austin, TX",
  "max_results": 25,
  "hydrate_details": true,
  "max_hydrations": 25,
  "enrich_from_website": true,
  "proxy": { "useApifyProxy": true }
}
```

### Parameters: term, location, detail hydration and enrichment

| Field | Type | Default | Description |
|---|---|---|---|
| `search_term` | string | `"restaurants"` | What to look for, exactly as you would type it into Maps. |
| `location` | string | `"Austin, TX"` | City and area. Any country; the search follows what you type. |
| `max_results` | integer | `25` | Cap of businesses delivered (cost control). Maps lists ~120 per search; narrow term and city to sweep beyond that. Max 150. |
| `hydrate_details` | boolean | `true` | Opens each listing for phone, website, full address, postal code and GPS. This is the step that makes a lead. Turn it off for a fast name-and-rating sweep. |
| `max_hydrations` | integer | `25` | Cap of listings opened — the time bottleneck, about 9s each. The default keeps a run inside Apify's 5-minute health check. Max 100. |
| `enrich_from_website` | boolean | `true` | Visits the business site once for email, social links and, in Brazil, the CNPJ, then queries the registry. |
| `proxy` | object | Apify Proxy | Rotating datacenter by default, measured returning 200 on Maps with no captcha. Switch to Residential and a nearby country if you hit blocking at volume. |
| `self_test` | boolean | `false` | Diagnostics only. Runs the frozen known-answers battery instead of a search. |

### Tips to get more leads for less

- **Narrow beats broad.** "dental clinics" + "Round Rock, TX" gives a list you can work through today; "business" + "Texas" gives you noise.
- **Turn hydration off to survey, on to sell.** Without it a run is fast and cheap and tells you how many businesses exist in a niche; with it you get the phone numbers.
- **Raise `max_hydrations` deliberately.** Each listing costs about 9 seconds. Sixty listings is a nine-minute run — fine when you launch it yourself, too slow for a 5-minute schedule.
- **Email rates vary by trade.** Restaurants and clinics publish contact addresses; trades and one-person shops often publish only a phone. You pay for the business either way, never for an email that was not there.
- **Read `match_confidence` on Brazilian rows.** `low` means the CNPJ came from a shared corporate site and may describe the group rather than that exact storefront.

### Use cases: B2B prospecting, market research and local SEO

- **Local B2B prospecting:** build a call list of every restaurant, clinic, gym or contractor in a city, with phone and website already attached.
- **Agency lead lists:** hand a client a fresh, verified list for their service area instead of reselling a stale database.
- **Territory and market sizing:** count and map how many businesses of a type operate in an area before committing a rep or a store to it.
- **CRM enrichment:** feed names you already have back through Maps to attach phone, site, coordinates and postal code.
- **Brazilian KYB and due diligence:** go from a storefront on the map to the legal entity, its registration status and its partners, without asking the merchant anything.
- **Franchise and expansion research:** measure how dense a category already is per neighborhood before picking the next location.

### FAQ about scraping local leads from Google Maps

**Does this work outside Brazil?** Yes — that is the default. Business discovery, contact details, address parsing and website enrichment work on any Google Maps search worldwide. The CNPJ and registry layer is a bonus that applies only to Brazilian companies; elsewhere those fields stay empty and cost you nothing.

**Where does the email come from?** From the business's own website, which the Actor visits once and reads in the pages where companies publish contact details. It is never guessed, never assembled from a name and a domain, and never taken from a third-party database. If the site publishes no address, the field is empty.

**Why is `email` empty on some rows?** Because that business either has no website or does not publish an address on it. About 3 in 5 rows carried an email in the Austin run. Reporting the gap honestly is the point: you are charged for the business, not for a fabricated contact.

**What is `registry_enriched`?** It is `true` only when a real CNPJ was found on the company's site and actually resolved in the Brazilian public registry. It is also the billing unit — a CNPJ that is found but does not resolve is delivered as information and **not charged**.

**A chain has 20 branches on one CNPJ. Do I pay 20 times?** No. A CNPJ that repeats within a run is charged **once**, and every affected row is labelled `match_confidence: low` so you know the number describes the group and not that specific branch.

**What happens if the search legitimately has no businesses?** That is a valid answer, not an error. The run finishes successfully with an empty dataset and charges nothing. Zero results are treated as real only when Google positively said so — a "no results" banner, a locality-only card, or a partial-match notice.

**And if Google blocks the crawl?** The run escalates to a residential route. If that comes back blind as well, the run **fails and names the reason** rather than finishing "successfully" with nothing in it. A block sold to you as an empty result is the failure mode this Actor was built to refuse.

**How long does a run take?** About 3 minutes on the defaults — 25 businesses fully hydrated, measured at 181 seconds. Time is dominated by opening listings: budget roughly 9 seconds per business you hydrate.

**Can I schedule it?** You can, but if what you want is a recurring feed of businesses that are *new* to a search rather than the whole list every time, use the monitor listed below instead: it charges for the change, not for the rescan.

### Pricing: what Google Maps lead extraction costs

Pay per result (PPE). You pay for businesses delivered and for registry data actually resolved — never for a run, a retry, or an empty search.

| Event | Price | When it is charged |
|---|---|---|
| `place_scraped` | $0.008 | Each business delivered to the dataset, with name, category, phone, website, email, rating, address and GPS. |
| `cnpj_enriquecido` | $0.005 | Each **unique** CNPJ found on a business site and resolved in the Brazilian registry. A chain sharing one CNPJ is charged once. |

A default run of 25 businesses outside Brazil costs **$0.20**. The same 25 in Brazil with 8 companies resolved costs **$0.24**.

**You are NOT charged for:** an empty search, a blocked or failed run, a business whose site has no email or no CNPJ, a CNPJ that does not resolve in the registry, or the same business twice after a platform migration.

### MoreLock — the Brazil data suite

This Actor is part of **MoreLock**, a collection of Brazil-data Actors built to join on the CNPJ. People who run this one usually pair it with:

| Actor | What it adds to your workflow |
|---|---|
| [Google Maps New Business Monitor — Fresh Local Leads](https://apify.com/paulovitor18/google-maps-new-business-monitor) | Tells you when a new business opens inside the area you watch. |
| [Brazil Company Data — Bulk CNPJ Lookup & Enrichment](https://apify.com/paulovitor18/brazil-company-data-enrichment) | Fills in the registry: legal name, shareholders, CNAE, status and an automatic risk score from the CNPJ. |
| [Brazil Due Diligence — CNPJ Registry + Reclame Aqui](https://apify.com/paulovitor18/brazil-company-due-diligence) | Registry data and Reclame Aqui reputation joined into one record per CNPJ. |

🌎 **Same product, other languages:** [🇧🇷 Portuguese version — Google Maps Brasil — Leads Locais com E-mail, Telefone e CNPJ](https://apify.com/paulovitor18/gmaps-brasil-leads) · [🇪🇸 Spanish version — Leads Locales de Google Maps — Email, Teléfono, Web y CNPJ](https://apify.com/paulovitor18/google-maps-leads-locales)

**[See all 26 MoreLock Actors →](https://apify.com/paulovitor18)**

### Changelog

- **0.1** — first version: Google Maps search to contact-ready leads, with detail hydration (phone, website, postal code, GPS), website enrichment (email and social links), the Brazilian CNPJ and registry layer with a chain guard, positive-proof handling of empty versus blocked searches, and billing that stays idempotent across platform migrations.

### Contact

Questions, bugs, or a source you would like added: use the Actor's Issues tab.

# Actor input Schema

## `search_term` (type: `string`):

What to look for on Google Maps, exactly as you would type it (e.g. "restaurants", "dental clinics", "pharmacies").

## `location` (type: `string`):

City and area to search (e.g. "Austin, TX", "Berlin", "São Paulo, SP"). The more specific, the sharper the result. Works on any Google Maps search worldwide.

## `max_results` (type: `integer`):

Cap of businesses delivered in this run (cost control). Google Maps usually lists ~120 per search; narrow the term and city to sweep beyond that. For large sweeps, prefer turning the detail step OFF (below) — without it the crawl is fast even with many results.

## `hydrate_details` (type: `boolean`):

Opens the detail listing of every business to pull phone, website, full address, postal code and precise GPS. This is the step that produces a real lead, and it is the most expensive one (one extra page load per business). Turn it off for a fast sweep with only name, category, rating and approximate location.

## `max_hydrations` (type: `integer`):

Limits how many detail listings are opened (the expensive step above). It is the time bottleneck: each listing is one page load, measured at ~9s. The default (25) is set so a default run finishes well inside Apify's 5-minute daily health check; raise it up to 100 for bigger sweeps, and run in batches by term and city beyond that.

## `enrich_from_website` (type: `boolean`):

When the business has a website, visits it once to find email, social links and — for Brazilian businesses — the CNPJ. With a valid CNPJ the public registry is queried (legal name, registration status, company size, share capital, activity, partners). It is best-effort: many sites do not publish contact details in scrapable text, and outside Brazil there is no CNPJ to find. In those cases the field comes back empty and you are NOT charged for the enrichment.

## `proxy` (type: `object`):

Network routing. The default (Apify Proxy, rotating datacenter) was measured returning 200 on Google Maps with no captcha. If you see blocking or a consent wall at volume, select Residential and a country close to your target area.

## `self_test` (type: `boolean`):

Not for production. Skips the search and runs the known-answers battery (frozen feed parsing, empty search, website enrichment, honest abstention, block-is-not-empty) to prove that the parser and the network egress are still correct. Emits a single diagnostic record.

## Actor input object example

```json
{
  "search_term": "restaurants",
  "location": "Austin, TX",
  "max_results": 25,
  "hydrate_details": true,
  "max_hydrations": 25,
  "enrich_from_website": true,
  "proxy": {
    "useApifyProxy": true
  },
  "self_test": false
}
```

# Actor output Schema

## `leads` (type: `string`):

Businesses delivered in this run, in the format described by dataset\_schema.json.

## `summary` (type: `string`):

Run counters: searched, delivered, hydrated, enriched, abstentions.

# 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 = {
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("paulovitor18/google-maps-local-leads").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 = { "proxy": { "useApifyProxy": True } }

# Run the Actor and wait for it to finish
run = client.actor("paulovitor18/google-maps-local-leads").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 '{
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call paulovitor18/google-maps-local-leads --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,paulovitor18/google-maps-local-leads"
        }
    }
}

```

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/6RB0LaUI5qWrLpiNH/builds/IHMd1ycqOAFKkcz3a/openapi.json
