# Google Maps Places Scraper — Phone, Website & Hours, No API Key (`webdatalabs/google-maps-places`) Actor

Extract 25+ fields per Google Maps place: phone, website, address, coordinates, rating, review count and opening hours. Search by term and location or paste Maps links. Splits large areas to pass Google's ~170-place limit. No login, no API key.

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

## Pricing

from $0.70 / 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 Places Scraper — Phones, Websites & Opening Hours

**Google Maps Places Scraper** turns a search term and a location into a clean list of businesses: name, category, full address, coordinates, phone, website, rating, review count and opening hours, one row per place. Use it to build local lead lists, map a market, or audit local-SEO competition without the Google Places API, without an API key and without logging in.

It is fast because it reads Google Maps' own data feed over plain HTTP, with no browser. It also goes **past Google's limit of about 200 places per search**: when a search hits that limit, the area is searched again tile by tile and duplicates are removed, so "all plumbers in a city" means more than the first 200.

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

| Field | What you get |
|---|---|
| `name`, `category`, `categories` | Business name and every Google category, primary first |
| `address`, `street`, `city`, `postalCode`, `state`, `countryCode` | Full address plus its parts, ready for CRM import |
| `latitude`, `longitude`, `timezone` | Exact pin and IANA time zone |
| `phone`, `phoneInternational` | Phone as shown on Google, and in E.164 format for dialers |
| `website`, `domain` | Website link and its bare domain, for email finders and matching |
| `rating`, `reviewsCount` | Average stars and number of Google reviews |
| `openingHours` | This week's hours, Monday to Sunday |
| `isClosed` | Whether Google marks the business as closed |
| `placeId`, `featureId`, `cid`, `googleMapsUrl` | Stable Google identifiers and a link to the place |
| `searchQuery`, `searchLocation`, `rank` | Which search found the place, and its position in Google's results |

The full field list with descriptions is in the **Output** tab. Views: **Overview**, **Contact (lead list)**, **Location** and **Full data**.

### Example output

One real record (a plumber in Austin, TX), exactly as the scraper delivers it:

```json
{
  "name": "Beyond Wow Plumbing & Drains",
  "category": "Plumber",
  "categories": ["Plumber", "Drainage service", "Gas installation service", "Hot water system supplier", "Water filter supplier"],
  "address": "3432 Greystone Dr, Austin, TX 78731",
  "street": "3432 Greystone Dr",
  "neighborhood": "Northwest Hills",
  "city": "Austin",
  "postalCode": "78731",
  "state": "Texas",
  "countryCode": "US",
  "latitude": 30.3578911,
  "longitude": -97.7482485,
  "phone": "(512) 601-6173",
  "phoneInternational": "+15126016173",
  "website": "https://beyondwow.com/?utm_campaign=gmb&utm_medium=gbp-austin&utm_source=google",
  "domain": "beyondwow.com",
  "rating": 4.9,
  "reviewsCount": 2596,
  "openingHours": [
    { "day": "Monday", "hours": "7 AM–6 PM" },
    { "day": "Tuesday", "hours": "7 AM–6 PM" },
    { "day": "Wednesday", "hours": "7 AM–6 PM" },
    { "day": "Thursday", "hours": "7 AM–6 PM" },
    { "day": "Friday", "hours": "7 AM–6 PM" },
    { "day": "Saturday", "hours": "Closed" },
    { "day": "Sunday", "hours": "Closed" }
  ],
  "isClosed": false,
  "timezone": "America/Chicago",
  "placeId": "ChIJpxUGhC_LRIYR2g941oH50ow",
  "featureId": "0x8644cb2f840615a7:0x8cd2f981d6780fda",
  "cid": "10147447246463438810",
  "googleMapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJpxUGhC_LRIYR2g941oH50ow",
  "searchQuery": "plumbers",
  "searchLocation": "Austin, TX, USA",
  "startUrl": null,
  "rank": 1,
  "scrapedAt": "2026-10-01T22:06:20.064Z"
}
```

