# Google Maps Leads Scraper - Emails, Phones, Sites (`convertfleetdotonline/google-maps-leads-scraper`) Actor

Scrape Google Maps business leads with emails, phones, websites, addresses, ratings & reviews. Deep website crawl finds hidden emails, socials & named contacts. Search by prompt, keyword, or Place ID. No API key.

- **URL**: https://apify.com/convertfleetdotonline/google-maps-leads-scraper.md
- **Developed by:** [Hasnain Nisar](https://apify.com/convertfleetdotonline) (community)
- **Categories:** Lead generation
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 scraped places

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

Extract business leads from [Google Maps](https://www.google.com/maps) at scale — names, addresses, phone numbers, websites, ratings, reviews, and (with deep scan) **emails, social-media links, and named contacts** harvested straight from each business's website. This Google Maps scraper turns a plain-English prompt like *"find 50 dentists in Dubai with email and rating above 4"* — or an explicit list of search terms, place URLs, or Place IDs — into a clean, export-ready dataset of local-business leads. The #1 use case: build targeted B2B prospect lists with verified contact details for cold outreach, no Google Places API key required.

### What does the Google Maps Leads Scraper Actor do?

The Google Maps Leads Scraper is a tool that searches Google Maps for any business category in any location and returns structured business listings enriched with contact information. It automates the full Maps workflow — running each search, scrolling the results panel, opening every listing's detail pane, and extracting the business metadata — then optionally crawls each listing's website to find the emails and phone numbers Google itself never shows. Built on Playwright + headless Chromium with Apify Proxy support and a built-in natural-language prompt parser, it streams every record to your dataset as it's produced, so partial runs stay useful.

- **Scrape business leads from Google Maps** by keyword + location ("plumbers in Brooklyn")
- **Extract emails from Google Maps listings** via deep website crawling (homepage + contact pages)
- **Find phone numbers, websites, and addresses** for every local business in bulk
- **Harvest social-media links** — Instagram, Facebook, LinkedIn, X/Twitter, TikTok, YouTube, WhatsApp
- **Pull named contacts** like CEO, Founder, Director, and Owner from business websites
- **Search with a natural-language prompt** ("find 100 coffee shops in NYC with email") — AI parses it for you
- **Scrape by Place ID or Google Maps URL** for pinpoint, exact-place extraction
- **Filter leads by rating, review count, has-email, has-phone, has-WhatsApp, or no-website**
- **Verify discovered emails** with a built-in MX-record check (`emailValid` flag)
- **Extract Google Maps reviews and photos** per place alongside the lead data

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

Each record in the dataset can include the following fields:

| Field | Description |
|---|---|
| `name` | Business name |
| `category` | Google Maps business category (e.g. "Coffee shop") |
| `address` | Full street address |
| `phone` | Primary phone number |
| `website` | Business website URL |
| `email` | Best email (promoted from deep scan when available) |
| `rating` | Aggregate star rating (0–5) |
| `reviewsCount` | Total number of reviews |
| `hours` | Open/closed status string |
| `plusCode` | Plus-code-style location identifier |
| `latitude` / `longitude` | Geographic coordinates |
| `googleMapsUrl` | Direct link to the Maps listing |
| `searchTerm` | The query that produced this record |
| `deepScanEmails` | All emails found on the website (comma-separated) |
| `deepScanPhones` | All phone numbers found on the website |
| `deepScanSocials` | Social-media links (Instagram, Facebook, LinkedIn, X, TikTok, YouTube, WhatsApp) |
| `deepScanIndividuals` | Named contacts (CEO, Founder, Director, Owner) |
| `emailValid` | MX-validity flag when email verification is enabled |
| `reviews` | Array of individual reviews when `reviewsLimit` > 0 |
| `images` | Array of photo URLs when `imagesLimit` > 0 |

`deepScan*` fields are empty strings unless deep scan is enabled. Any field absent on a listing returns an empty string or `null` — the actor never invents data.

### How to scrape leads from Google Maps (step-by-step)

1. Open the actor and click the **Run** tab in the Apify console.
2. Either type a natural-language **prompt** (e.g. "50 dentists in Dubai with email rating above 4") or fill in **Search terms** + **Location** explicitly.
3. Set **Max results per search term** to your target lead count.
4. Toggle **Deep scan** on to harvest emails, phones, socials, and named contacts from each website.
5. (Optional) Add **filters** — minimum rating, has-email, has-phone, no-website — to keep only qualified leads.
6. Click **Start** and watch records stream into the dataset in real time.
7. Export the results to **CSV, Excel, JSON, or Google Sheets**, or pull them via the Apify API.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `prompt` | string | `""` | Natural-language description; parsed into query, location, count, filters, and deep scan for slots you leave blank. |
| `searchTerms` | string[] | `["coffee shops"]` | One or more queries; each searched independently. Ignored when `prompt`/`placeUrls`/`placeIds` are set. |
| `placeCategories` | string[] | `[]` | Multiplies with each search term (e.g. "luxury" × ["hotels","restaurants"]). |
| `placeUrls` | string[] | `[]` | Direct Google Maps URLs to scrape. |
| `placeIds` | string[] | `[]` | Google Place IDs, converted to Maps URLs automatically. |
| `location` | string | `"New York"` | Free-text city/region/country combined as "<term> in <location>". |
| `country` / `city` / `state` / `county` / `postalCode` | string | `""` | Structured geolocation, used only when `location` is empty. |
| `maxResults` | integer | `50` | Target/cap per search term (1–500). |
| `language` | enum | `"en"` | Two-letter Google `hl` language code (14 options). |
| `deepScan` | boolean | `true` | Visit each website to harvest emails, phones, socials, named contacts. |
| `extractEmails` | boolean | `false` | Lightweight HTTP-only email lookup (used only when deep scan is off). |
| `extractWebsiteFallback` | boolean | `true` | Try `/contact` when the homepage has no email (quick-lookup only). |
| `verifyEmails` | boolean | `false` | Add an `emailValid` MX-check flag to each email. |
| `reviewsLimit` | integer | `0` | Reviews to extract per place (0–1000). |
| `reviewsSort` | enum | `"newest"` | newest / mostRelevant / highestRating / lowestRating. |
| `reviewsSince` | string | `""` | Drop reviews older than this YYYY-MM-DD date. |
| `reviewsKeywords` | string | `""` | Keep only reviews containing these comma-separated terms. |
| `includeReviewerData` | boolean | `true` | Attach reviewer name, profile URL, review count. |
| `imagesLimit` | integer | `0` | Extra photos to extract per place (0–200). |
| `includeImageAuthors` | boolean | `false` | Add the uploader name to each image. |
| `noWebsite` / `hasWebsite` / `hasPhone` / `hasEmail` / `hasWhatsapp` | boolean | `false` | Post-scrape contact filters. |
| `skipClosedPlaces` | boolean | `false` | Drop permanently/temporarily closed places. |
| `exactNameMatch` | boolean | `false` | Keep only places whose name contains a search term. |
| `minRating` / `maxRating` | number | `null` | Rating range filter (0–5). |
| `minReviews` | integer | `null` | Minimum review-count filter. |
| `headless` | boolean | `true` | Run browser headless (local debugging only). |
| `proxy` | object | `{ useApifyProxy: true }` | Apify Proxy config; RESIDENTIAL group recommended. |

```json
{
    "prompt": "find 100 plumbers in Brooklyn with email rating above 4",
    "deepScan": true,
    "verifyEmails": true,
    "maxResults": 100
}
````

### Output example

```json
{
    "name": "Joe's Coffee",
    "category": "Coffee shop",
    "address": "123 W 56th St, New York, NY 10019",
    "phone": "+1 212-555-0142",
    "website": "https://joescoffee.example",
    "email": "hello@joescoffee.example",
    "rating": 4.6,
    "reviewsCount": 812,
    "hours": "Open ⋅ Closes 8 PM",
    "plusCode": "Q263+CV New York, NY",
    "latitude": 40.762435,
    "longitude": -73.978942,
    "googleMapsUrl": "https://www.google.com/maps/place/Joe's+Coffee/data=...",
    "searchTerm": "coffee shops",
    "deepScanEmails": "hello@joescoffee.example, orders@joescoffee.example",
    "deepScanPhones": "+12125550142, +12125550143",
    "deepScanSocials": "https://instagram.com/joescoffee, https://facebook.com/joescoffee",
    "deepScanIndividuals": "John Doe, Jane Smith"
}
```

### Use cases

- **B2B lead generation** — build targeted prospect lists of local businesses with emails and phones for cold outreach.
- **Local agency prospecting** — find restaurants, dentists, or plumbers in a metro area, filtered to those with (or without) a website.
- **Sales territory building** — compile every business in a category and city with geocoordinates for route planning.
- **Lead enrichment** — harvest emails, social profiles, and named decision-makers from business websites to enrich an existing list.
- **Market research** — map the density, ratings, and review counts of competitors in any region.
- **Recruitment & partnerships** — identify and contact owners and founders across an industry vertical.

### Pricing

This actor runs on Apify's usage-based pricing — you pay for the platform compute units and proxy traffic your run consumes, which scales with the number of listings, whether deep scan is on, and how many reviews/images you pull. There's no per-result fee charged by the actor itself. RESIDENTIAL proxy is recommended for reliable Google Maps access. See the actor's **Pricing** tab for current details.

### Why use this Google Maps scraper instead of the Google Places API?

The official Google Places API charges per call, caps fields behind tiered pricing, and never exposes the email addresses or social links that small-business websites publish. This actor needs **no API key**, scrapes the contact details Google hides, and lets you drive it with a single natural-language prompt or a bulk list of terms, URLs, or Place IDs — then export everything to CSV or Sheets. The trade-off is that it depends on Google Maps' public HTML and proxy quality rather than a paid API contract.

### FAQ

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

This actor collects publicly available business data from Google Maps. Public data scraping is generally permissible, but you are responsible for complying with Google's Terms of Service and applicable laws such as GDPR and CCPA — especially when using the contact data for outreach.

#### Do I need an API key or Google account?

No Google account, Google Places API key, or any other API key is required. Natural-language prompt parsing is built in and works out of the box.

#### How much does it cost to scrape Google Maps leads?

Cost depends on your usage — number of listings, deep-scan crawling, reviews, and proxy traffic — billed via Apify's usage-based pricing. There is no fixed per-result charge from the actor.

#### How many results can I get and how fast?

Google itself stops listing results around the 120 mark per search; split a broad search into narrower neighborhood queries to go beyond that. Deep scan adds roughly 3–8 seconds per listing that has a website, with an early exit once an email and phone are found.

#### Can I export to CSV, Excel, JSON, or Google Sheets?

Yes. Every record streams to an Apify dataset that you can export to CSV, Excel, JSON, or Google Sheets, or fetch through the Apify API and Python/JavaScript client SDKs.

#### Will I get blocked, and does it use proxies?

Google rate-limits scrapers from a single IP after a few hundred requests. The actor integrates Apify Proxy — the RESIDENTIAL group is the recommended default to avoid blocks.

#### How complete are the scraped emails?

Roughly 30–60% of small-business websites expose an email publicly; the rest hide behind contact forms. The actor never fabricates an email — `deepScanEmails` is simply empty when none is found.

### Related actors

- [Google Maps Reviews Scraper](https://apify.com/hasnainnisar67/google-maps-reviews-scraper) — pull full review text, ratings, and owner replies for any place.
- [Email Verifier](https://apify.com/hasnainnisar67/email-verifier) — validate the scraped emails (syntax, MX, SMTP) before outreach.
- [LinkedIn Profile Scraper](https://apify.com/hasnainnisar67/linkedin-profile-scraper) — enrich leads with decision-maker profiles and corporate emails.
- [Facebook Page Scraper](https://apify.com/hasnainnisar67/facebook-page-scraper) — extract contact info from Facebook business pages.

### Support

Found a bug or need a tweak? Open an issue on the actor's **Issues** tab. Custom scraping and lead-generation solutions are available on request.

# Actor input Schema

## `prompt` (type: `string`):

Describe what you want in plain English — the actor parses it into a structured query, location, lead count, and filter set. Examples: "find 50 dentists in Dubai with email", "plumbers in Karachi Pakistan rating above 4", "100 coffee shops in NYC no website". When provided, the parsed values OVERRIDE `searchTerms`/`location`/`maxResults`/individual filters/`deepScan` unless those are also explicitly set. Prompt parsing is built in and works out of the box — no setup or keys required.

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

One or more search queries to run on Google Maps. Each term is searched independently and results are merged into one dataset. Combined with `placeCategories` if both are set. Ignored when `prompt` is set, or when `placeUrls`/`placeIds` are supplied.

## `placeCategories` (type: `array`):

Optional category list — the actor expands each searchTerm × category combination. Example: searchTerms=\["luxury"], placeCategories=\["hotels","restaurants"] → searches "luxury hotels" and "luxury restaurants". Leave empty to use search terms verbatim.

## `placeUrls` (type: `array`):

Direct Maps URLs to scrape. RECOMMENDED FORMAT: `https://www.google.com/maps/place/?q=place_id:<id>` — works reliably. The `/maps/place/<Name>/data=!4m7!...` form that Google's 'Share → Copy link' produces is unreliable from headless browsers (Maps doesn't fully render the detail pane on cold-start). If you have one of those, prefer using `placeIds` below — the actor converts those to the working URL form automatically.

## `placeIds` (type: `array`):

Google Place IDs (e.g. `ChIJN1t_tDeuEmsRUsoyG83frY4`). Each is converted to a Maps URL and scraped exactly like `placeUrls`. Combine freely with `placeUrls`.

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

City, region, or country to scope the search to. Combined with each search term as "<term> in <location>". Leave empty to fall back to the structured Country/City/State fields below.

## `country` (type: `string`):

Used only when free-text Location above is empty. Combined with City/State/County/Postal code into a single location string.

## `city` (type: `string`):

Used only when free-text Location is empty.

## `state` (type: `string`):

Used only when free-text Location is empty.

## `county` (type: `string`):

Used only when free-text Location is empty. Rarely needed but kept for parity with admin hierarchies.

## `postalCode` (type: `string`):

Used only when free-text Location is empty.

## `maxResults` (type: `integer`):

When filters are off, this is the search cap. When filters are active, the actor over-discovers (up to 3×, capped at 25 URLs) and stops scraping the moment enough records pass — so this becomes an output target. Tip: with Deep scan on, each listing also visits the business website (~3-8s), so large values take longer — raise this once you've confirmed a small run looks right.

## `language` (type: `string`):

Two-letter language code passed to Google Maps via the `hl` parameter. Affects business names, categories, and review content.

## `deepScan` (type: `boolean`):

ON by default. The actor visits every listing's website (homepage + a couple of contact-style sub-pages) to harvest emails, phone numbers, social-media links, and named contacts. The best email/phone is also promoted into the top-level `email` / `phone` fields. Adds ~3-8 seconds per listing that has a website.

## `extractEmails` (type: `boolean`):

Lightweight fallback for when Deep scan is disabled: fetches the homepage and standard contact paths over plain HTTP (no browser) looking for an email. Ignored when Deep scan is on, since deep scan is strictly more thorough.

## `extractWebsiteFallback` (type: `boolean`):

Only used by Quick email lookup (when Deep scan is OFF). Doubles per-site latency for sites that don't expose an email at all.

## `verifyEmails` (type: `boolean`):

Runs a syntax check + DNS MX-record lookup on every email the actor produces. Adds an `emailValid` boolean to each record. Adds ~50-200 ms per unique domain (cached). Recommended for cold-outreach lists.

## `reviewsLimit` (type: `integer`):

0 = skip reviews scraping entirely. Up to ~200 per place is reasonable; higher counts substantially increase per-place runtime (≈1 s per 10 reviews scrolled). Output appears in a `reviews` array on each record.

## `reviewsSort` (type: `string`):

How Maps orders the reviews list before we read it. `newest` is best for tracking; `mostRelevant` is the Maps default; `highestRating`/`lowestRating` for sentiment slicing.

## `reviewsSince` (type: `string`):

Optional cutoff. Reviews older than this date are dropped. Maps shows reviews as relative time ("3 weeks ago") so absolute dates are estimated; off-by-a-day is possible.

## `reviewsKeywords` (type: `string`):

If set, only reviews whose text contains at least one of these terms (case-insensitive) are kept. Empty = no keyword filter.

## `includeReviewerData` (type: `boolean`):

When on, each review carries the reviewer's display name, profile URL, and total-reviews count where Maps exposes them.

## `imagesLimit` (type: `integer`):

0 = main photo only (free, always included as `mainImageUrl`). Higher numbers open the photo gallery and harvest URLs. Output appears in an `images` array on each record. The first ~30 images load fast; beyond that requires extra scrolling.

## `includeImageAuthors` (type: `boolean`):

Adds the photo uploader's display name to each image entry where Maps shows it.

## `noWebsite` (type: `boolean`):

Keep only records whose `website` field is empty.

## `hasWebsite` (type: `boolean`):

Mutually exclusive with the previous toggle.

## `hasPhone` (type: `boolean`):

Drop records with no phone (Google Maps `phone` field, or `deepScanPhones` if Deep scan is on).

## `hasEmail` (type: `boolean`):

Drop records with no email. Without Deep scan, only the Quick-email-lookup result is checked.

## `hasWhatsapp` (type: `boolean`):

Keep only records whose deep-scanned social links include `wa.me` or `whatsapp.com`. Effectively requires Deep scan.

## `skipClosedPlaces` (type: `boolean`):

Drop records flagged with "Permanently closed" or "Temporarily closed" on Maps.

## `exactNameMatch` (type: `boolean`):

Maps's relevance ranking can return loosely-related places. Enable this to drop records whose `name` doesn't contain at least one of the search terms (case-insensitive). No effect when running from `placeUrls`/`placeIds`.

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

Drop records with rating below this value. Records with no rating at all are also dropped. Leave blank for no minimum.

## `maxRating` (type: `number`):

Drop records with rating above this value. Leave blank for no maximum.

## `minReviews` (type: `integer`):

Drop records with fewer reviews than this. Leave blank for no minimum.

## `headless` (type: `boolean`):

Disable only for debugging on a local run — Apify's servers are headless regardless.

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

Use Apify Proxy or your own custom proxies. Strongly recommended — Google rate-limits scrapers from any single IP after a few hundred requests. RESIDENTIAL group is safest.

## Actor input object example

```json
{
  "searchTerms": [
    "coffee shops"
  ],
  "placeCategories": [],
  "placeUrls": [],
  "placeIds": [],
  "location": "New York",
  "maxResults": 5,
  "language": "en",
  "deepScan": true,
  "extractEmails": false,
  "extractWebsiteFallback": true,
  "verifyEmails": false,
  "reviewsLimit": 0,
  "reviewsSort": "newest",
  "includeReviewerData": true,
  "imagesLimit": 0,
  "includeImageAuthors": false,
  "noWebsite": false,
  "hasWebsite": false,
  "hasPhone": false,
  "hasEmail": false,
  "hasWhatsapp": false,
  "skipClosedPlaces": false,
  "exactNameMatch": false,
  "headless": true,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One business listing per item — name, address, phone, website, email, rating, reviews count, lat/lng, and (when enabled) deep-scanned contact details, reviews, and images.

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

Final tally of records, active filters, and trial/billing status. Lives at key 'SUMMARY' in the default key-value store.

# 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 = {
    "prompt": "",
    "searchTerms": [
        "coffee shops"
    ],
    "placeCategories": [],
    "placeUrls": [],
    "placeIds": [],
    "location": "New York",
    "maxResults": 5,
    "reviewsSince": "",
    "reviewsKeywords": "",
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("convertfleetdotonline/google-maps-leads-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 = {
    "prompt": "",
    "searchTerms": ["coffee shops"],
    "placeCategories": [],
    "placeUrls": [],
    "placeIds": [],
    "location": "New York",
    "maxResults": 5,
    "reviewsSince": "",
    "reviewsKeywords": "",
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("convertfleetdotonline/google-maps-leads-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "prompt": "",
  "searchTerms": [
    "coffee shops"
  ],
  "placeCategories": [],
  "placeUrls": [],
  "placeIds": [],
  "location": "New York",
  "maxResults": 5,
  "reviewsSince": "",
  "reviewsKeywords": "",
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call convertfleetdotonline/google-maps-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=convertfleetdotonline/google-maps-leads-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Maps Leads Scraper - Emails, Phones, Sites",
        "description": "Scrape Google Maps business leads with emails, phones, websites, addresses, ratings & reviews. Deep website crawl finds hidden emails, socials & named contacts. Search by prompt, keyword, or Place ID. No API key.",
        "version": "0.2",
        "x-build-id": "jjhajP6jYm5H2yXFX"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/convertfleetdotonline~google-maps-leads-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-convertfleetdotonline-google-maps-leads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/convertfleetdotonline~google-maps-leads-scraper/runs": {
            "post": {
                "operationId": "runs-sync-convertfleetdotonline-google-maps-leads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/convertfleetdotonline~google-maps-leads-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-convertfleetdotonline-google-maps-leads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "prompt": {
                        "title": "Natural-language prompt (optional)",
                        "type": "string",
                        "description": "Describe what you want in plain English — the actor parses it into a structured query, location, lead count, and filter set. Examples: \"find 50 dentists in Dubai with email\", \"plumbers in Karachi Pakistan rating above 4\", \"100 coffee shops in NYC no website\". When provided, the parsed values OVERRIDE `searchTerms`/`location`/`maxResults`/individual filters/`deepScan` unless those are also explicitly set. Prompt parsing is built in and works out of the box — no setup or keys required."
                    },
                    "searchTerms": {
                        "title": "Search terms",
                        "type": "array",
                        "description": "One or more search queries to run on Google Maps. Each term is searched independently and results are merged into one dataset. Combined with `placeCategories` if both are set. Ignored when `prompt` is set, or when `placeUrls`/`placeIds` are supplied.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "placeCategories": {
                        "title": "Place categories (multiply with search terms)",
                        "type": "array",
                        "description": "Optional category list — the actor expands each searchTerm × category combination. Example: searchTerms=[\"luxury\"], placeCategories=[\"hotels\",\"restaurants\"] → searches \"luxury hotels\" and \"luxury restaurants\". Leave empty to use search terms verbatim.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "placeUrls": {
                        "title": "Google Maps URLs (alternative to search)",
                        "type": "array",
                        "description": "Direct Maps URLs to scrape. RECOMMENDED FORMAT: `https://www.google.com/maps/place/?q=place_id:<id>` — works reliably. The `/maps/place/<Name>/data=!4m7!...` form that Google's 'Share → Copy link' produces is unreliable from headless browsers (Maps doesn't fully render the detail pane on cold-start). If you have one of those, prefer using `placeIds` below — the actor converts those to the working URL form automatically.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "placeIds": {
                        "title": "Place IDs (alternative to search)",
                        "type": "array",
                        "description": "Google Place IDs (e.g. `ChIJN1t_tDeuEmsRUsoyG83frY4`). Each is converted to a Maps URL and scraped exactly like `placeUrls`. Combine freely with `placeUrls`.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "location": {
                        "title": "Location (free-text)",
                        "type": "string",
                        "description": "City, region, or country to scope the search to. Combined with each search term as \"<term> in <location>\". Leave empty to fall back to the structured Country/City/State fields below."
                    },
                    "country": {
                        "title": "Country",
                        "type": "string",
                        "description": "Used only when free-text Location above is empty. Combined with City/State/County/Postal code into a single location string."
                    },
                    "city": {
                        "title": "City",
                        "type": "string",
                        "description": "Used only when free-text Location is empty."
                    },
                    "state": {
                        "title": "State / Province",
                        "type": "string",
                        "description": "Used only when free-text Location is empty."
                    },
                    "county": {
                        "title": "County",
                        "type": "string",
                        "description": "Used only when free-text Location is empty. Rarely needed but kept for parity with admin hierarchies."
                    },
                    "postalCode": {
                        "title": "Postal / ZIP code",
                        "type": "string",
                        "description": "Used only when free-text Location is empty."
                    },
                    "maxResults": {
                        "title": "Max results per search term",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "When filters are off, this is the search cap. When filters are active, the actor over-discovers (up to 3×, capped at 25 URLs) and stops scraping the moment enough records pass — so this becomes an output target. Tip: with Deep scan on, each listing also visits the business website (~3-8s), so large values take longer — raise this once you've confirmed a small run looks right.",
                        "default": 50
                    },
                    "language": {
                        "title": "Language",
                        "enum": [
                            "en",
                            "es",
                            "fr",
                            "de",
                            "it",
                            "pt",
                            "nl",
                            "pl",
                            "ru",
                            "ja",
                            "ko",
                            "zh-CN",
                            "ar",
                            "hi"
                        ],
                        "type": "string",
                        "description": "Two-letter language code passed to Google Maps via the `hl` parameter. Affects business names, categories, and review content.",
                        "default": "en"
                    },
                    "deepScan": {
                        "title": "Deep scan — visit each business website to harvest contact details",
                        "type": "boolean",
                        "description": "ON by default. The actor visits every listing's website (homepage + a couple of contact-style sub-pages) to harvest emails, phone numbers, social-media links, and named contacts. The best email/phone is also promoted into the top-level `email` / `phone` fields. Adds ~3-8 seconds per listing that has a website.",
                        "default": true
                    },
                    "extractEmails": {
                        "title": "Quick email lookup (used only when Deep scan is OFF)",
                        "type": "boolean",
                        "description": "Lightweight fallback for when Deep scan is disabled: fetches the homepage and standard contact paths over plain HTTP (no browser) looking for an email. Ignored when Deep scan is on, since deep scan is strictly more thorough.",
                        "default": false
                    },
                    "extractWebsiteFallback": {
                        "title": "Try `/contact` if homepage has no email",
                        "type": "boolean",
                        "description": "Only used by Quick email lookup (when Deep scan is OFF). Doubles per-site latency for sites that don't expose an email at all.",
                        "default": true
                    },
                    "verifyEmails": {
                        "title": "Verify discovered emails (MX-record check)",
                        "type": "boolean",
                        "description": "Runs a syntax check + DNS MX-record lookup on every email the actor produces. Adds an `emailValid` boolean to each record. Adds ~50-200 ms per unique domain (cached). Recommended for cold-outreach lists.",
                        "default": false
                    },
                    "reviewsLimit": {
                        "title": "Reviews to extract per place",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "0 = skip reviews scraping entirely. Up to ~200 per place is reasonable; higher counts substantially increase per-place runtime (≈1 s per 10 reviews scrolled). Output appears in a `reviews` array on each record.",
                        "default": 0
                    },
                    "reviewsSort": {
                        "title": "Sort reviews by",
                        "enum": [
                            "newest",
                            "mostRelevant",
                            "highestRating",
                            "lowestRating"
                        ],
                        "type": "string",
                        "description": "How Maps orders the reviews list before we read it. `newest` is best for tracking; `mostRelevant` is the Maps default; `highestRating`/`lowestRating` for sentiment slicing.",
                        "default": "newest"
                    },
                    "reviewsSince": {
                        "title": "Only reviews posted after this date (YYYY-MM-DD)",
                        "type": "string",
                        "description": "Optional cutoff. Reviews older than this date are dropped. Maps shows reviews as relative time (\"3 weeks ago\") so absolute dates are estimated; off-by-a-day is possible."
                    },
                    "reviewsKeywords": {
                        "title": "Filter reviews by keywords (any-match, comma-separated)",
                        "type": "string",
                        "description": "If set, only reviews whose text contains at least one of these terms (case-insensitive) are kept. Empty = no keyword filter."
                    },
                    "includeReviewerData": {
                        "title": "Include reviewer profile data with each review",
                        "type": "boolean",
                        "description": "When on, each review carries the reviewer's display name, profile URL, and total-reviews count where Maps exposes them.",
                        "default": true
                    },
                    "imagesLimit": {
                        "title": "Additional images to extract per place",
                        "minimum": 0,
                        "maximum": 200,
                        "type": "integer",
                        "description": "0 = main photo only (free, always included as `mainImageUrl`). Higher numbers open the photo gallery and harvest URLs. Output appears in an `images` array on each record. The first ~30 images load fast; beyond that requires extra scrolling.",
                        "default": 0
                    },
                    "includeImageAuthors": {
                        "title": "Include uploader name with each image",
                        "type": "boolean",
                        "description": "Adds the photo uploader's display name to each image entry where Maps shows it.",
                        "default": false
                    },
                    "noWebsite": {
                        "title": "Filter: only listings WITHOUT a website",
                        "type": "boolean",
                        "description": "Keep only records whose `website` field is empty.",
                        "default": false
                    },
                    "hasWebsite": {
                        "title": "Filter: only listings WITH a website",
                        "type": "boolean",
                        "description": "Mutually exclusive with the previous toggle.",
                        "default": false
                    },
                    "hasPhone": {
                        "title": "Filter: must have a phone number",
                        "type": "boolean",
                        "description": "Drop records with no phone (Google Maps `phone` field, or `deepScanPhones` if Deep scan is on).",
                        "default": false
                    },
                    "hasEmail": {
                        "title": "Filter: must have an email",
                        "type": "boolean",
                        "description": "Drop records with no email. Without Deep scan, only the Quick-email-lookup result is checked.",
                        "default": false
                    },
                    "hasWhatsapp": {
                        "title": "Filter: must have a WhatsApp link",
                        "type": "boolean",
                        "description": "Keep only records whose deep-scanned social links include `wa.me` or `whatsapp.com`. Effectively requires Deep scan.",
                        "default": false
                    },
                    "skipClosedPlaces": {
                        "title": "Filter: skip permanently / temporarily closed places",
                        "type": "boolean",
                        "description": "Drop records flagged with \"Permanently closed\" or \"Temporarily closed\" on Maps.",
                        "default": false
                    },
                    "exactNameMatch": {
                        "title": "Filter: only places whose name contains the search term",
                        "type": "boolean",
                        "description": "Maps's relevance ranking can return loosely-related places. Enable this to drop records whose `name` doesn't contain at least one of the search terms (case-insensitive). No effect when running from `placeUrls`/`placeIds`.",
                        "default": false
                    },
                    "minRating": {
                        "title": "Filter: minimum rating",
                        "minimum": 0,
                        "maximum": 5,
                        "type": "number",
                        "description": "Drop records with rating below this value. Records with no rating at all are also dropped. Leave blank for no minimum."
                    },
                    "maxRating": {
                        "title": "Filter: maximum rating",
                        "minimum": 0,
                        "maximum": 5,
                        "type": "number",
                        "description": "Drop records with rating above this value. Leave blank for no maximum."
                    },
                    "minReviews": {
                        "title": "Filter: minimum review count",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Drop records with fewer reviews than this. Leave blank for no minimum."
                    },
                    "headless": {
                        "title": "Run browser headless",
                        "type": "boolean",
                        "description": "Disable only for debugging on a local run — Apify's servers are headless regardless.",
                        "default": true
                    },
                    "proxy": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Use Apify Proxy or your own custom proxies. Strongly recommended — Google rate-limits scrapers from any single IP after a few hundred requests. RESIDENTIAL group is safest.",
                        "default": {
                            "useApifyProxy": true
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
