# Google Maps Scraper - Businesses and Leads (`sumitr_mardy/google-maps-scraper`) Actor

Find public Google Maps businesses by keyword and area. Phone, website, hours, categories, ratings and star distribution included at one flat price. Adaptive map splitting expands coverage. Export JSON or CSV, then enrich websites into verified leads.

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

## Pricing

from $0.80 / 1,000 places

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 — Businesses and Leads

Find public business listings by keyword and location. Phone numbers, websites, opening hours, categories, ratings and star distribution are included at one flat price. Export JSON, CSV or Excel. No Google API key, login, cookies or browser required.

### Quick start

```json
{"searchQueries":["coffee"],"locations":["Lisbon, Portugal"],"maxPlaces":10}
```

Queries run in every location. With no locations, write the location inside the query:

```json
{"searchQueries":["dentist in Austin TX"],"maxPlaces":50,"requireWebsite":true,"requirePhone":true}
```

### Input

| Field | Default | Meaning |
|---|---|---|
| searchQueries | \[] | Business keywords, not URLs. |
| locations | \[] | Place names geocoded through public Maps. Empty means use the query as written. |
| maxPlaces | 100 | Returned unique places per query/location, 1–5,000. Prefill is 10. |
| splitArea | true | Split busy viewports into four smaller areas, down to a 500 m viewport. |
| language | en | Maps UI language (hl). |
| country | us | Two-letter country code (gl). |
| minRating | 0 | Local minimum-rating filter. Missing rating counts as 0. |
| requireWebsite | false | Return only listings with a website. |
| requirePhone | false | Return only listings with a phone. |

At most 100 query/location combinations. Duplicate combinations are merged. Deduplication by placeId applies across all pages/tiles for each combination. The same place may appear under different queries or locations. Filtering happens before saving and charging; filtered-out listings are free.

### Coverage and automatic map splitting

A single Maps search often stops near 140–180 places. The Actor paginates each viewport; a short page before offset 120 marks it complete. A capped area is split into four quadrants. This continues until the result limit, 500 m minimum viewport, inventory exhaustion, spending limit or free-work guard.

Measured from Apify on 2026-10-30: the Maps-returned viewport for Manhattan produced **174 unique restaurants** from one wide search; adaptive splitting reached **1,000 unique restaurants across 7 viewports**, then stopped at the requested limit with 18 areas still pending. This is a measured coverage improvement, not an exhaustive census.

The location sets a Maps viewport/bias, not an administrative polygon. Nearby districts or cities may appear. A Maps geocoded viewport can be much wider than the named area; we do not claim exact city-boundary filtering. Smaller viewports and Maps ranking can still hide places. maxPlaces is a ceiling, not a guarantee.

### Output

One row per unique successful place per query/location. All 26 keys are always present, never null. Missing strings are "", counts 0, categories \[], openingHours {}, and starDistribution always has keys "1" through "5".

| Field | Meaning |
|---|---|
| inputIndex, query, location | Original combination index and search context. |
| placeId, cid, mapsUrl | Public place ID, decimal CID converted from its feature ID, and Maps link. |
| name, category, categories | Public business name, primary category and category array. |
| address, city | Displayed address and available locality label; not verified administrative membership. |
| phone, phoneE164, website | Listed display phone, E.164 phone and website. Empty if absent. |
| rating, reviewsCount | Displayed average rating and number of reviews. |
| starDistribution | Object with counts for 1-, 2-, 3-, 4- and 5-star ratings; absent values 0. |
| latitude, longitude | Public listing coordinates; 0 if absent. |
| openingHours, timezone | Displayed weekday/hour text and available timezone. Hours can reflect special/holiday dates. |
| position | One-based returned position within this combination. |
| status, subStatus, error, scrapedAt | Stable result, machine-readable reason, explanation and ISO timestamp. |

| status | subStatus | Place charge |
|---|---|---|
| ok | place | Yes |
| no_results | no_results | No |
| failed | invalid_input, location_not_found, http_CODE, blocked, parse_error, network_error, free_work_limit, cost_limit_reached | No |

A partially completed search can return successful places plus a final free error or limit row. A free failure does not remove charges for places already saved.

### Measured field availability

Direct Apify spike, 2026-10-30. Availability varies by business and changes over time; fields are not fabricated.

| Sample | Places | Phone | Website | Rating | Hours |
|---|---|---|---|---|---|
| Lisbon coffee viewport | 160 | 108 (67.5%) | 106 (66.3%) | 153 (95.6%) | 147 (91.9%) |
| Manhattan restaurant viewport with splitting | 1,000 | 985 (98.5%) | 878 (87.8%) | 988 (98.8%) | 970 (97.0%) |
| dentist in Austin TX | 60 | 60 (100%) | 60 (100%) | 60 (100%) | 60 (100%) |

Phone and website are included only when the business lists them. Use requirePhone/requireWebsite to filter, with no extra fee.

### Pricing

| Apify tier | Per place | Per 1,000 places |
|---|---|---|
| FREE | $0.002 | $2.00 |
| BRONZE | $0.0015 | $1.50 |
| SILVER | $0.0012 | $1.20 |
| GOLD+ | $0.0008 | $0.80 |