### How to scrape Google Maps (quick start)

1. Type one or more **Search terms**, as you would in Google Maps: `plumber`, `dentist`, `italian restaurant`.
2. Set the **Location**: a city, region or postcode, such as `Austin, TX, USA` or `10115 Berlin`.
3. Set **Max places per search** and press **Start**.

You can also paste **Google Maps links or place IDs**: search links (the map area you see is searched), place links, `maps.app.goo.gl` share links, place IDs (`ChIJ...`) or cid numbers.

Every plumber and electrician in a city, past Google's per-search limit:

```json
{
  "searchQueries": ["plumber", "electrician"],
  "location": "Houston, TX, USA",
  "maxPlacesPerSearch": 2000
}
```

German dentists, with German category names:

```json
{
  "searchQueries": ["Zahnarzt"],
  "location": "München",
  "maxPlacesPerSearch": 300,
  "language": "de",
  "countryCode": "de"
}
```

Known places, by link or ID:

```json
{
  "startUrls": [
    "https://www.google.com/maps/place/?q=place_id:ChIJpxUGhC_LRIYR2g941oH50ow",
    "ChIJ6ymiaLC1RIYR4GW3rWuXlQw"
  ]
}
```

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

You pay per place delivered: one charge per row in your dataset, at the price shown on this page. Duplicates found by overlapping searches or map tiles are removed before delivery and never charged, and failed requests cost you nothing. Set a maximum cost per run in the run options and the scraper stops there.

### How many places can I get per search?

Google stops any one search after about 200 places (170 to 300 in our tests). With **Location** filled in and **Split large areas** on (the default, under Advanced), the scraper keeps going: it searches the area again in smaller map tiles, removes duplicates, and stops by itself once new tiles mostly return places it already has.

Put the city in **Location**, not inside the search term. "plumber" with Location "Houston, TX" can be split into tiles. "plumber in Houston" typed as one term cannot, because Google then ignores the map area.

### Use cases

- **Lead generation agencies:** pull every roofer, dentist or law firm in a region and filter `website = null` to find businesses without a website, or `reviewsCount < 20` to pitch review management.
- **Local SEO audits:** compare `rating`, `reviewsCount` and `rank` across competitors for the same search term and city.
- **Sales teams:** export the **Contact (lead list)** view with `phoneInternational` and `domain`, straight into a CRM or dialer.
- **Market research:** count businesses per category and postcode, or map them with `latitude` and `longitude`.
- **Data hygiene:** re-check a list of place IDs on a schedule and catch businesses that Google now marks `isClosed`.

### FAQ

#### Is it legal to scrape Google Maps?

The scraper collects publicly visible business information from Google Maps without logging in. It does not collect reviews or reviewer names. Phone numbers of sole traders can still count as personal data in some jurisdictions (for example under the GDPR), so use the data in line with the laws that apply to you, and check Google's terms for your use case. This is not legal advice.

#### Does it include reviews?

No. Google shows review text only to logged-in users and serves logged-out visitors a "limited view" without it, and this scraper never logs in. You get the rating and the review count.

#### Why are reviewsCount or openingHours sometimes null?

Null means Google did not provide the value, never a guess. Google sometimes answers with its logged-out limited view, which hides review counts and the weekly schedule. The scraper re-requests such pages, so this is rare, and the run summary counts any places it affected (`limitedViewPlaces`). Places that publish no hours, and places without any reviews, are null too.

#### Why do some places have no address?

Service-area businesses (plumbers, cleaners and others who come to you) often hide their street address on Google. Their `address` and address parts are null, while phone, website and coordinates are still delivered.

#### Does it include the price level ($$)?

No. Google does not show price levels to logged-out visitors, so the field is not offered rather than always empty.

#### Do I need a Google API key or a proxy?

