# Google Maps Scraper - Leads, Phones, Websites (`angaba92/google-maps-scraper`) Actor

Extract businesses from Google Maps by keyword and location: name, category, address, phone, website, rating, review count, opening hours, coordinates, place ID. Filter by rating, website, phone. No API key. $2.50 per 1,000 places.

- **URL**: https://apify.com/angaba92/google-maps-scraper.md
- **Developed by:** [Andres Garcia-Baquero Leon](https://apify.com/angaba92) (community)
- **Categories:** Lead generation, Travel, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/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 - Leads, Phones, Websites

Turn any Google Maps search into a lead list: **business name, category, full address, phone (local and E.164), website, rating, review count, opening hours, coordinates, place ID and Maps URL**. Type what you would search in Google Maps ("dentists in Chicago"), set a limit and export to CSV, Excel, JSON or your CRM. No Google API key, no browser, fast and cheap.

**Price: $2.50 per 1,000 places** ($0.0025 per place). Places removed by your filters (min rating, website, phone) are not charged.

### Why use it

| Capability | This Actor |
|---|---|
| Pricing | $2.50 per 1,000 returned places, plus start fee |
| Contacts | Phone, website and available hours in the same row |
| Collection limit | Configurable 1-400; availability depends on Google |

### Output example

```json
{
  "name": "Beyond Wow Plumbing & Drains",
  "category": "Plumber",
  "categories": ["Plumber", "Drainage service", "Gas installation service"],
  "address": "3432 Greystone Dr, Austin, TX 78731, United States",
  "street": "3432 Greystone Dr",
  "neighborhood": "Northwest Hills",
  "city": "Austin, TX, United States",
  "phone": "+1 512-601-6173",
  "phoneE164": "+15126016173",
  "website": "https://beyondwow.com/",
  "rating": 4.9,
  "reviewsCount": 2592,
  "latitude": 30.3578911,
  "longitude": -97.7482485,
  "timezone": "America/Chicago",
  "openingHours": {"Monday": "7 am–6 pm", "Saturday": "Closed"},
  "placeId": "ChIJpxUGhC_LRIYR2g941oH50ow",
  "cid": "10147447246463438810",
  "googleMapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJpxUGhC_LRIYR2g941oH50ow",
  "searchQuery": "plumbers in Austin TX",
  "rank": 1
}
```

### Input

| Field | Description |
|---|---|
| searchQueries | Search terms like "dentists in Chicago". Omitting searchQueries (e.g. `{}`) runs a demo. Explicit empty, blank or invalid targets fail with an OUTPUT diagnostic; they never become demo data. |
| location | Optional; appended as "in <location>". |
| maxPlacesPerQuery | 1-400, default 100. |
| minRating | Skip lower-rated places (not charged). |
| skipPlacesWithoutWebsite / skipPlacesWithoutPhone | Keep only contactable leads (not charged). |
| language | en, es, de... |

### Use cases

- **Lead generation for agencies and B2B sales**: local businesses with phone and website by niche and city.
- **Market research**: count and rate competitors in an area.
- **Local SEO**: see who ranks for "plumber in Austin" and their ratings. `rank` is the position in this returned source sequence, not a universal personalized Maps/SEO rank.
- **Data enrichment**: place IDs and coordinates for mapping and CRM matching.

### Tips for more results

This Actor inspects at most 400 source positions per query as an operational safety limit, not a guaranteed Google source limit or result count. For a whole city, split it: "dentists in Chicago 60601", "dentists in Lincoln Park Chicago", etc. Duplicates across searches in one run are removed automatically and not charged.

### Pricing

Pay per event: **$0.0025 per place** plus the tiny standard Apify actor-start fee. Set a max cost per run and the Actor stops before another result would exceed it. The start event may apply even when no results are returned.

### FAQ

**Do I need a Google API key?** No.
**Do you extract emails?** Not in this version; the website field lets you enrich emails with any contact-scraper.
**Do you extract reviews text?** No, only rating and review count.
**Why is reviewsCount sometimes empty?** Google omits the count in some regional result formats. `reviewsCountAvailable` reports whether the count was provided, including a valid zero.

**Are opening hours a complete week?** Not always. `openingHoursCoverage` is `full_week`, `partial` or `unavailable`; `openingHoursDays` counts non-null days. Missing days are unknown, not closed.

**Are queries matched exactly?** Google may correct or approximate your query. Each row includes `searchMatchType: google_interpreted`; no exact-name or keyword-match guarantee is made. The original query is retained in `searchQuery`.

**How are partial runs reported?** OUTPUT contains `queryStatus` per query with status and stopReason: source\_exhausted, result\_limit, scan\_limit, charge\_limit or upstream\_failure. `complete` means this returned result sequence ended, not that every business in the area was found. A scan limit or upstream failure is reported as partial; all upstream requests failing with no delivered places causes FAILED.

**How does deduplication work?** Place IDs (or feature IDs) are deduplicated before billing. Only delivered identities are globally excluded from later queries; unreturned page tails remain eligible.

### Troubleshooting

| Problem | Solution |
|---|---|
| Search in `failed` | Google temporarily rate-limited the request; rerun it. |
| Fewer places than requested | Inspect OUTPUT.queryStatus: source exhausted, filters, scan limit or upstream failure; broaden or split the search as appropriate. |
| Run stopped early | Max cost per run reached. |

Only publicly listed business data is collected. You are responsible for compliance with Google's terms and privacy laws (GDPR, CAN-SPAM) when contacting leads.

# Actor input Schema

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

What you would type in Google Maps, e.g. 'dentists in Chicago' or just 'dentists' plus Location. One place returned = one result.

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

Appended as 'in <location>' to search terms that do not already contain ' in '. E.g. 'Madrid, Spain'.

## `maxPlacesPerQuery` (type: `integer`):

Operational limit: inspects up to 400 source positions per query; results may be fewer after filters or upstream exhaustion. Not a guarantee of full coverage. Split large areas by neighborhood or ZIP.

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

Skip (and do not charge) places rated below this.

## `skipPlacesWithoutWebsite` (type: `boolean`):

Skip (and do not charge) places without a website.

## `skipPlacesWithoutPhone` (type: `boolean`):

Skip (and do not charge) places without a phone number.

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

Language code for categories and hours, e.g. en, es, de.

## Actor input object example

```json
{
  "searchQueries": [
    "dentists in Chicago",
    "plumbers in Austin TX"
  ],
  "maxPlacesPerQuery": 100,
  "skipPlacesWithoutWebsite": false,
  "skipPlacesWithoutPhone": false,
  "language": "en"
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        "dentists in Chicago",
        "plumbers in Austin TX"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("angaba92/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": [
        "dentists in Chicago",
        "plumbers in Austin TX",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("angaba92/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": [
    "dentists in Chicago",
    "plumbers in Austin TX"
  ]
}' |
apify call angaba92/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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