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

🏷️ From $0.70 / 1K | Search Google Maps for any kind of business around any town or city worldwide. One row per business: name, address, phone, website, rating, and the emails published on its own website. Emails are charged only when found. No API key.

- **URL**: https://apify.com/wildorigins/google-maps-scraper.md
- **Developed by:** [Wild Origins](https://apify.com/wildorigins) (community)
- **Categories:** Lead generation, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 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

Find every business of a given kind around any town or city in the world on Google Maps, and get one row per business with its phone, address, website, rating and **the email addresses published on its own website**. Switch on the optional add on and each business also comes with the **owners and directors who list it on LinkedIn**.

### 🔍 What does Google Maps Scraper do?

Google Maps Scraper searches **Google Maps** for a trade in a place, such as plumbers in Austin, dentists in Manchester or accountants in Lyon, and returns a lead list you can call or email the same day. It reads each business's **own website** for the addresses it publishes, including addresses hidden behind Cloudflare's email protection, which a plain page read never sees.

Google Maps shows at most around 120 to 150 results for one map view. Google Maps Scraper gets past that by splitting the area into smaller map views wherever one view is full, and searching each one, so a whole city comes back rather than the first few pages.

The easiest way to try it: type a trade and a town, leave everything else as it is, and press Start.

### 🆚 What about the official route?

Google's own route is the [Places API](https://developers.google.com/maps/documentation/places/web-service/text-search). It is a good API, and if you already run a Google Cloud billing account it is worth knowing about. Three things it does not do for a lead list:

- **A text search returns at most 60 places**, across all pages, by Google's own documentation. A city's plumbers run to several hundred.
- **There is no email field** among the place fields Google documents. Phone and website are there, on the Enterprise tier.
- It needs an **API key and a billing account**, and every field group is billed separately.

Google Maps Scraper gives you every place the map will show for the area, the emails from each website, and optionally the decision makers, in one run with no key, no Google Cloud project and a clean table at the end.

### 📊 What data can I extract?

| | Field | What it is |
|---|---|---|
| 🏢 | `name` | Business name as listed on Google Maps |
| 🏷️ | `category`, `categories` | Primary Maps category, and every category on the listing |
| 📍 | `address`, `street`, `neighbourhood`, `town`, `postcode` | The address, and its parts |
| 📞 | `phone`, `phoneE164` | Phone as Google Maps prints it, and in E.164 form ready for a dialler or CRM. E.164 is filled when Google gives the international number, and for UK, US and Canadian national numbers |
| 🌐 | `website`, `websiteDomain` | Website from the listing, and its host |
| ✉️ | `email` | The first email published on the business website |
| 🙋 | `personalEmail` | The first email that reads as one person's mailbox rather than info@ |
| 📬 | `emails` | Every published email, each with `type` (role or personal), `source` and the `page` it was found on |
| 🔢 | `emailCount` | How many emails were found |
| 🧾 | `websiteStatus` | What happened with the website: emails-found, no-emails-published, no-website, not-own-site, unreachable and so on |
| 👔 | `decisionMakers` | With the add on: name, title, LinkedIn URL, location, the evidence tying them to the business, and an email built from the business's own naming pattern |
| 🧭 | `decisionMakerStatus` | found, none-found, no-website, search-blocked or not-requested |
| ⭐ | `rating`, `reviewCount` | Google rating and number of reviews |
| 🗺️ | `latitude`, `longitude` | Map pin |
| 🚐 | `serviceAreaBusiness` | True when the business hides its address and serves an area, so the pin is approximate |
| 🚫 | `permanentlyClosed`, `temporarilyClosed` | Closure flags from Maps |
| 🆔 | `placeId`, `cid`, `googleMapsUrl` | Google's identifiers and a link to the listing |
| 🔗 | `sources` | Which sources contributed to the row: googleMaps, website, linkedinSearch |
| 🔎 | `searchTerm`, `searchLocation`, `retrievedAt` | What found it, where, and when |

### 💡 Why scrape business leads from Google Maps?

Google Maps is the most complete directory of small businesses there is, because the businesses keep it up to date themselves: it is where their customers find them. What it lacks is the one thing outbound needs, an email address. Most small firms publish one on their own website, and that is where this Actor reads it.

Some of the ways it gets used:

- **Selling to a trade.** A merchant, a software company or a recruiter pulling every plumber, electrician or roofer in a region, with a phone number and an email for each.
- **Local services.** A web designer or an accountant looking for businesses in their own town that have no website or a poor one. `websiteStatus` says which.
- **Territory planning.** Every dental practice in a city, with coordinates and ratings, loaded straight into a map or a CRM.
- **Decision makers for small firms.** For businesses too small to appear in any company database, the owner's name and LinkedIn profile are often the only way to reach the person who decides.
- **Market sizing.** How many estate agents there are in a postcode district, and how they are rated.

### 🚀 How do I use Google Maps Scraper?

1. Open Google Maps Scraper on Apify and click **Try for free**.
2. Type one or more **search terms**, the way you would into Google Maps: plumbers, dentists, accountants.
3. Type a **location**: a town or city anywhere, written with its state or country when the name is shared, such as Austin, TX or Lyon, France. A UK postcode or postcode district such as LS6 works too. For several places at once, list them under **More locations**.
4. Leave emails on. Tick **Add LinkedIn decision makers** if you want the owners and directors.
5. Press **Start**, and download the results as a spreadsheet, CSV, JSON or Excel when the run finishes.

### ⬇️ Input

| Field | What it does |
|---|---|
| `searchTerms` | The kinds of business to find, one per line |
| `location` | A town or city anywhere in the world, or a UK postcode or postcode district |
| `locations` | Several of those, one per line, up to 50, all searched in one run. A business found twice is returned once |
| `radiusKm` | Optional. How far from the centre to search. Left empty, the area is sized to the place: a whole city for a city, about 2 km around a postcode |
| `maxPlacesPerSearch` | The most businesses per search term, default 200 |
| `scrapeEmails` | Read each business website for emails, on by default |
| `maxPagesPerSite` | Pages read per website, including the homepage, default 4 |
| `includeDecisionMakers` | Add LinkedIn owners, directors and founders, off by default |
| `maxDecisionMakersPerBusiness` | The most people per business, default 3 |
| `skipClosed` | Leave out permanently closed businesses, on by default |
| `proxyConfiguration` | Residential proxy, set for you. Leave it as it is |

```json
{
  "searchTerms": ["dentists"],
  "location": "Manchester",
  "maxPlacesPerSearch": 300,
  "scrapeEmails": true,
  "includeDecisionMakers": true
}
```

### ⬆️ Output

#### Table view

The **Overview** view shows each business with its category, town, postcode, phone, website, first email, rating and reviews. **Contacts** shows every email with its type and the page it came from. **Decision makers** shows the people.

#### JSON

An example row. The shape is exactly what a run returns; the business and the person are invented:

```json
{
  "name": "Example Plumbing & Heating",
  "category": "Plumber",
  "categories": ["Plumber", "Heating contractor"],
  "address": "12 Example Road, Headingley, Leeds LS6 1AA",
  "street": "12 Example Road",
  "neighbourhood": "Headingley",
  "town": "Leeds",
  "postcode": "LS6 1AA",
  "phone": "0113 000 0000",
  "phoneE164": "+441130000000",
  "website": "https://example-plumbing.co.uk/",
  "websiteDomain": "example-plumbing.co.uk",
  "email": "info@example-plumbing.co.uk",
  "personalEmail": "sam@example-plumbing.co.uk",
  "emails": [
    { "email": "info@example-plumbing.co.uk", "source": "website", "type": "role", "page": "https://example-plumbing.co.uk/" },
    { "email": "sam@example-plumbing.co.uk", "source": "website", "type": "personal", "page": "https://example-plumbing.co.uk/contact" }
  ],
  "emailCount": 2,
  "websiteStatus": "emails-found",
  "decisionMakers": [
    {
      "name": "Sam Example",
      "firstName": "Sam",
      "lastName": "Example",
      "title": "Director at Example Plumbing & Heating Ltd",
      "roleCategory": "leadership",
      "seniority": "director",
      "roleEvidence": "title",
      "linkedinUrl": "https://www.linkedin.com/in/sam-example",
      "location": "Leeds, England, United Kingdom",
      "evidence": "experience",
      "email": "sam@example-plumbing.co.uk",
      "emailPattern": "first",
      "emailConfidence": 1
    }
  ],
  "decisionMakerStatus": "found",
  "rating": 4.9,
  "reviewCount": 57,
  "latitude": 53.8196,
  "longitude": -1.5769,
  "serviceAreaBusiness": false,
  "permanentlyClosed": false,
  "temporarilyClosed": false,
  "placeId": "ChIJ0000000000000000000000",
  "cid": "4127435111269695969",
  "googleMapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJ0000000000000000000000",
  "sources": ["googleMaps", "website", "linkedinSearch"],
  "searchTerm": "plumbers",
  "searchLocation": "Leeds",
  "retrievedAt": "2026-09-27T12:00:00.000Z"
}
```

### 🗺️ How the whole area is covered

A single Google Maps view lists about 120 to 150 places, then starts padding the list with places from somewhere else entirely. Google Maps Scraper searches the whole area first, then splits any view that was still producing new businesses into four quarters and searches those, and so on down to views about 2 km across. A page that has drifted out of the area stops that view, and businesses whose pin falls outside the radius are left out. Every business is returned once, keyed on its Google place id, even when several views or search terms found it.

Service area businesses, the ones that hide their street address, carry an approximate pin. They are kept when Google listed them among the local results, and flagged with `serviceAreaBusiness`.

### ✉️ Where the emails come from

Each business's own website: the homepage, then the contact and about pages, read for mailto links, visible addresses, the addresses sites declare in their structured data, and **Cloudflare protected addresses**, which a site shows as "\[email protected]" until a script decodes them. The decoding is done here, so those addresses are found too.

Addresses that belong to someone else are left out: an ombudsman or regulator named on a legal page, a web agency's address in the footer when the site has its own, and placeholder text such as you@example.com. A Facebook page, a Linktree or a directory listing given as the website is not read, because its addresses belong to the platform. `websiteStatus` says which of these happened for every row.

### 👔 How the decision makers are found

With **Add LinkedIn decision makers** on, Google Maps Scraper asks a public search engine for LinkedIn profiles that name the business. No LinkedIn page is opened and no login is used. A person is returned only when their LinkedIn **Experience** field or headline names the business, their title reads as an owner, director, founder or other leader, and the listing shows they are in the UK: a UK location, the business's own town, or a UK company form such as Ltd. Small firms share names across countries, so a plumbing firm in Ohio called the same thing as one in Leeds is refused, and an employer ending in LLC, Inc or Pty always is. Staff whose listing shows no title are left out, and so is a name shared by several unrelated firms, because a wrong person is worse than no person.

Each person comes with an email built from the business's own naming pattern where the website shows one, with `emailConfidence` saying how sure that is. A built email is a guess, not an address the person published.

Small businesses are often not on LinkedIn at all, so expect a minority of businesses to come back with a decision maker. You are only charged for the people found.

### ⏱️ How long does a run take?

Businesses start arriving in the dataset within about a second of the map answering, because each one is written as soon as its website has been read rather than at the end. Measured on Apify with emails on and decision makers off:

| Search | Businesses | Whole run | First row |
|---|---|---|---|
| Plumbers in Leeds | 297 | 26 seconds | 1.1 seconds |
| Harrogate and Skipton, 36 kinds of business | 1,967 | 3 minutes 53 seconds | 1.2 seconds |

A larger run grows roughly in line with the number of businesses it finds. Decision makers add a search per business with a website, which runs alongside the website read.

### 💰 How much does it cost?

Google Maps Scraper is priced per result, with three events:

| Event | Charged when |
|---|---|
| Place | Once for every business delivered |
| Business emails | Once for a business that comes back with at least one email |
| Decision maker | Once for every person delivered |

A business with no email costs the place price only, and a business where no decision maker was found costs nothing for that part. The current prices for each event, and the lower rates on paid Apify plans, are on the Actor's Pricing tab.

### 🔌 Integrations

Schedule a run weekly to catch new businesses, send the rows to Google Sheets, HubSpot, Airtable, Slack, Make, Zapier or a webhook through Apify integrations, and use `placeId` to keep only the businesses you have not seen before. `phoneE164` drops straight into a dialler. Each row's `websiteDomain` is the input for [Company Email Finder](https://apify.com/spookyweb/company-email-finder) when you want more addresses at a business,.

AI agents can call it through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) as `spookyweb/google-maps-scraper`. The input schema is the tool definition, and every row carries `sources`, `websiteStatus` and per person `evidence`, so an agent can see where each value came from.

