# Google Maps Scraper — Places, Storefront vs Service-Area (`jaybird/google-maps-places-scraper`) Actor

Scrape Google Maps business listings by keyword and city: name, category, phone, website, address, hours, rating and booking link. Flags service-area businesses that hide their address. Clean, de-duplicated, no personal data. $1 per 1,000 places.

- **URL**: https://apify.com/jaybird/google-maps-places-scraper.md
- **Developed by:** [Jaybird Technologies](https://apify.com/jaybird) (community)
- **Categories:** Lead generation, SEO tools, Marketing
- **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

### What does Google Maps Places Scraper do?

**Google Maps Places Scraper** searches [Google Maps](https://www.google.com/maps) for any type of business in a city or region and exports **one clean record per place**: name, categories, phone, website, address, coordinates, rating, review count, opening hours, open/closed status, description and booking link.

It also tells you whether each business shows a **street address (storefront)** or hides it and serves customers at their location (a **service-area business**: plumbers, cleaners, mobile detailers, mobile IV nurses, contractors). That one flag makes it easy to build lists of mobile and home-service businesses.

Use it as a lightweight **Google Maps API alternative** (Google Places API) for local business data: no Google Cloud key, no login, **$1 per 1,000 places**.

### Why use this Google Maps scraper?

- 🏷️ **Storefront vs service-area flag.** `hasStorefront: false` marks businesses that hide their address, the usual sign of a mobile or home-service business.
- 🧹 **Clean, fixed output.** Every record has the same 24 fields; missing values are `null`, never absent. Ready for a spreadsheet, CRM or database without cleanup.
- 🔁 **De-duplicated across searches.** Search "plumber", "emergency plumber" and "drain cleaning" in one run and each business appears once, with every term that found it in `matchedSearchTerms`.
- 📍 **Stays in your area.** Google pads results with other cities once local ones run out; the scraper drops anything outside your city's radius or your custom polygon.
- 🗺️ **Big areas covered.** One Google Maps view tops out around 200 places, so for larger requests the scraper automatically splits the map into tiles and keeps going.
- 🔒 **Business data only.** No reviews, reviewer names, photos, owner names or emails, so you aren't collecting personal data you don't need.
- ⚡ **Fast and cheap.** No browser: about 1,000 places in 4 minutes, 2,500 in 9.

Run it on the Apify platform with scheduling, the API, webhooks, and integrations (Make, Zapier, Google Sheets, n8n), plus proxy rotation and monitoring built in.

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

| Field | Example | Notes |
|---|---|---|
| `name` | IV Bird | |
| `primaryCategory`, `categories` | Medical spa, IV therapy service | Google's categories, main one first |
| `phone` | +1 512-434-0651 | International format |
| `website` | https://ivbird512.com/ | Campaign tags (`utm_*`) removed |
| `hasStorefront` | true / false | Does the listing show a street address? |
| `address` | 1023 Springdale Rd Bldg 8e, Austin, TX 78721 | `null` for service-area businesses |
| `lat`, `lng`, `plusCode` | 30.2610, -97.6970, 7885+R2 Austin, Texas | |
| `rating`, `reviewCount` | 4.9, 212 | Counts only, no review text |
| `openingHours` | Monday: 8 AM–8 PM … | Monday first |
| `businessStatus` | OPERATIONAL | Or CLOSED_TEMPORARILY / CLOSED_PERMANENTLY |
| `description` | "Mobile IV therapy delivered…" | Owner description or Google's summary |
| `attributes` | Wheelchair accessible entrance, Identifies as women-owned | |
| `bookingUrl` | https://booking.mangomint.com/… | The listing's "Book online" link |
| `placeId`, `mapsUrl` | ChIJ…, https://www.google.com/maps/place/?q=place_id:… | Stable Google IDs |
| `matchedSearchTerms` | \["plumber", "emergency plumber"] | Every term that found the place |

### How to scrape Google Maps

1. Click **Try for free** and open the **Input** tab.
2. Enter one or more **search terms**, as you'd type them into Google Maps ("plumber", "coffee shop").
3. Enter a **location**: a city, metro or region ("Austin, TX, USA", "Manchester, UK").
4. Set **max places per search term** (20 for a quick look, 500+ for a whole city).
5. Click **Start**, then download the results as JSON, CSV, Excel or HTML, or pull them through the API.

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

You pay **$1 per 1,000 places** saved (less on higher Apify plans: down to $0.70 per 1,000 on Gold), plus a tiny start fee per run. There are no separate charges for details, contact fields or filters: every field above is included. Places that appear under several search terms are charged once.

| Places | Cost (Free plan pricing) |
|---|---|
| 100 | $0.10 |
| 1,000 | $1.00 |
| 10,000 | $10.00 |

Apify's free plan includes monthly credit, enough for thousands of places. Set a **maximum cost per run** in the run options and the scraper stops collecting when it reaches it, so you are never charged more.

### Input

See the **Input** tab for every option. Example:

```json
{
  "searchTerms": ["plumber", "emergency plumber"],
  "location": "Austin, TX, USA",
  "maxPlacesPerSearch": 100,
  "language": "en",
  "skipPermanentlyClosed": true
}
```

- **`customGeolocation`** (optional): a GeoJSON Polygon or MultiPolygon for an exact area, such as a metro outline or a set of ZIP codes. Places outside it are dropped.
- **`maxDistanceKm`** (optional): how far from the location's centre to keep places. Left empty, it is sized to the location automatically (about 63 km for Austin). `0` keeps everything Google returns.

### Output

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. One record per unique place:

```json
{
  "placeId": "ChIJcWJvCp6zRIYRlvUjxSjqGuQ",
  "name": "Mobile IV Medics",
  "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJcWJvCp6zRIYRlvUjxSjqGuQ",
  "matchedSearchTerms": ["mobile iv therapy", "mobile iv"],
  "searchLocation": "Austin, TX, USA",
  "primaryCategory": "IV therapy service",
  "categories": ["IV therapy service", "Health consultant", "Wellness program"],
  "website": "https://mobileivmedics.com/service-areas/texas/austin/",
  "phone": "+1 833-483-7477",
  "hasStorefront": false,
  "address": null,
  "serviceAreaText": null,
  "serviceAreas": [],
  "lat": 30.2801215,
  "lng": -97.771258,
  "plusCode": null,
  "rating": 4.9,
  "reviewCount": 60,
  "openingHours": [{ "day": "Monday", "hours": "8 AM–8 PM" }],
  "businessStatus": "OPERATIONAL",
  "description": "Mobile IV Medics brings IV hydration therapy directly to you in Austin, Texas…",
  "attributes": ["LGBTQ+ friendly"],
  "bookingUrl": "https://mobileivmedics.com/booking/",
  "scrapedAt": "2026-10-24T22:35:00.000Z"
}
```

The **OUTPUT** record in the key-value store summarizes each run: places found per term, unique places, how many were skipped (closed, outside the area) and how many were charged.

### Tips

- **More places per city:** add related terms ("plumber", "plumbing service", "drain cleaning"). Each finds businesses the others miss, and duplicates are removed for free.
- **Mobile and home-service businesses:** filter the results on `hasStorefront = false`.
- **Exact areas:** use `customGeolocation` with a polygon (for example from [geojson.io](https://geojson.io)) instead of a city name.
- **Keep runs cheap:** start with a small `maxPlacesPerSearch` to check the results, then scale up.

### Feedback and reviews

Something not working, or a field you need? Open an issue on the [Issues tab](https://apify.com/jaybird/google-maps-places-scraper/issues) and we'll take a look.

If this Actor saved you time, a short [review on Apify Store](https://apify.com/jaybird/google-maps-places-scraper/reviews) helps other people decide whether it fits their needs.

### FAQ

#### Does it extract emails or reviews?

No. It collects business-level data only: no review text, reviewer names, photos, owner names or emails.

#### Why are `serviceAreaText` and most About attributes empty?

Since 2026 Google shows the "Serves … and nearby areas" line and most of a listing's About tab (such as "Onsite services") only to signed-in users. This scraper doesn't log in, so those fields are usually empty. Every other public scraper has the same limit. The `hasStorefront` flag still reliably separates storefronts from service-area businesses, and the scraper fills the service-area fields automatically if Google shows them again.

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

One Google Maps view returns up to about 200 places. Above that, the scraper splits the area into map tiles automatically, typically reaching several thousand places per term in a large city. Very common categories in huge metros (for example "restaurants" in New York) are best split into several runs by borough or polygon.

#### Can I search outside the US?

Yes. Enter any city or region as the location and set `language` for local category names. Results come from Google Maps for that area.

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

Our Actors are ethical and do not extract any private user data, such as email addresses, gender, or location. They only extract what the user has chosen to share publicly. We therefore believe that our Actors, when used for ethical purposes by Apify users, are safe. However, you should be aware that your results could contain personal data. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

#### Can I use it through the API?

Yes. See the **API** tab for ready-made code in JavaScript, Python and cURL, or call it from Make, Zapier, n8n or any HTTP client.

### Other Jaybird Actors

- [Healthgrades Scraper](https://apify.com/jaybird/healthgrades-scraper): doctors and practices with specialties, ratings and accepted insurance.
- [Trustpilot Review Scraper](https://apify.com/jaybird/apify-trustpilot-review-scraper): reviews and ratings for any company on Trustpilot.
- [AI Search Visibility Tracker](https://apify.com/jaybird/ai-search-visibility-tracker): see whether ChatGPT-style AI answers mention your brand.

# Actor input Schema

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

What to search for, as you would type it into Google Maps ("plumber", "mobile iv therapy", "coffee shop"). Each term runs as its own search inside the location. Places found by several terms are saved once, listing every matching term.

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

One city, metro or region per run, as free text ("Austin, TX, USA", "Manchester, UK"). It is geocoded once and every term is searched inside that map area.

## `customGeolocation` (type: `object`):

Optional. A GeoJSON Polygon or MultiPolygon (or a Feature holding one) for a whole metro. When set, searches are centred on it and places outside it are dropped. "Location" is still used as the label in the output.

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

Cap per search term, before duplicates across terms are removed. One map view returns up to about 200 places; above that the area is split into smaller map tiles automatically, so large caps take longer and cost more.

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

Language for Google Maps results, e.g. "en", "es", "de". Affects category names, hours and descriptions.

## `skipPermanentlyClosed` (type: `boolean`):

Leave out places Google marks as permanently closed.

## `maxDistanceKm` (type: `integer`):

Drop places whose map pin is farther than this from the location. Leave empty for an automatic radius sized to the location (about 63 km for Austin); 0 turns the filter off. Ignored when a custom area is set. Places that hide their location are always kept.

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

Google blocks datacenter IPs on Maps, so US residential proxies are the default.

## Actor input object example

```json
{
  "searchTerms": [
    "plumber"
  ],
  "location": "Austin, TX, USA",
  "maxPlacesPerSearch": 20,
  "language": "en",
  "skipPermanentlyClosed": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `places` (type: `string`):

Default dataset with one record per unique place.

## `summary` (type: `string`):

Terms searched, places found per term, unique places, skipped and charged counts.

# 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": [
        "plumber"
    ],
    "location": "Austin, TX, USA",
    "maxPlacesPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jaybird/google-maps-places-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/bnVDIIvduqQVvdUsM/builds/9I0Gyvbn1YAyRP1St/openapi.json
