# Google Maps Scraper — Local Leads & Emails (`pequod-labs/google-maps-local-leads`) Actor

Google Maps scraper for local lead generation: phone, address, rating, hours and Maps URL. Optional website emails and decision-maker enrichment. Country sweep (IT, FR, DE, ES, GB, NL, BE, PT, US). Pay per business; contacts billed only when found. No proxy setup.

- **URL**: https://apify.com/pequod-labs/google-maps-local-leads.md
- **Developed by:** [Pequod](https://apify.com/pequod-labs) (community)
- **Categories:** Lead generation
- **Stats:** 6 total users, 4 monthly users, 98.5% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.60 / 1,000 business scrapeds

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/platform/actors/running/actors-in-store#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 Scraper — Local Leads & Emails

### What is a Google Maps scraper for local lead generation?

A **Google Maps scraper** is a tool that searches Maps by keyword and area, then exports structured **local business leads** — phone, address, rating, hours and Maps URL — into a dataset you can download as **CSV or JSON**. This **Apify** Actor is built for lead generation and optional **emails** from each business website (not review text or photo galleries). You pay per business delivered on Apify; contact and decision-maker add-ons bill only when data is found. **No Google Maps API key and no proxies** to configure on your side.

**Measured at a glance.** Listing benchmark (9 countries, 1,582 businesses): phone fill **~97%**, website on Maps **~89%**. Contact extraction benchmark (~480 websites attempted across IT, ES, US, BE, DE): **54–80% email yield** by country — e.g. ~71% in Spain, 80% in Germany and Belgium. **Pay-per-event pricing on Apify:** **$2.50 / 1,000** businesses delivered, **$2.50 / 1,000** website-contact hits, **$0.22** per verified decision maker found. **Country sweep** covers **9 countries** (IT, FR, DE, ES, GB, NL, BE, PT, US) with resumable state — no API key, no proxies.

### Google Maps leads scraper & email extractor — how it works

1. **Map search** — set `searchTerms` plus an area (`locationQuery` or coordinates + `radiusKm`), or enable **country sweep** for an entire market.
2. **Listing parse** — each Maps result becomes one row: name, category, address, phone, rating, review count, opening hours, website and Maps URL. Optional filters: `minRating`, `websiteFilter`.
3. **Optional website crawl** — when enabled, visits the official site linked on Maps for public **emails and phones**, and optionally runs a **decision-maker** lookup (named contact + verified email).
4. **Data export** — rows stream to the Apify **dataset** (JSON items, CSV download, or API). Billing fires per event only for rows and add-ons actually delivered.

Configure everything in the **Apify Console** input form — no coding required for a standard run.

### What data can you extract from Google Maps?

Each dataset row is one billable local business (`place-scraped` event). Enable **Extract website contacts** to fill `emails` and `phonesFromSite` from the official site (`website-contacts` event, only when contacts are found).

| Field | Example |
|-------|---------|
| `name` | Trattoria Da Mario |
| `category` | Restaurant |
| `address` | Via Garibaldi 12, 10122 Torino TO, Italy |
| `phone` | +39 011 436 7890 |
| `website` | https://trattoriadamario.it |
| `rating` | 4.6 |
| `reviewCount` | 128 |
| `openingHours` | `{ "Monday": "12:00–15:00, 19:00–23:00", ... }` |
| `mapsUrl` | https://www.google.com/maps/place/?q=place\_id:ChIJ… |
| `searchTerm` | restaurants |
| `emails` | `["info@trattoriadamario.it"]` *(add-on)* |
| `phonesFromSite` | `["+39 011 436 7891"]` *(add-on)* |

#### Business identity & location

Name, category, categories, address, latitude, longitude, Maps URL (`mapsUrl`), and the search term that matched the listing.

#### Phones, websites & Maps URLs

Phone number and website URL as shown on Google Maps — ready for CRM import or outbound workflows.

#### Ratings, review counts & opening hours

Google rating, review count, price level when available, and `openingHours` by weekday when Maps exposes them.

#### Website emails & phones (optional add-on)

When **Extract website contacts** is enabled, the Actor visits each business website and extracts public emails and phone numbers into `emails` and `phonesFromSite`. Billed only when contacts are found.

#### Decision-maker name & verified email (optional add-on)

When **Find the decision maker** is enabled, the Actor attaches `decisionMakerName`, `decisionMakerRole`, `decisionMakerEmail`, `decisionMakerEmailSendable`, and `decisionMakerLinkedin` for businesses with a website (up to `maxDecisionMakers` lookups per run). Expect **~30–60 seconds per business** looked up. The `decision-maker` event fires **only** when a named decision maker is found with a verified contact email. For the full waterfall with alternates and bulk domain input, use the standalone [Decision Maker Finder](https://apify.com/pequod-labs/company-decision-maker-finder).

#### What this Google Maps scraper does not extract

**No review text, no photo galleries, no menus, no popular-times heatmaps.** This Actor optimizes for **lead fields** — identity, location, phones, websites, rating counts and hours — plus optional website contacts and decision makers. If you need full-feature Maps crawling (reviews, photos, deep place metadata), use a category-leading **full Maps crawler** on the Apify Store instead.

### How it compares to other Google Maps scrapers on Apify

| Approach | Typical focus | This Actor |
|----------|---------------|------------|
| **Full-featured Maps crawlers** on Apify | Reviews, photos, rich place metadata, maximum field coverage | **Lead-first:** phone, address, hours, Maps URL, optional site emails — you pay $2.50 / 1,000 businesses and only for contacts actually found |
| **Email-only Maps extractors** on Apify | Pull emails from Maps-linked sites | **Listing + emails + optional DM** in one dataset row; filters (`minRating`, `websiteFilter`) included at no extra filter fee |
| **DIY scripts / extensions** | Free tiers, self-hosted | **Managed Apify run:** no API key, no proxy setup, resumable **country sweep** across **9 countries**, pay-per-result billing |

We do not claim to replace every Maps scraper on Apify — we compete where you want **local business leads and verified website contacts**, not a media archive of reviews and images.

### How much does Google Maps lead scraping cost?

Configure list prices on Apify; the Actor charges by event name:

| Event | Typical price | When charged |
|-------|---------------|--------------|
| `apify-actor-start` | $0.00005 (FREE tier) | Once at run start |
| `place-scraped` | $2.50 / 1,000 (FREE tier) | Each business delivered to the dataset |
| `website-contacts` | $2.50 / 1,000 (FREE tier) | Each business where site emails/phones are found (add-on) |
| `decision-maker` | $0.22 (FREE tier) | Each business where a named decision maker with a verified email is found (add-on) |

**Example:** 1,000 restaurants with listing only ≈ **$2.50** in place events (+ $0.00005 start). Same 1,000 with contacts found on 600 sites ≈ **$2.50 + $1.50** = **$4.00** total. Filters (`minRating`, `websiteFilter`) are free.

**Is there a free Google Maps scraper?** Apify’s **free plan** includes about **$5 of platform credits per month** — enough for small test runs (set a low `maxPlaces` and a spending limit). This is not unlimited free scraping; at scale you pay **pay-per-event** prices above. No fake “free forever” lead lists.

Your run **spending limit** caps how many `place-scraped` events can fire; the Actor stops cleanly when the limit is reached.

### Find businesses without a website on Google Maps

Set **`websiteFilter: none`** to return only businesses that have **no website** listed on Maps — a strong fit for **web design prospecting** and agencies selling sites to shops that still lack an online presence. Listing-only rows still bill `place-scraped`; no website means no website-contact or decision-maker add-on for those rows.

### Scrape Google Maps leads by country (country sweep)

**Country sweep** mode walks a supported country **locality by locality** (towns and cities from [GeoNames](https://www.geonames.org/) `cities500`, filtered to populated places). Each run delivers up to `maxPlaces` new businesses; progress is saved in a **named key-value store** so the next run with the **same `countryCode`, `searchTerms`, and geographic selection** resumes where the previous run stopped. Place IDs already delivered in earlier runs are **not charged again**.

Supported countries: **IT, FR, DE, ES, GB, NL, BE, PT, US**.

This is a **Google Maps scraper by country** without you configuring proxies or API keys — the Actor handles data access while you sweep an entire market niche by niche.

#### Restrict to regions, provinces, or municipalities

Leave the geo filters empty to sweep the whole country. Or pass any combination of:

- **`sweepRegions`** — GeoNames admin1 names (Italian regions, US states, …)
- **`sweepProvinces`** — GeoNames admin2 names (Italian provinces / metropolitan cities, US counties, …)
- **`sweepLocalities`** — individual town / municipality names from the bundled dataset

Filters use **union** semantics: a locality is included if its region **or** province **or** name matches any selected value. Matching is **case- and accent-insensitive**. Homonyms (same town name in different provinces) are **all** included.

**Friendly name matching:** type **native official names** for regions (e.g. `Lombardia` or `Lombardy` both work). For provinces and US counties, administrative prefixes and trailing `County` are ignored — `Milano` matches `Città metropolitana di Milano`, `Los Angeles` matches `Los Angeles County`, `Cuneo` matches `Provincia di Cuneo`.

<details>
<summary>Region names per country</summary>

- **IT** (Italy): Abruzzo, Aosta Valley, Apulia, Basilicate, Calabria, Campania, Emilia-Romagna, Friuli Venezia Giulia, Lazio, Liguria, Lombardy, Molise, Piedmont, Sardinia, Sicily, The Marches, Trentino-Alto Adige, Tuscany, Umbria, Veneto
- **FR** (France): Bourgogne, Brittany, Centre-Val de Loire, Corsica, Grand Est, Hauts-de-France, New Aquitaine, Normandy, Occitanie, Pays de la Loire, Provence-Alpes-Côte d'Azur, Rhône-Alpes, Île-de-France
- **DE** (Germany): Baden-Wurttemberg, Bavaria, Brandenburg, Bremen, Hamburg, Hesse, Lower Saxony, Mecklenburg-Vorpommern, North Rhine-Westphalia, Rheinland-Pfalz, Saarland, Saxony, Saxony-Anhalt, Schleswig-Holstein, State of Berlin, Thuringia
- **ES** (Spain): Andalusia, Aragon, Asturias, Balearic Islands, Basque Country, Canary Islands, Cantabria, Castille and León, Castille-La Mancha, Catalonia, Ceuta, Extremadura, Galicia, La Rioja, Madrid, Melilla, Murcia, Navarre, Valencia
- **GB** (United Kingdom): England, Northern Ireland, Scotland, Wales
- **NL** (Netherlands): Drenthe, Flevoland, Friesland, Gelderland, Groningen, Limburg, North Brabant, North Holland, Overijssel, South Holland, Utrecht, Zeeland
- **BE** (Belgium): Brussels Capital, Flanders, Wallonia
- **PT** (Portugal): Aveiro, Azores, Beja, Braga, Bragança, Castelo Branco, Coimbra, Faro, Guarda, Leiria, Lisbon, Madeira, Portalegre, Porto, Santarém, Setúbal, Viana do Castelo, Vila Real, Viseu, Évora
- **US** (United States): Alabama, Alaska, Arizona, Arkansas, California, Colorado, Connecticut, Delaware, District of Columbia, Florida, Georgia, Hawaii, Idaho, Illinois, Indiana, Iowa, Kansas, Kentucky, Louisiana, Maine, Maryland, Massachusetts, Michigan, Minnesota, Mississippi, Missouri, Montana, Nebraska, Nevada, New Hampshire, New Jersey, New Mexico, New York, North Carolina, North Dakota, Ohio, Oklahoma, Oregon, Pennsylvania, Rhode Island, South Carolina, South Dakota, Tennessee, Texas, Utah, Vermont, Virginia, Washington, West Virginia, Wisconsin, Wyoming

</details>

Province names for all countries are listed in [docs/GEO\_NAMES.md](docs/GEO_NAMES.md).
Any municipality name from the bundled GeoNames cities500 extract also works via
`sweepLocalities` (case- and accent-insensitive).

Each distinct geographic selection (plus `searchTerms` + country) uses its **own resumable state store**, so a Lombardy-only sweep never shares progress with a full-country run.

#### Example input (country sweep)

```json
{
  "searchTerms": ["plumber", "heating engineer"],
  "countryCode": "IT",
  "countrySweep": true,
  "maxPlaces": 500,
  "sweepMinPopulation": 2000
}
```

#### Example input (region / province / comune filter)

```json
{
  "searchTerms": ["idraulico"],
  "countryCode": "IT",
  "countrySweep": true,
  "sweepRegions": ["Lombardia", "Toscana"],
  "sweepProvinces": ["Milano", "Torino"],
  "sweepLocalities": ["Alba", "Bra"],
  "maxPlaces": 200
}
```

Optional **`startFromLocality`**: jump the sweep to a named town (case-insensitive match on the active locality list after geo filters). With a homonym name, the sweep jumps to the **first** matching locality in that list (population-descending order). When set, this **always** moves the cursor to that locality for the run — even if a saved sweep cursor already exists — for example to retry or prioritize a town. `searchTerms` (and geo filters) must still match the store fingerprint.

After each run, check **`RUN_SUMMARY`** for `nextLocalityIndex`, `nextLocalityName`, `sweepCompleted`, `stateStoreName`, and `sweepGeoScope` (null when unfiltered). When `sweepCompleted` is `true`, every locality in the (filtered) dataset has been processed for your search terms.

#### Resume workflow

1. Run with `countrySweep: true` until `maxPlaces` is reached (or the spending limit caps delivery).
2. Start a **new run** with the same `searchTerms`, `countryCode`, and geo filters — the Actor reopens the same state store and continues from `nextLocalityIndex`.
3. Repeat until `sweepCompleted` is `true` in `RUN_SUMMARY`.

Do **not** change `searchTerms` mid-sweep: the saved state is keyed to your term list; different terms require a new sweep (new state store). Changing region/province/locality filters also opens a **different** state store.

#### Coverage in dense areas and megacities

Maps results are paginated (~20 places per page, capped per cell). When a cell still returns a full last page, the Actor **subdivides that cell once** and re-queries the four finer anchors — still under the same query budget — so dense city centers lose fewer leads past the page cap. **`RUN_SUMMARY`** exposes `saturatedCells` (cells that hit the page cap) and `splitCells` (finer anchors scheduled by subdivision). Country sweep also uses a **wider 15 km grid for megacities** (population ≥ 2M) so outskirts beyond the GeoNames city centroid are covered.

Each run has a **fixed upstream query budget** derived from `maxPlaces` (COGS guard). With many `searchTerms`, that budget is **shared fairly across terms** — each term covers the central cells first, spiralling outward. If `queryBudgetExhausted` is `true` in `RUN_SUMMARY`, split the run into fewer terms or a smaller geographic area.

Concurrent runs with the same `searchTerms`, country, and geo selection use a **best-effort lock** in the named key-value store (15-minute window). If another run is still active, the Actor fails with a clear message rather than corrupting shared state. This is not a hard distributed lock — avoid launching duplicate sweeps on purpose.

#### GeoNames attribution

Locality names, coordinates, and admin region/province labels are derived from GeoNames data ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)). GeoNames is an open geographical database. When publishing results built with country sweep, please credit GeoNames appropriately (e.g. “Contains data from GeoNames, licensed under CC BY 4.0”).

### Google Maps lead generation use cases

#### Local sales call lists

Build call lists of shops, clinics or trades in a city with phone and address ready for CRM import.

#### Outbound email from real website inboxes

Enable website contact extraction to pull **info@** and similar addresses from the business’s own site, not guessed inboxes.

#### Web design prospecting (no website)

Set `websiteFilter: none` to find businesses **without** a website on Maps — prospects who need a new site.

#### Multi-niche market mapping

Compare categories and ratings across neighborhoods with multiple `searchTerms` in one run (up to 20 terms).

### Input example

City + term with website contact extraction (see also `.actor/example_run_input.json`):

```json
{
  "searchTerms": ["ristoranti", "restaurants"],
  "locationQuery": "Milan, Italy",
  "countryCode": "it",
  "maxPlaces": 100,
  "radiusKm": 5,
  "enrichWebsiteContacts": true
}
```

**Tips:** use `locationQuery` **or** `latitude` + `longitude`, not both. **`websiteFilter: none`** for no-website prospecting. Up to 20 `searchTerms` per run.

### Output example (JSON)

```json
{
  "placeId": "ChIJexample123",
  "name": "Trattoria Da Mario",
  "category": "Restaurant",
  "categories": ["Restaurant", "Italian restaurant"],
  "address": "Via Garibaldi 12, 10122 Torino TO, Italy",
  "latitude": 45.0703,
  "longitude": 7.6869,
  "rating": 4.6,
  "reviewCount": 128,
  "priceLevel": 2,
  "phone": "+39 011 436 7890",
  "website": "https://trattoriadamario.it",
  "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJexample123",
  "openingHours": {
    "Monday": "12:00–15:00, 19:00–23:00",
    "Tuesday": "12:00–15:00, 19:00–23:00"
  },
  "sourceProvider": "maps-live",
  "searchTerm": "restaurants",
  "emails": ["info@trattoriadamario.it"],
  "phonesFromSite": []
}
```

- **Dataset:** one item per business (`place-scraped`).
- **Key-value store `RUN_SUMMARY`:** places pushed, contacts found, query budget, provider mix and failure reasons.

### Do you need a Google Maps API key or proxies?

**No.** You do not configure a Google Maps API key, proxies or upstream credentials. This Actor operates the data layer for you — a practical option when you want **Google Maps leads without API key setup** or proxy maintenance. Compliance with Google’s terms and your local prospecting laws remains your responsibility.

### FAQ

#### How can I generate leads using Google Maps?

Define **search terms** (business types) and an **area** (city or coordinates). The Actor returns structured local business leads — phone, address, rating, hours and Maps URL — ready for CRM or outbound. Optional add-ons enrich rows with website emails or decision-maker contacts.

#### How do I extract emails from Google Maps businesses?

Enable **Extract website contacts**. Emails come from the **official website linked on Maps**, not from guessed patterns on the Maps listing itself. You are billed `website-contacts` only when at least one email or phone is found on the site.

#### Is there a free Google Maps scraper?

**Apify’s free plan** includes about **$5 of platform credits per month** — enough to test this Actor with a small `maxPlaces` and a spending limit. There is no unlimited free lead export; scale runs use **pay-per-event** pricing (`place-scraped`, optional `website-contacts` and `decision-maker`). Honest freemium, not a fake “100% free scraper.”

#### Can I scrape Google Maps without coding or an API key?

**Yes.** Use the Apify Console input form: search terms, location, filters and add-on toggles — **no Google Maps API key**, **no proxy configuration**, and no code required for a standard run. Developers can also trigger runs via the Apify API or integrations (n8n, Make, Zapier).

#### Is it legal to scrape data from Google Maps?

This Actor collects **public business listing fields** exposed on Maps and, when enabled, contact details published on linked websites. Laws vary by country (GDPR, anti-spam, local marketing rules). You are the data controller for exports — obtain appropriate legal advice before outbound campaigns.

#### How do I find businesses without websites on Google Maps?

Set **`websiteFilter: none`**. You receive only listings where Maps shows no website — ideal for agencies prospecting businesses that need a new site.

#### Can I scrape Google Maps leads for an entire country?

Yes — enable **`countrySweep: true`** with a supported `countryCode`. The Actor walks localities from bundled GeoNames data, saves progress in a named key-value store, and skips already-delivered place IDs on resume.

#### Do I need a Google Maps API key or proxies?

No. We handle data access; you configure search terms, area and filters only.

#### Do you scrape Google Maps reviews or photos?

**No.** Focus is lead fields: identity, location, contact channels, rating **counts** and hours — not review text or photo galleries.

#### Where do emails come from?

From the business’s **official website** linked on Maps (add-on). We do not invent email patterns.

#### Come estrarre email e contatti da Google Maps?

Imposta **termini di ricerca** + **area** (es. `ristoranti`, `Milano, Italy`), abilita **Extract website contacts** (`enrichWebsiteContacts: true`). Le email provengono dal sito ufficiale indicato su Maps, non da pattern inventati.

### Related Actors (Pequod portfolio)

| Step | Actor | When |
|------|-------|------|
| 1. Discover | **Google Maps Local Leads** (this actor) | Local businesses by area + term |
| 1. Discover | [Lookalike Discovery](https://apify.com/pequod-labs/ecommerce-lookalike-discovery) | Domain/niche-based ecommerce stores |
| 1. Discover | [Instagram TikTok Shop Leads](https://apify.com/pequod-labs/instagram-tiktok-shop-leads) | Hashtag-driven social sellers |
| 2. Enrich contact | [Decision Maker Finder](https://apify.com/pequod-labs/company-decision-maker-finder) | Named decision makers on the company domain |
| 3. Enrich shipping | [Shipping Intelligence](https://apify.com/pequod-labs/ecommerce-shipping-intelligence) | Carriers and policies for ecommerce sites |

# Actor input Schema

## `searchTerms` (type: `array`):

Business types or keywords to find on Maps — e.g. <code>restaurants</code>, <code>idraulico</code>, <code>dental clinic</code>. Up to 20 terms per run; each term is searched across the grid covering your area.

## `locationQuery` (type: `string`):

Human-readable area to search — e.g. <code>Milan, Italy</code> or <code>Alba CN, Italy</code>. Use this <b>or</b> latitude/longitude below, not both.

## `latitude` (type: `number`):

Center latitude when you already have coordinates. Requires <code>longitude</code> too.

## `longitude` (type: `number`):

Center longitude when you already have coordinates. Requires <code>latitude</code> too.

## `radiusKm` (type: `number`):

Radius around the center point. Larger areas need more upstream queries; capped at 50 km.

## `countryCode` (type: `string`):

Two-letter ISO country for Maps results (e.g. <code>it</code>, <code>de</code>, <code>us</code>).

## `languageCode` (type: `string`):

Two-letter interface language for Maps (e.g. <code>it</code>, <code>en</code>). Defaults from country when omitted.

## `maxPlaces` (type: `integer`):

Upper bound on billable businesses pushed to the dataset.

## `minRating` (type: `number`):

Skip businesses below this Google rating (0–5). Leave empty for no filter.

## `websiteFilter` (type: `string`):

<b>any</b>: all businesses. <b>has</b>: only with a website listed on Maps. <b>none</b>: only businesses <i>without</i> a website — ideal for web agencies prospecting shops that need a site.

## `enrichWebsiteContacts` (type: `boolean`):

When enabled, visits each business website and extracts public emails and phone numbers. Billed as an extra event per business where contacts are found (in addition to the place-scraped event).

## `findDecisionMaker` (type: `boolean`):

When enabled, runs the decision-maker waterfall on each business website (up to <code>maxDecisionMakers</code> sites with a website). Expect <b>~30–60 seconds per business</b> — much slower than listing-only mode. Charged only when a named decision maker with a verified contact email is found (<code>decision-maker</code> event). Requires server-side API keys configured on the actor.

## `targetPersona` (type: `string`):

Persona preset for decision-maker search, e.g. <code>founder\_ceo</code>, <code>sales\_leader</code>, <code>ops\_logistics</code>. Defaults to founder/CEO when omitted.

## `maxDecisionMakers` (type: `integer`):

Cap on how many businesses with a website receive a decision-maker lookup this run. Businesses beyond the cap are still delivered without DM fields.

## `countrySweep` (type: `boolean`):

When enabled, scrapes an entire supported country locality-by-locality using bundled GeoNames data. Do not set locationQuery or coordinates. Optionally restrict the sweep with <code>sweepRegions</code>, <code>sweepProvinces</code>, and/or <code>sweepLocalities</code> (union: a locality matches if it hits any selected value). Each run delivers up to maxPlaces; progress is saved in a named key-value store so the next run with the same searchTerms, country, and geographic selection resumes without double-charging previously delivered places.

## `sweepRegions` (type: `array`):

Optional GeoNames admin1 / region names to include (e.g. <code>\["Lombardy", "Tuscany"]</code> or native names like <code>\["Lombardia", "Toscana"]</code> for Italy, <code>\["California"]</code> for the US). Matching is case- and accent-insensitive; native official names are accepted alongside GeoNames labels. Combined with provinces and localities as a <b>union</b>. Requires <code>countrySweep=true</code>. Each distinct selection keeps its own resumable state store.

## `sweepProvinces` (type: `array`):

Optional GeoNames admin2 names — Italian provinces/metropolitan cities (e.g. <code>\["Milano"]</code> or <code>\["Città metropolitana di Milano"]</code>, <code>\["Cuneo"]</code> for Provincia di Cuneo) or US counties (e.g. <code>\["Los Angeles"]</code> or <code>\["Los Angeles County"]</code>). Administrative prefixes and trailing <code>County</code> are ignored during matching. Case- and accent-insensitive. Union semantics with regions and localities. Requires <code>countrySweep=true</code>.

## `sweepLocalities` (type: `array`):

Optional locality (municipality) names from the bundled GeoNames dataset (e.g. <code>\["Alba", "Bra"]</code>). Homonyms are all included. Matching is case- and accent-insensitive. Union semantics with regions and provinces. Requires <code>countrySweep=true</code>.

## `startFromLocality` (type: `string`):

Optional locality name to jump the sweep cursor to (case-insensitive match against the active locality list, after any geo filters). When set, always overrides the saved cursor for this run — useful to retry or prioritize a town without waiting for earlier ones.

## `sweepMinPopulation` (type: `integer`):

Skip localities below this population threshold during country sweep.

## Actor input object example

```json
{
  "searchTerms": [
    "restaurants",
    "idraulico"
  ],
  "locationQuery": "Milan, Italy",
  "radiusKm": 5,
  "countryCode": "it",
  "maxPlaces": 100,
  "websiteFilter": "any",
  "enrichWebsiteContacts": false,
  "findDecisionMaker": false,
  "maxDecisionMakers": 50,
  "countrySweep": false,
  "sweepMinPopulation": 1000
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "searchTerms": [
        "restaurants",
        "idraulico"
    ],
    "locationQuery": "Milan, Italy"
};

// Run the Actor and wait for it to finish
const run = await client.actor("pequod-labs/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 = {
    "searchTerms": [
        "restaurants",
        "idraulico",
    ],
    "locationQuery": "Milan, Italy",
}

# Run the Actor and wait for it to finish
run = client.actor("pequod-labs/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 '{
  "searchTerms": [
    "restaurants",
    "idraulico"
  ],
  "locationQuery": "Milan, Italy"
}' |
apify call pequod-labs/google-maps-local-leads --silent --output-dataset

```

## MCP server setup

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