### 🔗 Using Google Maps Scraper with the Apify API

Start a run and wait for it with the [Apify API](https://docs.apify.com/api/v2):

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~google-maps-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms":["accountants"],"location":"Bristol","maxPlacesPerSearch":100}'
```

Or from Node.js with the [Apify client](https://docs.apify.com/api/client/js):

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('spookyweb/google-maps-scraper').call({
  searchTerms: ['accountants'],
  location: 'Bristol',
  maxPlacesPerSearch: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((b) => b.email).length, 'businesses with an email');
```

### ❓ FAQ

#### Does Google Maps Scraper need a Google API key?

No. It reads the same results Google Maps shows in a browser, over plain HTTP. There is no key and no Google Cloud project to set up.

#### Why does a business have no email?

`websiteStatus` says why: the listing has no website, the website is a social page, the site was down, or it publishes no address on the pages read. Plenty of small firms only take enquiries through a contact form.

#### What is the difference between a role and a personal email?

A role address, such as info@ or bookings@, is read by whoever handles enquiries. A personal address is one person's mailbox, often the owner's on a small firm. Both are published by the business itself.

#### Which countries does it cover?

Any country Google Maps covers. Give a town or city, with its state or country when the name is shared: Austin, TX, Lyon, France, Birmingham, AL. The place is found through OpenStreetMap, the search is asked of that country's own Google index, and a business Google puts in a different country is left out. In the UK you can also search by full postcode or postcode district.

#### Can one run cover several towns?

Yes. Put them in `locations`, one per line. Every town is searched with every search term, and a business that turns up in two neighbouring towns, or under two terms, comes back once and is charged once.

#### Why did I get fewer businesses than I expected?

The area may genuinely hold that many, or the radius may be tight. Leave `radiusKm` empty to size the search to the place, or raise it. `maxPlacesPerSearch` also caps each search term.

#### Are the decision makers verified?

Each one is tied to the business by LinkedIn's own Experience field or headline, which is in `evidence`. That is strong evidence the person works there. It is not a guarantee they still do, because a LinkedIn profile is only as current as its owner keeps it.

#### What happens when I hit my spending cap?

The run stops cleanly. Businesses already delivered are complete. A business whose place was paid for but not its emails comes back without them, marked `withheld-spending-cap`, rather than with data you were not charged for.

### ⚖️ Is it legal to scrape Google Maps and business websites?

Google Maps Scraper collects business information that businesses publish themselves, on Google Maps and on their own websites, for anyone to read. It does not log in anywhere. Email addresses of named people are personal data under UK GDPR, so if you use them for marketing you need a lawful basis, usually legitimate interests for business to business contact, and you must follow PECR, which allows unsolicited marketing email to corporate subscribers but not to sole traders and partnerships without consent. Check with your own legal adviser for your use. Apify's [blog post on web scraping legality](https://blog.apify.com/is-web-scraping-legal/) covers the general picture.

### 👍 Your feedback

Found a business that came back wrong, or an area that returned less than Google Maps shows? Open an issue on the **Issues** tab with the input you used, and it will be looked at. If you need a version for another country or a different source, ask there too.

### 🔎 You might also like

- [Company Email Finder](https://apify.com/spookyweb/company-email-finder): the email pattern and addresses at a company domain
- [Website Contact Scraper](https://apify.com/spookyweb/website-contact-scraper): emails, phones and social profiles from any list of websites
- [UK Food Hygiene Ratings](https://apify.com/spookyweb/uk-food-hygiene-ratings): every food business in an area with its FSA rating
- [UK Planning Applications](https://apify.com/spookyweb/uk-planning-applications): new planning applications near a postcode

# Actor input Schema

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

The kinds of business to find, one per line, up to 50, written the way you would type them into Google Maps: plumbers, dentists, accountants, estate agents. Each term is searched separately over the same area, and a business found by two terms is returned once.

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

A town or city anywhere in the world, written with its state or country when the name is shared, such as Austin, TX or Lyon, France. A UK postcode such as LS1 4AP or a district such as LS6 works too. The search area is sized to the place: a whole city for a city, about 2 km around a full postcode. To cover several places in one run, use locations instead or as well.

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

Several towns, cities or UK postcodes, one per line, up to 50, all searched in the same run with every search term. A business found in two overlapping places or by two terms is returned and charged once. A location that cannot be found is named in the status message and skipped.

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

How far from the centre of the location to search, from 1 to 30 km. Leave empty to size the area to the place itself. Businesses whose map pin falls outside the radius are not returned.

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

The most businesses to return for each search term, from 1 to 2,000. The search stops early when the area has no more. A dense trade in a city typically yields 150 to 400.

## `scrapeEmails` (type: `boolean`):

Read each business's own website, its homepage and its contact and about pages, for published email addresses, including ones hidden behind Cloudflare's email protection. Social profiles and directory listings given as a website are not read.

## `maxPagesPerSite` (type: `integer`):

How many pages of each business website to read for emails, including the homepage, from 1 to 8. Four covers the homepage, the contact page and the about page on almost every small business site.

## `includeDecisionMakers` (type: `boolean`):

For each business with its own website, look for the owners, directors and founders who list it on their public LinkedIn profile, through a public search engine. No LinkedIn page is opened and no login is used. Each person comes with a name, title, LinkedIn URL and an email built from the business's own naming pattern.

## `maxDecisionMakersPerBusiness` (type: `integer`):

The most people to return for one business when decision makers are on, from 1 to 10. People are taken in the order the search engine ranks them.

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

Leave out businesses Google Maps marks as permanently closed. Temporarily closed ones are always kept and flagged in the output.

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

Google Maps is read through Apify Proxy, starting on the account's default datacenter pool and moving a lane to residential addresses only after two refusals in a row. Leave this as it is unless you have your own proxies.

## Actor input object example

```json
{
  "searchTerms": [
    "plumbers"
  ],
  "location": "Leeds",
  "maxPlacesPerSearch": 30,
  "scrapeEmails": true,
  "maxPagesPerSite": 4,
  "includeDecisionMakers": false,
  "maxDecisionMakersPerBusiness": 3,
  "skipClosed": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `businesses` (type: `string`):

One row per business: name, category, address, postcode, phone in national and E.164 form, website, rating, review count, coordinates and Google Maps link, the emails published on its website with the page each was found on, and the LinkedIn decision makers when that add on is on.

# 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": [
        "plumbers"
    ],
    "location": "Leeds",
    "maxPlacesPerSearch": 30,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("wildorigins/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": ["plumbers"],
    "location": "Leeds",
    "maxPlacesPerSearch": 30,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("wildorigins/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": [
    "plumbers"
  ],
  "location": "Leeds",
  "maxPlacesPerSearch": 30,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call wildorigins/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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