# Google Maps Scraper - Business Leads with Phone & Website (`leadproof/google-maps-scraper`) Actor

Scrape Google Maps business listings: name, category, rating, review count, phone, website, address and coordinates. Filter by review count, pay only for places delivered.

- **URL**: https://apify.com/leadproof/google-maps-scraper.md
- **Developed by:** [Lead Proof](https://apify.com/leadproof) (community)
- **Categories:** Lead generation, Integrations
- **Features:** Limited permissions
- **Stats:** 9 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 results

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

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

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

## How to integrate an Actor?

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

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

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

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

# README

## Google Maps Scraper - Business Leads with Phone & Website

Extract business listings from Google Maps: name, category, star rating, review count, phone number, website, full address and coordinates.

**You pay per place delivered.** Places filtered out by your own settings are never charged, and a query that returns nothing costs nothing.

### What you get

Every place is one row:

| Field | Example |
|---|---|
| `name` | Parker & Sons |
| `category` | HVAC contractor |
| `rating` | 4.7 |
| `reviews` | 683 (may be `null`; unknown counts are kept only when `minReviews` is 0) |
| `phone` | +1 602-424-9619 |
| `website` | https://www.parkerandsons.com/ |
| `address` | 3636 E Anne St A, Phoenix, AZ 85040 |
| `city_state` | Phoenix, AZ 85040 |
| `lat` / `lng` | 33.4139155 / -112.0022947 |
| `placeId` | 0x872b0faad068d687:0xcb13480c28213ccd |
| `mapsUrl` | https://www.google.com/maps?cid=14633118831306947789 |
| `searchQuery` | hvac contractor Phoenix, AZ |

Export as CSV, JSON, Excel or feed it straight into your CRM through the Apify API.

### Input

```json
{
  "searchTerms": ["hvac contractor", "plumber"],
  "locations": ["Phoenix, AZ", "Dallas, TX"],
  "maxResultsPerQuery": 40,
  "maxResults": 100,
  "country": "US",
  "minReviews": 20,
  "requireWebsite": true
}
```

Every search term is combined with every location, so the example above schedules four searches and stops early if it delivers 100 places. Leave `locations` empty if your terms already name a place. Blank and repeated terms/locations are removed.

1. Enter your search terms and locations.
2. Set `country` to one ISO code or English country name (for example `IT`, `Italy`, `US`, `USA`, `United States`, `UK` or `England`). Native spellings such as `Italia` or `Deutschland` work too. An unrecognized country fails the run with a clear message before any search request is made, so nothing is charged.
3. Set `maxResults` to cap the total delivered rows. The default, 0, keeps the existing unlimited-total behavior. `maxResultsPerQuery` remains a separate 1-100 ceiling.
4. Start the Actor, follow the processed-query and delivered-place counts in the run status, and export your dataset.

#### Filters that save you money

- **`minReviews`** - keep only businesses with a known review count at or above your threshold. Google answers the same request with or without review counts at random, so when counts are missing the Actor refetches the query up to three times (fresh session each time) and merges by place ID, keeping existing records and known fields. Counts still unknown are skipped. Set 0 to include unknown counts without any refetch. **Behavior change:** older builds kept unknown-count rows even with a positive minimum; they are now excluded before billing.
- **`requireWebsite`** - skip businesses with no website, which is usually what you want if the next step is finding an email address.

Both filters run before charging, so you are not billed for rows you asked to exclude.

### Notes

- Results are deduplicated by Google place ID, falling back to name/address, then phone plus name when no address is present. Different branches sharing a phone number are preserved. `dedupeScope: "run"` deduplicates across queries; `"query"` deliberately allows the same place in different queries.
- `maxResultsPerQuery` is a ceiling, not a guarantee. Google returns what it has for that query, and thin niches in small towns return fewer.
- `country` sets the search region and proxy exit country, not a strict geographic filter. `language` sets the result language. Phone, website and review count can be missing when Google does not supply them; the Actor does not invent values.
- Requests rejected by the upstream service, transport failures and malformed responses are retried up to three times with a fresh proxy URL. If every query fails, the run ends succeeded with no delivered rows, no charge and a status message that starts with `No results:`. If some queries fail, valid results from other queries remain available and billable.
- The Actor stops fetching when it reaches `maxResults` or the Apify run charge limit. An aborted run can still contain billable results already delivered. Automatic resume/checkpoint recovery is not implemented in this version.
- `SUMMARY` reports queries planned/processed/failed, places delivered, review/website filter exclusions, delivered rows with unknown review counts, how many review-count refetches were made (`reviewRefetches`), and whether a result or charge limit ended the run. It is saved every 10 processed queries and on normal completion; after an abrupt stop it may lag behind the dataset. Filter counters count candidate rows, not unique businesses.

### Development checks

From this Actor directory, install `requirements.txt` and run `python -m unittest discover -s test`. Tests use synthetic Maps responses and mocked transport; they do not contain customer data or run paid searches. Live upstream completeness still requires a bounded smoke test after deployment.

### Related

Need verified email addresses rather than just websites? [Google Maps Leads with Verified Emails](https://apify.com/leadproof/google-maps-verified-leads) runs this sourcing step, crawls each website for a contact address, verifies deliverability over SMTP, and charges only for leads with a confirmed-deliverable email.

### Legal

This Actor collects information that businesses publish publicly on Google Maps. It does not collect personal data, log in to anything, or bypass access controls. You are responsible for using the output lawfully, including under GDPR, CCPA and any marketing or anti-spam rules that apply where you operate.

***

*Built by [LeadProof](https://leadproof.co) - verified local-business lead lists, built to order.*

# Actor input Schema

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

What to search for, e.g. "hvac contractor", "dentist". Each term is combined with every location below. Blank and repeated entries are removed.

## `locations` (type: `array`):

Where to search, e.g. "Phoenix, AZ", "Miami, FL", "Tel Aviv". Leave empty if your search terms already include a location.

## `maxResultsPerQuery` (type: `integer`):

Maximum places delivered per unique term/location combination (1-100). Filters may produce fewer results.

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

Deliver only places with a known review count at or above this minimum. Unknown counts are refetched up to three times, then skipped. 0 keeps places even when reviews are unknown. Skipped places are never charged.

## `requireWebsite` (type: `boolean`):

Skip places that have no website listed. Filtered places are never charged.

## `dedupeScope` (type: `string`):

'run' removes repeated place IDs across all queries. 'query' removes duplicates only within each query. Different locations of a business are kept even if they share a phone number.

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

Language code for result text, e.g. en, it, en-US or zh-CN.

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

One country per run, as an ISO code or English name: US, USA, United States, GB, UK, England, IT, Italy. Native spellings such as Italia or Deutschland work too. Sets the search region and proxy exit country; it does not restrict results to national borders.

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

Stop after this many delivered places across the entire run. 0 means no additional total limit. Filtered and duplicate places do not count.

## Actor input object example

```json
{
  "searchTerms": [
    "hvac contractor",
    "plumber"
  ],
  "locations": [
    "Phoenix, AZ",
    "Dallas, TX"
  ],
  "maxResultsPerQuery": 40,
  "minReviews": 0,
  "requireWebsite": false,
  "dedupeScope": "run",
  "language": "en",
  "country": "us",
  "maxResults": 0
}
```

# Actor output Schema

## `places` (type: `string`):

All scraped places as JSON.

## `placesCsv` (type: `string`):

The same places as a CSV file, ready for a CRM or spreadsheet import.

## `runSummary` (type: `string`):

Query count, failed queries and total places delivered.

# 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": [
        "dentist"
    ],
    "locations": [
        "Miami, FL"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadproof/google-maps-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 = {
    "searchTerms": ["dentist"],
    "locations": ["Miami, FL"],
}

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

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

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

```

## CLI example

```bash
echo '{
  "searchTerms": [
    "dentist"
  ],
  "locations": [
    "Miami, FL"
  ]
}' |
apify call leadproof/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/GIl27YeLQRaFS5mp0/builds/0qOoIaM5RO9sMP40r/openapi.json