No API key and no login. Apify Proxy is used by default. The run starts on cheap datacenter IPs and moves to residential IPs only if Google starts refusing requests. You can pick specific proxies, or turn the proxy off, under Advanced.

#### Where is the run summary?

Each run writes an **OUTPUT** record to its key-value store. It shows what happened per search term and link, how many places came from the area split, any duplicates skipped, and, if a run was stopped, a ready-made input for the searches that did not run.

### Related actors

- [Yelp Scraper - Local Business Data, Hours & Contacts](https://apify.com/webdatalabs/yelp-business-scraper-pro): the same local-business leads from Yelp.
- [Meta Ad Library Scraper - Facebook Ads, Days Running & Changes](https://apify.com/webdatalabs/meta-ad-library-scraper): see which local businesses advertise and for how long.
- [Google Jobs Scraper - Salary, Boards & Descriptions](https://apify.com/webdatalabs/google-jobs-intelligence): find businesses that are hiring, a strong buying signal.
- [Facebook Posts Scraper — Pages, Groups & Comments, No Login](https://apify.com/webdatalabs/facebook-posts-scraper): follow what local businesses post on their Facebook pages.

### Support

Found a problem or missing a field? Open an issue in the **Issues** tab and include the run ID; issues are answered there.

# Actor input Schema

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

What to search for, one term per line, as you would type it into Google Maps: plumber, dentist, italian restaurant. Put the city in "Location" below rather than in the term; that is what lets the scraper go past Google's limit of about 200 places per search.

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

Where to search: a city, region, postcode or address, for example "Austin, TX, USA" or "10115 Berlin". Used for every search term.

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

Upper limit for each search term. Google returns about 200 places per search; with a Location set, larger numbers are reached by splitting the area into map tiles (see Advanced).

## `startUrls` (type: `array`):

Optional. Google Maps search or place links (https://www.google.com/maps/place/..., https://maps.app.goo.gl/...), place IDs (ChIJ...) or cid numbers, one per line. A search link uses the map area it shows.

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

Language of category names and opening-hours text. Day names in the output are always English.

## `splitLargeAreas` (type: `boolean`):

When a search reaches Google's limit of about 200 places and you asked for more, the scraper searches the area again tile by tile and removes duplicates. Needs a Location (or a search link with a map area). Stops by itself once new tiles mostly return places it already has.

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

Leave out places that Google marks as closed. Off by default: closed places are delivered with isClosed = true.

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

Two-letter country code passed to Google (its gl parameter), for example us, de, gb. Affects ranking and formats, not the searched area.

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

Default: Apify Proxy, starting on datacenter IPs and switching to residential IPs only if Google starts refusing requests. Choose specific proxy groups or your own proxies to use exactly those; turn Apify Proxy off to send requests without a proxy.

## Actor input object example

```json
{
  "searchQueries": [
    "plumber",
    "dentist"
  ],
  "location": "Austin, TX, USA",
  "maxPlacesPerSearch": 20,
  "startUrls": [
    "https://www.google.com/maps/search/coffee/@30.2672,-97.7431,13z",
    "ChIJxcCceAi1RIYR9AqXT8eWDyc"
  ],
  "language": "en",
  "splitLargeAreas": true,
  "skipClosedPlaces": false,
  "countryCode": "us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `contact` (type: `string`):

No description

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

No description

## `full` (type: `string`):

No description

## `runSummary` (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 shop"
    ],
    "location": "Austin, TX, USA",
    "maxPlacesPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("webdatalabs/google-maps-places").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 shop"],
    "location": "Austin, TX, USA",
    "maxPlacesPerSearch": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("webdatalabs/google-maps-places").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 shop"
  ],
  "location": "Austin, TX, USA",
  "maxPlacesPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call webdatalabs/google-maps-places --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,webdatalabs/google-maps-places"
        }
    }
}
```

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/nMeYb9MpKagiUGhJN/builds/pom2YNzAm3gN59WvA/openapi.json
