# Google Maps Scraper (`atomus/google-maps-scraper`) Actor

Turn any Google Maps search into clean business leads: names, addresses, phones, websites, ratings, and hours. You only pay for the businesses returned. Add decision-maker contacts, verified work emails, and full Google reviews as paid add-ons. No proxies, no login.

- **URL**: https://apify.com/atomus/google-maps-scraper.md
- **Developed by:** [Atomus APIs](https://apify.com/atomus) (community)
- **Categories:** Lead generation, Automation, Social media
- **Stats:** 8 total users, 5 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.00 / 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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Maps Scraper

Google Maps Scraper extracts business listings from Google Maps by keyword and location. Give it a search like "coffee shops in Berlin" and get back clean, structured records with names, addresses, phone numbers, websites, ratings, review counts, categories, coordinates, and opening hours. No proxies to configure, no login, no blocks to fight, and nothing to maintain.

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

Search any keyword and location and get one structured record per business:

| Field | Description |
|---|---|
| `name` | Business name |
| `original_title` | Untranslated business name, when Google localized the name |
| `address` | Full address |
| `address_info` | Structured address (street, city, zip, region, country) |
| `phone` | Phone number |
| `website` | Website URL |
| `domain` | Website domain |
| `contact_url` | Contact, menu, or appointment link on Google Maps, when available |
| `contributor_url` | Google Maps contributor profile of the listing's main contributor |
| `rating` | Star rating |
| `reviews_count` | Number of reviews |
| `rating_distribution` | Count of ratings per star (1 to 5) |
| `hotel_rating` | Hotel star rating, for hotels |
| `category` | Primary category |
| `additional_categories` | Secondary categories |
| `category_ids` | Google category identifiers (stable, language-independent) |
| `price_level` | Price level (inexpensive to very expensive) |
| `latitude`, `longitude` | GPS coordinates |
| `place_id`, `cid`, `feature_id` | Google place identifiers |
| `hours` | Opening hours by day |
| `status` | Whether the place is open now |
| `is_claimed` | Whether the owner has claimed the listing |
| `is_directory_item` | Whether the result is a directory entry rather than a single business |
| `book_online_url` | Reservation or booking link, when available |
| `total_photos` | Number of photos on the listing |
| `main_image` | Main photo URL |
| `local_justifications` | Google's "why this result" match justifications, when present |
| `rank`, `rank_group` | Position in the results (overall and within its group) |

### How do I scrape Google Maps?

1. Enter a **search query**, for example "coffee shops", "plumber", or "dentist".
2. Set a **location**, for example "New York, United States" or "Berlin, Germany".
3. Choose **how many businesses** you want back (1 to 700).
4. Run the actor. Results appear in the dataset, ready to fetch via the Apify API.

### Input

| Field | Required | Description |
|---|---|---|
| `query` | Yes | What to search for on Google Maps |
| `location` | No | City, region, or country. Defaults to United States |
| `locationCoordinate` | No | Search around a point: "latitude,longitude,zoom" (e.g. "40.7128,-74.0060,14z"). Overrides `location` |
| `languageCode` | No | Two-letter language code, e.g. "en", "fr", "de" (default "en") |
| `maxResults` | No | How many businesses to return, 1 to 700 (default 20) |

You get back exactly the number of businesses you ask for when they are available. Ask for 5 and you get 5, even when the area has more.

#### Free filters

Narrow the results at no extra cost. Filters apply to the whole search, up to the number of businesses you asked for, so a strict filter over 700 results really does look at all 700. Filtering itself is always free:

| Field | Description |
|---|---|
| `minRating` | Only businesses with at least this star rating (0 to 5) |
| `requireWebsite` | Only businesses that have a website |
| `skipClosed` | Exclude closed or permanently closed businesses |
| `exactMatch` | Only businesses whose name contains your exact search text |
| `category` | Only businesses whose category matches this text |

#### Find businesses that got a bad review recently

`recentBadReviewMaxRating` (1 to 4) plus `recentBadReviewPeriod` returns only the businesses that received a review at or below that rating inside the period. Set the rating to 1 and the period to 6 months and you get the businesses that took a 1-star review in the last six months, which is the list reputation agencies prospect from.

| `recentBadReviewPeriod` | Meaning | Cost |
|---|---|---|
| `3m` / `6m` / `12m` / `24m` | Got a bad review inside that window | Reads reviews (paid) |
| `all` | Ever got a bad review | **Free** |

`all` is free because the Google Maps search already returns each business's count of reviews per star rating. That count is complete, so "has ever been rated 1 star" is answered without reading a single review, and more accurately than sampling the most recent ones.

The dated periods cost money because that same count says how many 1-star reviews exist but never when they were left. Only the reviews carry a date, so those periods switch the reviews add-on on by themselves at 10 reviews per business. Two things follow, both stated on the input page:

- Businesses that have never been rated that low are discarded first, for free, so we only pay to read the reviews of real candidates.
- You are billed for the reviews we read, not only for the businesses that end up matching. Reading is what identifies a match. A search of 100 businesses where 60 have ever taken a 1-star bills reviews for 60 and may return 9.

For dated periods we read **only the bad reviews, and all of them**, never a sample of the newest few. The same free per-star count that powers `all` says how many reviews sit at or below your threshold, so a business with 8 reviews of 2 stars or fewer has all 8 read. A business whose only 2-star review is the 150th most recent is still found. Only businesses with more than 200 bad reviews are sampled.

The filter combines with the reviews add-on: set `maxReviews` as well and every business that matches comes back with its reviews attached, billed once.

Periods are deliberately coarse. Many Google reviews carry only a relative date ("a month ago"), which answers "last 3 months" cleanly and "the last 47 days" not at all, so we read those relative dates rather than discard the reviews that have them.

### Output

Each business is one record in the dataset:

```json
{
  "name": "Blue Bottle Coffee",
  "address": "66 Mint St, San Francisco, CA 94103",
  "address_info": { "address": "66 Mint St", "city": "San Francisco", "zip": "94103", "region": "California", "country_code": "US" },
  "phone": "+14156535228",
  "website": "https://bluebottlecoffee.com",
  "domain": "bluebottlecoffee.com",
  "rating": 4.5,
  "reviews_count": 1243,
  "rating_distribution": { "1": 20, "2": 15, "3": 60, "4": 300, "5": 848 },
  "category": "Coffee shop",
  "additional_categories": ["Cafe", "Espresso bar"],
  "price_level": "moderate",
  "latitude": 37.7825,
  "longitude": -122.4072,
  "place_id": "ChIJ...",
  "cid": "987654321",
  "hours": { "monday": [{ "open": { "hour": 7 } }] },
  "status": "open",
  "is_claimed": true,
  "is_directory_item": false,
  "book_online_url": "https://www.google.com/maps/reserve/...",
  "total_photos": 342,
  "main_image": "https://lh3.googleusercontent.com/...",
  "category_ids": ["coffee_shop", "cafe"],
  "feature_id": "0x8085808:0xabc123",
  "contributor_url": "https://maps.google.com/maps/contrib/1142866417270639...",
  "rank": 1,
  "rank_group": 1
}
```

With the **Business leads** add-on on, each business also carries a `leads` array (and each lead an `email` + `email_status` when the **Lead emails** add-on is on):

```json
{
  "name": "Neon Ambition",
  "domain": "neonambition.com",
  "leads": [
    {
      "full_name": "Jordan Slover",
      "title": "Founder",
      "seniority": "founder",
      "department": "c_suite",
      "linkedin_url": "https://www.linkedin.com/in/jordanslover",
      "company_name": "Neon Ambition",
      "company_industry": "advertising services",
      "company_employee_count": 16,
      "email": "jordan@neonambition.com",
      "email_status": "VALID"
    }
  ]
}
```

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

Pay per business, with no subscription:

- **$0.003 per business** returned ($3 per 1,000). Higher Apify plans (Gold and up) pay **$2 per 1,000**.
- **Minimum of 1 business per 100 results scanned.** Running the search costs us money whether or not your filters keep anything, so every search has a small floor. It only shows up when a filter throws almost everything away: ask for 700 and get 3 matches, and you are billed 7 businesses ($0.021) instead of 3. A search with no filters always bills exactly what you receive.

A search that returns 20 businesses costs 20 x $0.003 = $0.06, and scraping 1,000 businesses is $3.00. A search that returns nothing still charges for one business. Failed searches are free.

#### Paid add-ons

Optional extras, billed only when you turn them on:

- **Reviews** ($0.0008 per review, dropping to $0.0005 on higher Apify plans). Set "Reviews per business" to also pull Google reviews for each result, with review text, rating, author, author avatar, date, the photos the reviewer attached, and the owner's reply. Each business with reviews enabled bills a minimum of about 3 reviews (about $0.0024) even if it has fewer, because the reviews lookup runs and bills regardless. A search of 20 businesses with 10 reviews each adds about 20 x 10 x $0.0008 = $0.16.

- **Business leads** ($0.006 per lead found). Set "Maximum leads per place" to also find decision-maker contacts at each business — full name, job title, seniority, department, LinkedIn profile, and company data (industry, employee count). Filter by seniority (owner, founder, C-suite…) and department. **The number is per place found:** 5 leads across 200 places attempts up to 1,000 leads. You are only charged for leads actually found; businesses with no website, and leads not found, are free. Works only when a business has a website.

- **Lead emails** ($0.006 per email found). Turn on "Find lead emails" to also find and verify each lead's work email (with a VALID/INVALID status). This **requires "Maximum leads per place" above 0** — there is no email to find without first finding the person. You are only charged for emails successfully found; not-found emails are free.

Leads appear under a `leads` array on each business. A search of 50 places (with websites) finding ~2 leads each is about 100 x $0.006 = $0.60 for leads; adding emails found for ~70 of them adds about 70 x $0.006 = $0.42.

### Limits and how it handles them

- **Up to 700 businesses per search.** For a bigger market than one query can cover, vary the location or narrow the query.
- **A location is required for good results.** If you leave it empty, the search defaults to the United States.
- **You get exactly `maxResults` businesses.** If you ask for fewer than the area has, we return and bill for only what you asked; if the area has fewer, you get what exists.

### Use it with AI agents and MCP

Like any Apify actor, Google Maps Scraper can be called from the Apify API and used as a tool by AI agents and MCP clients. An agent can run it to pull fresh local-business data on demand, then feed the structured records straight into its workflow.

### Use cases

- **Generate qualified leads.** Build prospect lists of local businesses with names, websites, and phone numbers for your sales team.
- **Track competitors by area.** See where competitors operate and how they are rated across any city or region.
- **Run market research.** Measure how saturated a market is, find service gaps, and benchmark local businesses by rating and reviews.

### Google Maps Scraper vs the official API vs cookie tools

| | Google Maps Scraper | Official Google Places API | Browser or cookie tools |
|---|---|---|---|
| Setup | Query + location, run | API key, billing account, quotas | Install extension or manage a browser |
| Proxies / blocks | Handled for you | Not applicable | You manage them |
| Cost model | Pay per business returned | Per request, with field-based tiers | Your time and infrastructure |
| Bulk friendly | Yes, up to 700 per search | Rate-limited and quota-capped | Manual and slow |
| Maintenance | None | Key and quota management | Breaks when the site changes |

### FAQ

**Is there an official Google Maps API?**
Yes, the Google Places API. It requires a billing account, API key, and works per request with field-based pricing. This actor gives you the same core business data with a simpler input and per-result pricing.

**Is it legal to scrape Google Maps?**
Scraping publicly available business information is widely used for lead generation and market research. You are responsible for how you use the data and for complying with applicable laws and terms.

**How many businesses can I get per search?**
Up to 700 per search. To cover a bigger market, run several searches with different locations or narrower queries.

**Do I need a Google account or login?**
No. There is no login, no cookies, and no proxy setup.

### Disclaimer

This actor extracts publicly available business information from Google Maps. Use it in compliance with applicable laws and the terms of the services involved. It is not affiliated with or endorsed by Google.

# Actor input Schema

## `query` (type: `string`):

What to search for on Google Maps, e.g. "coffee shops", "dentist", "plumber".

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

Where to search, in Google's "City,State,Country" format, e.g. "New York,New York,United States" or "Paris,Ile-de-France,France". A country on its own also works ("United States", "Germany"). Use commas with NO spaces after them. Defaults to United States if left empty. Ignored when "Location by coordinates" is set. If a location isn't recognized you'll get a clear message and the run won't just fail.

## `locationCoordinate` (type: `string`):

Search around a point instead of a named place. Format: "latitude,longitude,zoom", e.g. "40.7128,-74.0060,14z" (zoom 3z to 21z, higher is tighter, default 17z). Overrides the Location field above. Free, same search cost.

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

Language of the Google Maps search results. Free, same search cost. Defaults to English.

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

How many businesses to return (1 to 700). We return exactly this many when Google has them, and you are billed per business returned. Google itself runs out of relevant local results for most searches well before 700, so asking for more than a city really has simply returns fewer, and you are not charged for businesses that don't exist.

## `minRating` (type: `integer`):

Only return businesses with at least this star rating (0 to 5). Businesses with no rating at all are also excluded.

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

Keep only businesses that have a website. Useful for building outreach lists, and required if you plan to use the contacts add-on below.

## `skipClosed` (type: `boolean`):

Exclude businesses marked closed or permanently closed.

## `exactMatch` (type: `boolean`):

Keep only businesses whose name contains your exact search text (case-insensitive).

## `category` (type: `string`):

Keep only businesses whose category matches this text, e.g. "coffee shop", "restaurant". Matches primary and secondary categories (case-insensitive). Keep in mind that Google's categories vary and businesses categorize themselves, so a category filter can exclude places you meant to keep.

## `maxReviews` (type: `integer`):

Number of Google reviews to fetch for each business (0 turns the add-on off). Each review comes with its text, star rating, author, date, photos, and the owner's reply.

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

Which reviews you get when you ask for fewer than a business has. "Newest first" is the right choice for spotting recent complaints, "Lowest rating first" for finding the worst ones regardless of when they were written.

## `recentBadReviewMaxRating` (type: `string`):

Returns only the businesses that got a review of this many stars or fewer. Pick 1 star to find the ones that recently took a 1-star review.

We read only the bad reviews, and all of them, so none is missed: a business with 8 reviews of 2 stars or fewer gets all 8 read. A bad review that is the 150th most recent is still found.

You pay for the reviews read on every business that could match, not only on the ones that end up in your results, because reading them is what tells the two apart.

## `recentBadReviewPeriod` (type: `string`):

How recent the bad review has to be. Only used when the filter above is set. "Any time" is free and reads nothing; every other option reads the bad reviews of the businesses that could match.

## `maxLeadsPerPlace` (type: `integer`):

Number of decision-makers to look for at each business (0 turns the add-on off). Only works for businesses that have a website, so pairing this with the "Only businesses with a website" filter above avoids wasting the search on places we can never enrich.

## `leadSeniority` (type: `array`):

Only look for people at these levels. Leave empty to accept any. To reach the owner of a small local business, pick Owner and Founder.

## `leadDepartment` (type: `array`):

Only look for people in these departments. Leave empty to accept any.

## `findLeadEmails` (type: `boolean`):

Look up the work email of every contact found above, and verify it (each email comes back marked VALID or INVALID). Does nothing on its own: without "How many contacts per business" set above there is no person to find an email for, and nothing is charged.

## Actor input object example

```json
{
  "query": "coffee shops",
  "location": "New York,New York,United States",
  "languageCode": "en",
  "maxResults": 20,
  "requireWebsite": false,
  "skipClosed": false,
  "exactMatch": false,
  "maxReviews": 0,
  "reviewsSort": "newest",
  "recentBadReviewPeriod": "6m",
  "maxLeadsPerPlace": 0,
  "findLeadEmails": false
}
```

# Actor output Schema

## `businesses` (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 = {
    "query": "coffee shops",
    "location": "New York,New York,United States"
};

// Run the Actor and wait for it to finish
const run = await client.actor("atomus/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 = {
    "query": "coffee shops",
    "location": "New York,New York,United States",
}

# Run the Actor and wait for it to finish
run = client.actor("atomus/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 '{
  "query": "coffee shops",
  "location": "New York,New York,United States"
}' |
apify call atomus/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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