# Google Maps Scraper with Emails (`badr_xba/google-maps-scraper`) Actor

Google Maps places worldwide: name, category, address, coordinates, phone, website, rating, reviews, price level and hours. Optional contact extraction opens each business site for e-mails and socials. Tiled search beats Google's result cap. Example: 'restaurant', Los Angeles, 100 places.

- **URL**: https://apify.com/badr\_xba/google-maps-scraper.md
- **Developed by:** [Badr Bouargan](https://apify.com/badr_xba) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.25 / 1,000 place 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?

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 – Places, Phones, Emails & Websites

![Google Maps Scraper](https://api.apify.com/v2/key-value-stores/paDGjcmD1ZMsRJLZS/records/google-maps-banner.png?signature=1RH86Fd6Cp5kiaSerCP7g)

Extract **Google Maps places** for any search, in any city on Earth — **no API key, no Google Cloud account**. Get name, address, GPS coordinates, phone, website, rating, review count, opening hours and categories, and optionally **business e-mail addresses and social profiles** pulled from each company's own website.

One run over Los Angeles County returned **6,342 businesses in 44 minutes**. A 40-company run with contact extraction found **21 e-mail addresses** and 14 social accounts.

### What you can do with it

- **Build a prospect list**: every plumber in a metro area, with phone, e-mail and whether they already have a website.
- **Study a market**: how many Italian restaurants in a city, their ratings, review counts and price positioning.
- **Enrich a CRM**: paste your customer names as searches and pull back addresses, coordinates and opening hours.
- **Monitor competitors**: schedule a weekly run and watch ratings, review counts and new openings move.
- **Plan field work**: GPS coordinates for every location, ready for a map or a route planner.

### Why this Actor

- **Nothing to set up.** Type what you would type in Google Maps, name a place, press Start.
- **Goes past Google's limit.** Google returns 20 places per request and stops after a few pages. This Actor splits the area into four smaller squares whenever a square comes back full, and keeps splitting. A full county can be covered in one run.
- **E-mails, which Google Maps never provides.** Turn on *Extract contacts* and the Actor opens each business website, reads the contact page, decodes protected addresses, and returns e-mails plus Facebook, Instagram, LinkedIn, X, TikTok, YouTube and WhatsApp links.
- **Built for lead lists.** Filter on *no website*, *has phone*, *has e-mail*, minimum rating, minimum reviews, or category keywords.

### Switching from another Google Maps scraper

Field names and input names follow the conventions of the most used Google Maps scrapers on Apify Store, so an existing setup keeps working:

- **Input accepted as-is**: `searchStringsArray`, `locationQuery`, `maxCrawledPlacesPerSearch`, `language`, `countryCode`, `city`, `state`, `county`, `postalCode`, `categoryFilterWords`, `placeMinimumStars` (number or `four`, `threeAndHalf`…), `website` (`allPlaces` / `withWebsite` / `withoutWebsite`), `skipClosedPlaces`, `scrapeContacts`.
- **Output field names**: `title`, `categoryName`, `categories`, `totalScore`, `reviewsCount`, `location` (`lat`/`lng`), `street`, `city`, `state`, `postalCode`, `countryCode`, `neighborhood`, `phone`, `phoneUnformatted`, `website`, `url`, `placeId`, `cid`, `openingHours` (`[{day, hours}]`), `additionalInfo`, `permanentlyClosed`, `temporarilyClosed`, `wasOpenAtScrapeTime`, `rank`, `searchString`, `scrapedAt`.

Not included: review texts, photos, questions and answers, popular times and hotel-specific data. Those live on each place's own page, which this Actor does not open — that is also why it is fast and inexpensive.

### How it works

1. The area you type is resolved to a bounding box through OpenStreetMap — free, no key.
2. The Actor queries Google Maps' own map data for the centre of that box.
3. When a square returns a full set of results, it is divided into four and each part is searched again, down to the split depth you choose.
4. Every place is de-duplicated on its Google place ID, so overlapping squares never produce the same row twice.
5. Optionally, each website is fetched to find contact details.

### Step-by-step tutorial

1. In **Search queries**, enter one term per line: `dentist`, `plumber`, `coffee shop`.
2. In **Areas**, write `city, region, country` — for example `Los Angeles, California`. Ambiguous names like `Springfield` alone can resolve to the wrong place. You can instead give **Latitude**, **Longitude** and a **Radius** to cover an exact point.
3. Set **Max places per search** (0 means no limit).
4. For prospecting, switch on **Extract contacts**, and optionally **Only without website** or **Only with e-mail**.
5. Press **Start**. Results appear live; download as Excel, CSV or JSON, or pull them from the API.

### Input example

```json
{
  "searchQueries": ["dentist"],
  "locations": ["Los Angeles, California"],
  "maxPlacesPerSearch": 500,
  "extractContacts": true,
  "onlyWithoutWebsite": false,
  "minReviews": 5,
  "maxSplitDepth": 2,
  "languageCode": "en",
  "countryCode": "us"
}
```

### Output example

![Sample output](https://api.apify.com/v2/key-value-stores/paDGjcmD1ZMsRJLZS/records/google-maps-output.png?signature=kS51RsGppDQzxjzVUn5X)

Every row as JSON:

```json
{
  "title": "1212 Santa Monica",
  "placeId": "ChIJ8y6TjN-kwoARlvKn2JbD2mQ",
  "categoryName": "New American restaurant",
  "categories": ["New American restaurant", "Bar", "Cocktail bar"],
  "address": "1212 3rd Street Promenade, Santa Monica, CA 90401",
  "street": "1212 3rd Street Promenade",
  "city": "Santa Monica",
  "state": "CA",
  "postalCode": "90401",
  "countryCode": "US",
  "location": { "lat": 34.0178522, "lng": -118.4990463 },
  "phone": "(310) 576-9996",
  "phoneUnformatted": "+13105769996",
  "website": "http://www.1212santamonica.com/",
  "email": "info@1212santamonica.com",
  "instagramUrl": "https://www.instagram.com/1212santamonica",
  "totalScore": 4.5,
  "reviewsCount": 3481,
  "openingHours": [{ "day": "Monday", "hours": "5–10 PM" }],
  "additionalInfo": { "Accessibility": ["Wheelchair accessible entrance"] },
  "permanentlyClosed": false,
  "wasOpenAtScrapeTime": false,
  "rank": 1,
  "searchString": "restaurant",
  "url": "https://www.google.com/maps/place/?q=place_id:ChIJ8y6TjN-kwoARlvKn2JbD2mQ"
}
```

The **Leads** dataset view shows name, phone, e-mail, website, category, rating and address — ready to paste into a CRM.

### Pricing

Pay per result, shown on the Actor's pricing tab. Platform costs — proxy and compute — are included in that price; there is nothing else to pay, and no Google bill, because no Google API is used. A run of 500 places without contact extraction takes about three minutes.

### FAQ

**Do I need a Google API key?** No. Neither yours nor anyone else's.

**How many places can I get?** As many as Google shows for that area. Raise *Split depth* to 3 for dense cities; 6,342 places were collected in one 44-minute run over Los Angeles County.

**Why is an e-mail missing for some businesses?** Many small firms publish only a contact form. In a test on 40 plumbing companies, 21 e-mails were found — roughly two out of three of those that have a website.

**Some requests were refused.** Google throttles shared IPs. Set the **Proxy** field to the **RESIDENTIAL** group and run again.

**Can I schedule it?** Yes — use Apify **Schedules** to re-run daily or weekly and track new businesses over time.

### Integrations

- **Schedules**: run it every morning and keep a dated history of a market.
- **API**: every run's dataset is available as JSON, CSV or Excel through the Apify API.
- **Webhooks**: trigger your own service the moment a run finishes.
- **No-code tools**: pipe results into Google Sheets, Slack, Zapier or Make from the Integrations tab.

### Support

Found a bug or need a field that is missing? Open an issue on the Actor's **Issues** tab; every report is read.

**Did this Actor do the job?** Leaving a short review on its Store page takes a minute and helps other people find it — and tells me which feature to build next.

### Legal

This Actor reads **public** listing data that anyone can see on Google Maps without signing in, and public contact details businesses publish on their own websites. It never signs in, never solves CAPTCHAs on private areas, and collects no personal data beyond business contact information. Automated access may conflict with Google's Terms of Service, and how you use the data — including e-mail outreach rules such as GDPR and CAN-SPAM — is your responsibility.

### En français

Extrait les lieux Google Maps pour n'importe quelle recherche et n'importe quelle ville, **sans clé API**. Nom, adresse, coordonnées GPS, téléphone, site web, note, nombre d'avis, horaires, catégories — et, en option, l'**adresse e-mail** et les réseaux sociaux relevés sur le site de chaque entreprise. L'Actor découpe automatiquement la zone en carrés plus petits pour dépasser la limite de 20 résultats par requête, et supprime les doublons. Filtres de prospection : sans site web, avec téléphone, avec e-mail, note et nombre d'avis minimum, mots-clés de catégorie.

# Actor input Schema

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

What you would type into Google Maps: restaurant, dentist, plumber, hotel, gym… One search per line.

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

Cities, districts, regions or countries: “Los Angeles, California”, “Brooklyn, New York”, “Casablanca, Morocco”. Write “city, region, country” so ambiguous names resolve correctly.

## `maxPlacesPerSearch` (type: `integer`):

0 means no limit: the Actor stops when the area is exhausted. A large city can hold several thousand places.

## `latitude` (type: `string`):

To target an exact point rather than a city: centre latitude, e.g. 34.0522. Use together with Longitude and Radius.

## `longitude` (type: `string`):

Centre longitude, e.g. -118.2437.

## `radiusKm` (type: `integer`):

Half-width of the square area searched around those coordinates.

## `extractContacts` (type: `boolean`):

Opens each company's website and reads its public e-mail address plus Facebook, Instagram, LinkedIn, X, TikTok, YouTube and WhatsApp links. Google Maps never provides e-mails, so this is the only way to get them. It makes runs longer.

## `onlyWithEmail` (type: `boolean`):

Keeps only businesses whose e-mail address was found. Switches the option above on automatically.

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

Drops places Google marks as permanently or temporarily closed.

## `strictArea` (type: `boolean`):

When a square inside your area is nearly empty — ocean, mountains — Google widens the search to the whole country on its own. Switched on, the Actor drops those results from elsewhere. Switch it off to keep everything Google offers.

## `dedupeAcrossSearches` (type: `boolean`):

A business found by two different keywords appears only once in the final file.

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

Keep only places rated at least this, from 1 to 5.

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

Keep only places with at least this many reviews.

## `onlyWithPhone` (type: `boolean`):

Useful for prospecting: drops listings without a phone number.

## `onlyWithWebsite` (type: `boolean`):

Keeps only places that show a website.

## `onlyWithoutWebsite` (type: `boolean`):

The opposite: businesses with no website, often the best prospects for a web agency.

## `categoryKeywords` (type: `array`):

Keep only places whose Google category contains one of these words, e.g. pizza, italian.

## `excludeKeywords` (type: `array`):

Exclude places whose name contains any of these words.

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

Two-letter language code (en, fr, es, ar…). Sets the language of categories and opening hours.

## `countryCode` (type: `string`):

Two-letter country code (us, fr, ma…). Leave empty not to force a country.

## `maxPagesPerTile` (type: `integer`):

Google returns 20 places per page. 5 pages means up to 100 places per square before it is split.

## `maxSplitDepth` (type: `integer`):

When a square comes back full, the Actor divides it into four to reach the places hidden behind the limit. 2 means up to 16 squares per area, 3 up to 64 — slower, more complete.

## `maxConcurrency` (type: `integer`):

How many requests run at the same time. Each request returns 20 places, so 8 is enough to collect a thousand in well under a minute. Lower it only if Google starts refusing requests.

## `proxyConfiguration` (type: `object`):

Apify Proxy prevents Google from blocking requests. If requests are refused, pick the RESIDENTIAL group.

## Actor input object example

```json
{
  "searchQueries": [
    "restaurant"
  ],
  "locations": [
    "Los Angeles, California"
  ],
  "maxPlacesPerSearch": 200,
  "radiusKm": 5,
  "extractContacts": false,
  "onlyWithEmail": false,
  "skipClosedPlaces": false,
  "strictArea": true,
  "dedupeAcrossSearches": false,
  "onlyWithPhone": false,
  "onlyWithWebsite": false,
  "onlyWithoutWebsite": false,
  "languageCode": "en",
  "maxPagesPerTile": 5,
  "maxSplitDepth": 2,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Tableau des lieux : nom, catégorie, note, avis, téléphone, site web, adresse.

## `allFields` (type: `string`):

Données complètes : coordonnées GPS, horaires, description, identifiants Google.

## `runStats` (type: `string`):

Nombre de lieux, carrés parcourus, requêtes et doublons.

# 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": [
        "restaurant"
    ],
    "locations": [
        "Los Angeles, California"
    ],
    "maxPlacesPerSearch": 200,
    "maxConcurrency": 8,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("badr_xba/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": ["restaurant"],
    "locations": ["Los Angeles, California"],
    "maxPlacesPerSearch": 200,
    "maxConcurrency": 8,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("badr_xba/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": [
    "restaurant"
  ],
  "locations": [
    "Los Angeles, California"
  ],
  "maxPlacesPerSearch": 200,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call badr_xba/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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