Everything listed above is included. No phone, website, details or filter surcharge. Default Actor start fee: $0.00005 per start event (per GB, minimum one). Failed, empty and locally filtered places have no place charge; start still applies. Dataset-item billing is disabled. The Actor pays platform usage under PPE. User spending limits may stop runs early.

### Lead pipeline: Maps → contacts → email verification

1. Collect business listings here and take non-empty website values.
2. Pass those URLs as **domains** to [Website Contact Finder](https://apify.com/sumitr_mardy/website-contact-finder), which returns its emails array alongside contacts and source information.
3. Pass collected email strings as **emails** to [Email Verifier](https://apify.com/sumitr_mardy/email-verifier).

Contact Finder input example (its field name was checked):

```json
{"domains":["https://example-business.com"],"maxPagesPerDomain":5}
```

Email Verifier input:

```json
{"emails":["contact@example-business.com"]}
```

Each Actor has its own pricing and spending limit. This Actor does not automatically run or charge the other two. Keep verification status and catch-all/unknown results when assessing contacts.

### API examples

```bash
curl -X POST "https://api.apify.com/v2/acts/sumitr_mardy~google-maps-scraper/run-sync-get-dataset-items?format=json" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"searchQueries":["coffee"],"locations":["Lisbon, Portugal"],"maxPlaces":10}'
```

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("sumitr_mardy/google-maps-scraper").call(
    run_input={"searchQueries":["dentist"], "locations":["Austin, TX"],
               "maxPlaces":50, "requireWebsite":True, "requirePhone":True}
)
places = list(client.dataset(run["defaultDatasetId"]).iterate_items())
websites = sorted({row["website"] for row in places
                   if row["status"] == "ok" and row["website"]})
## Optional next step; this runs a separately priced Actor:
## contacts_run = client.actor("sumitr_mardy/website-contact-finder").call(
##     run_input={"domains": websites}
## )
```

Use asynchronous runs for larger exports; download the dataset after completion.

### Use it from AI agents (MCP)

Hosted OAuth:

```json
{"mcpServers":{"maps":{"url":"https://mcp.apify.com/?tools=sumitr_mardy/google-maps-scraper"}}}
```

Hosted Bearer alternative:

```json
{"mcpServers":{"maps":{"url":"https://mcp.apify.com/?tools=sumitr_mardy/google-maps-scraper","headers":{"Authorization":"Bearer YOUR_APIFY_TOKEN"}}}}
```

Stdio:

```json
{"mcpServers":{"maps":{"command":"npx","args":["-y","@apify/actors-mcp-server","--tools","sumitr_mardy/google-maps-scraper"],"env":{"APIFY_TOKEN":"YOUR_APIFY_TOKEN"}}}}
```

Set an explicit maxPlaces, specify the area, and inspect status/subStatus. Ask for phone/website filters only when needed. Do not treat a missing field or 0 coordinate as verified data.

### Scope and limits

Not affiliated with Google. Public listing JSON only; no place detail page, review texts, photos, login or private data. Results reflect Google Maps for the requested language/country. Field positions can change; missing fields stay empty. Opening hours and star distributions may be unavailable or stale.

At most two query/location workers; pages sequential, with a 300 ms pause after each request before that worker's next request. Hard network deadlines and a free-work loss guard bound slow or unproductive work. The default prefill makes two requests: geocode plus one results page.

A CAPTCHA, /sorry/ or consent challenge stops that request and future requests in the run. No captcha solving, cookie/session reuse, residential proxy or IP rotation around a challenge. Non-challenge HTTP failures may be retried once directly, then once through Apify's default datacenter proxy. A guard or spending limit can end coverage before all tiles are visited.

# Actor input Schema

## `searchQueries` (type: `array`):

Business keywords, not URLs. Each query runs in every location.

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

Place names to geocode through public Maps. Leave empty for queries such as dentist in Austin TX.

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

Unique returned places per combination. Filters can require more pages.

## `splitArea` (type: `boolean`):

Split capped viewports into quadrants down to 500 m. Improves coverage but does not guarantee exhaustive or administrative-boundary results.

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

Google Maps listing language (hl).

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

Two-letter country code (gl).

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

Filter locally, free of charge. Missing rating counts as zero.

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

Only push and charge listings with a website.

## `requirePhone` (type: `boolean`):

Only push and charge listings with a phone number.

## Actor input object example

```json
{
  "searchQueries": [
    "coffee"
  ],
  "locations": [
    "Lisbon, Portugal"
  ],
  "maxPlaces": 10,
  "splitArea": true,
  "language": "en",
  "country": "us",
  "minRating": 0,
  "requireWebsite": false,
  "requirePhone": false
}
```

# Actor output Schema

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

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "coffee"
    ],
    "locations": [
        "Lisbon, Portugal"
    ],
    "maxPlaces": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("sumitr_mardy/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 = {
    "searchQueries": ["coffee"],
    "locations": ["Lisbon, Portugal"],
    "maxPlaces": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("sumitr_mardy/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 '{
  "searchQueries": [
    "coffee"
  ],
  "locations": [
    "Lisbon, Portugal"
  ],
  "maxPlaces": 10
}' |
apify call sumitr_mardy/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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