# Google Maps Scraper Plus — Places, Leads & Change Monitoring (`cheapapi/google-maps-scraper-plus`) Actor

Fast HTTP Google Maps scraper: unlimited area coverage with an adaptive grid, multi-city runs, emails & socials with MX check, transparent lead score, change monitoring, honest fill-rate report and interactive map.

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

## Pricing

Pay per event + usage

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Plus - Leads, Emails & Monitoring

Extract businesses from any area of Google Maps — far beyond Google's ~120 results per map view — with contacts, emails, social profiles, a transparent lead score, top reviews and change tracking between runs. Built on Google Maps' own internal endpoints over plain HTTP (no browser), so it is **fast, cheap and does not need residential proxies**.

### Why this Actor

- **$2.00 per 1,000 places** on the Free and Bronze plans, **$1.40 on Silver** and **$0.75 per 1,000 on Gold and above** — full place details, 5 top reviews, photos, lead score and monitoring in that one price, no start fee and no add-on maze. For a typical lead-generation run (places + contacts + 5 reviews) that is 30–71 % less in event prices (depending on your plan; Apify platform usage is billed separately) than the most-used Google Maps Actor ([exact comparison](#price-comparison-for-a-typical-lead-generation-run)). If you only need a bare listing, some smaller Actors are cheaper (from $0.50 / 1,000).
- **Beyond the ~120-result cap**: an adaptive grid on the real boundary of each location found **84 %** of all places in *Auto* mode and **93 %** in *Thorough* mode in our test runs (**one test area, n=1**: a dense urban neighbourhood, September 2026 — see [How coverage works](#how-coverage-works) for the method and its limits).
- **70+ output fields per place**, including rating distribution, opening hours, amenities, booking links, and optional emails + 13 social networks from the business website.
- **Pay only for what is new**: contacts are charged only when the website adds something the listing didn't have; in monitoring mode unchanged places are neither output nor charged.
- **Honest run reports**: fill rate of every field, unresolved inputs and Google's "limited view" are reported, never hidden.
- **What it does not do:** it does not return employee-level contacts (no named decision-makers, job titles or personal LinkedIn profiles), does not guess personal email addresses and does not verify individual mailboxes (MX check only).

#### Compared with typical Google Maps scrapers

| | Typical Google Maps scrapers | **Google Maps Scraper Plus** |
|---|---|---|
| Results per search | Capped at ~120 per map view, or grid search as a paid add-on | **Adaptive grid on the real boundary** of every location — dense tiles are zoomed into automatically, empty ones are skipped (one test area, n=1: 84 % of all places in *Auto*, 93 % in *Thorough* — [method](#how-coverage-works)) |
| Locations per run | Often one | **Any number of locations × any number of search terms**, deduplicated across all of them |
| Place details, reviews, images | Separate add-on fees | **Included in the base price** (5 top reviews per place) |
| Full review history | Often broken since Google hid reviews from logged-out visitors in 2026 | **Up to 5,000 most recent reviews per place**, sortable, with owner replies |
| Popular times | Not available logged-out | **Popular times, price level, "people also search"** as an optional extended profile |
| Emails & socials | Add-on, often charged even when nothing is found | **Charged only when the website adds something new** (an email, or a phone/social profile not already on the listing); emails checked for a working mail server (MX), junk and template addresses removed |
| Website health | – | parked / dead / DNS error / social-only / redirects elsewhere, HTTPS, mobile, CMS, analytics, pixel, online booking |
| Lead qualification | Rare, opaque | **0–100 lead score with a point-by-point breakdown** + sales signals (`no_website`, `unclaimed_listing`, `website_outdated`…) |
| Recurring runs | Re-pay for the same places every time | **Monitoring mode**: NEW / UPDATED / UNCHANGED with a field-level diff; output only changes and pay only for them |
| Market analysis | Paid AI add-on, or nothing | **Free market & competitor report** (opportunity lists, category benchmarks, your business vs. nearest competitors) computed from the data — no AI, no hallucinations |
| Honesty | Silent nulls, "unlimited reviews" claims | **Run summary with fill rate per field**, per-record `dataQuality`, unresolved inputs listed, wrong geocodes refused instead of scraping the wrong area |
| Inputs | Often only search terms, sometimes rejects IDs | Search terms, cities/regions/postcodes/countries, GeoJSON polygons/circles, **any Maps URL, share links, Place IDs, CIDs, feature ids** |
| Privacy | Reviewer names and personal emails on by default | Reviewer data **off by default**; emails follow a documented business-mailbox allowlist (see below), other addresses are hidden unless you opt in |

*"Typical" column summarises public Apify Store listings of popular Google Maps scrapers, checked September 2026; features and prices of individual Actors change, so check the listing of any alternative you consider.*

### What data you get

The most used fields (a value Google does not show for a place is `null` or missing):

| Field | Type | Example |
|---|---|---|
| `title` | string | `"LOULOU"` |
| `categoryName` | string | `"French restaurant"` |
| `placeId` | string | `"ChIJLT_ZhspZwokR5p4Rn-hVEeQ"` |
| `address` | string | `"176 8th Ave, New York, NY 10011"` |
| `city` / `postalCode` / `countryCode` | string | `"New York"` / `"10011"` / `"US"` |
| `location` | object | `{ "lat": 40.7426678, "lng": -74.0001698 }` |
| `phone` / `phoneUnformatted` | string | `"(212) 337-9577"` / `"+12123379577"` |
| `website` | string | `"https://www.loulounyc.com/"` |
| `totalScore` | number | `4.7` |
| `reviewsCount` | integer | `4506` |
| `reviewsDistribution` | object | `{ "oneStar": 135, …, "fiveStar": 3738 }` |
| `businessStatus` | string | `"OPERATIONAL"` |
| `openingHours` | array | `[{ "day": "Monday", "hours": "11 AM–12 AM" }]` |
| `claimThisBusiness` | boolean | `false` |
| `reviews` | array | top 5 reviews with text, stars, date, owner reply |
| `imageUrls` | array | full-size photo URLs (usually 3–5) |
| `email` | string | `"info@loulounyc.com"` (leads enrichment) |
| `instagram` / `facebook` / `linkedin` | string | `"https://www.instagram.com/loulou.nyc"` |
| `contacts.websiteStatus` | string | `"ok"`, `"parked"`, `"social_only"` … |
| `contacts.emails` | array | `["info@loulounyc.com"]` — all business mailboxes found on the website |
| `contacts.emailsDetailed` | array | `[{ "email": "info@loulounyc.com", "type": "generic", "domainMatchesWebsite": true, "mxValid": true }]` |
| `contacts.phonesFromWebsite` | array | extra phone numbers from the website, E.164, deduplicated |
| `contacts.socialProfiles` | object | `{ "instagram": ["https://www.instagram.com/loulou.nyc"] }` (13 networks) |
| `contacts.websiteSignals` | object | `{ "https": true, "mobileFriendly": true, "bookingSystems": ["Resy"] }` |
| `leadScore` | integer | `95` |
| `leadScoreBreakdown` | array | `[{ "points": 15, "reason": "Has phone number" }, …]` — every point explained |
| `leadScoreCoverage` | number | `1` — share of the 10 score criteria that could be evaluated |
| `leadSignals` | array | `["no_website", "unclaimed_listing"]` |
| `changeStatus` | string | `"NEW"`, `"UPDATED"`, `"UNCHANGED"` (monitoring) |
| `searchQuery` / `searchLocation` | string | `"restaurant"` / `"Chelsea, Manhattan, New York"` |
| `scrapedAt` | string | ISO timestamp of the run |
| `dataQuality` | object | `{ "googleView": "full", … }` |

#### All fields, by group

- **Identity**: `title`, `categoryName`, `categories`, `placeId`, `cid`, `fid`, `kgmid`, `url`, `cidUrl`
- **Location**: `address`, `street`, `neighborhood`, `city`, `postalCode`, `state`, `countryCode`, `plusCode`, `location {lat,lng}`, `timezone`
- **Contact**: `phone`, `phoneUnformatted` (E.164), `website`, `websiteDomain`
- **Reputation**: `totalScore`, `reviewsCount`, `reviewsDistribution` (1–5 stars), `reviewsTags` (what reviews talk about, with counts), `reviews` (top reviews with text, stars, ISO date, per-aspect ratings like Food/Service/Atmosphere, visit context like "Price per person", owner reply, photos)
- **Business**: `businessStatus` (`OPERATIONAL` / `CLOSED_TEMPORARILY` / `CLOSED_PERMANENTLY`), `openingHours` (text + machine-readable periods), `additionalOpeningHours` (brunch, delivery, happy hour…), `openingHoursStatus`, `claimThisBusiness`, `ownerName`, `description`, `ownerDescription`, `ownerUpdates` (business posts)
- **Extras**: `isOpenNow`, `additionalInfo` (amenities: accessibility, service options, payments…), `highlights`, `actionLinks` (reservation / order online / shopping providers and URLs), `priceInfo` (hotel nightly price, fuel price), `hotel` (stars, check-in/out dates, amenities), `placesInside` (stores inside a mall, restaurants inside a hotel…), `imagesCount`, `imageUrls`, `mainImageUrl`
- **Leads** (optional): flat CSV-friendly `email`, `facebook`, `instagram`, `linkedin`, `twitter`, `youtube`, `tiktok` columns plus the full `contacts` object: `contacts.emails`, `contacts.emailsDetailed` (type `generic` / `business` / `published`, on-domain, free provider, MX valid), `contacts.personalEmailsHidden`, `contacts.multiLocationPage`, `contacts.phonesFromWebsite` (normalized, deduplicated), `contacts.socialProfiles` (Facebook, Instagram, X, LinkedIn, YouTube, TikTok, Pinterest, WhatsApp, Telegram, Discord, Threads, Yelp, Tripadvisor), `contacts.websiteStatus` (`ok`, `platform_page`, `social_only`, `parked`, `dns_error`, `blocked`, `redirects_elsewhere`, `under_construction`, `skipped_closed`…), `contacts.websiteSignals`
- **Scoring**: `leadScore`, `leadScoreCoverage` (share of criteria that could be evaluated), `leadScoreBreakdown`, `leadSignals`
- **Monitoring**: `changeStatus`, `changes[] {field, from, to}`, `metricsDelta` (rating / review-count movement), `firstSeenAt`
- **Meta**: `searchQuery`, `searchLocation`, `rank` (position in the Google result list of the map view it was found in), `scrapedAt`, `dataQuality`

`ownerName` is the display name of the Google Business Profile owner account (usually the business name itself), not a named decision-maker.

### How to use

#### In Apify Console (no code)

1. Open **Google Maps Scraper Plus** in Apify Console and click **Try for free** / **Start**.
2. In **What & where to search**, type your search terms (e.g. `dentist`) and one or more locations (e.g. `Austin, Texas, USA`). The form is prefilled, so you can also just press **Start** for a first test.
3. Optional: set **Max places per search term & location** (the form starts at 50 for a cheap first try; `0` = everything in the area).
4. Optional: turn on **Find emails, phones & social profiles on the website** for leads, add filters (minimum rating, only places without a website, unclaimed listings…) or **Track changes between runs**.
5. Optional: set **Maximum cost per run** in the run options to cap your spend.
6. Click **Start**. Watch results appear in the **Output** tab, then export them as CSV, Excel, JSON or HTML, or open the results map, run summary and market report from the Output tab.

#### Ready-to-paste input JSON

```json
{
  "searchQueries": ["dentist"],
  "locations": ["Austin, Texas, USA"],
  "maxPlacesPerQuery": 100,
  "scrapeContacts": true,
  "minRating": 4
}
```

#### Run it via the API

**curl** (runs the Actor and returns the dataset items when it finishes):

```bash
curl -X POST "https://api.apify.com/v2/acts/cheapapi~google-maps-scraper-plus/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries":["dentist"],"locations":["Austin, Texas, USA"],"maxPlacesPerQuery":100,"scrapeContacts":true}'
```

**JavaScript** (`npm install apify-client`):

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

const client = new ApifyClient({ token: '<YOUR_TOKEN>' });
const run = await client.actor('cheapapi/google-maps-scraper-plus').call({
    searchQueries: ['dentist'],
    locations: ['Austin, Texas, USA'],
    maxPlacesPerQuery: 100,
    scrapeContacts: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]?.title, items[0]?.email);
```

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("cheapapi/google-maps-scraper-plus").call(run_input={
    "searchQueries": ["dentist"],
    "locations": ["Austin, Texas, USA"],
    "maxPlacesPerQuery": 100,
    "scrapeContacts": True,
})
for place in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(place["title"], place.get("phone"), place.get("email"))
```

#### More input recipes

**Leads in several cities**

```json
{
  "searchQueries": ["plumber", "electrician"],
  "locations": ["Austin, Texas, USA", "Dallas, Texas, USA"],
  "maxPlacesPerQuery": 0,
  "scrapeContacts": true,
  "minRating": 4
}
```

**Web-design prospects: businesses without a website**

```json
{ "searchQueries": ["hair salon"], "locations": ["Manchester, UK"], "websiteFilter": "withoutWebsite", "maxPlacesPerQuery": 0 }
```

**Local-SEO prospects: unclaimed listings**

```json
{ "searchQueries": ["dentist"], "locations": ["Phoenix, Arizona"], "claimFilter": "unclaimed" }
```

**Your own polygon / radius** (paste from [geojson.io](https://geojson.io))

```json
{ "searchQueries": ["cafe"], "customGeolocation": { "type": "Point", "coordinates": [13.405, 52.52], "radiusKm": 2 } }
```

**Look up known places** (any mix of formats)

```json
{
  "placeIds": ["ChIJLT_ZhspZwokR5p4Rn-hVEeQ", "16434010972841156326", "0x8644eb01e1b6f12b:0xfd2a1ae49e89ca16"],
  "startUrls": [{ "url": "https://maps.app.goo.gl/…" }, { "url": "https://www.google.com/maps/search/pizza/@40.72,-74.0,15z" }]
}
```

**Weekly market monitoring** — schedule the Actor with:

```json
{ "searchQueries": ["coffee shop"], "locations": ["Lisbon"], "maxPlacesPerQuery": 0, "monitoringMode": true, "monitoringName": "lisbon-coffee", "outputOnlyChanges": true }
```

The first run stores a baseline (`BASELINE`). Later runs output only `NEW` and `UPDATED` places plus **verified** `REMOVED` places. Unchanged places are neither output nor charged.

- What counts as a change is configurable (`monitorFields`; default: name, category, address, phone, website, business status, opening hours, ownership). Rating and review-count movement is always reported in `metricsDelta` without flagging the place as changed, so busy places don't make every run expensive.
- Places that are temporarily incomplete (Google's limited view) never produce fake changes. A watched place that is now removed by your filters (for example it closed while "Skip closed places" is on) is not output, but the change still appears in the `CHANGES` record.
- **REMOVED is never guessed from absence alone.** A place missing from 3 consecutive complete runs is re-checked on Google by its id, and is reported only with a confirmed `reason`: `closed_permanently`, `merged` (Google replaced it with `newFid`), `moved_out_of_area` or `not_found` (Google no longer knows the id — confirmed twice, on different IPs, while a control place loads fine). If the re-check shows the place still exists, it was just a coverage miss and nothing is reported.
- A run whose search terms hit a limit, were truncated or had failed search pages does not count misses at all. With monitoring on and no per-search limit set, each area is searched without a limit (in our one-area test, n=1, the grid found ~84 % of places per run in *Auto*, ~93 % in *Thorough* — the verification above keeps that from ever turning into false removals).
- `NEW` means the watch sees the place for the first time. That includes businesses an earlier run's grid did not reach (in our one-area test, n=1, Auto covered ~84 % of the area per run, Thorough ~93 %), so `NEW` records carry `likelyNewBusiness: true` when the place has 3 reviews or fewer — the typical signature of a genuinely new business. Use `coverage: thorough` for the most stable watches.
- Each run also writes a compact `CHANGES` record to its key-value store — point a webhook or Slack/Zapier integration at it for alerts.

### Use cases

- **Lead generation**: phone, website, email and social profiles of every plumber, dentist or restaurant in several cities, ranked by `leadScore`.
- **Agency prospecting**: businesses without a website (web design), unclaimed listings (local SEO), no online booking or outdated websites (`leadSignals`).
- **Market research**: how many competitors, their ratings, review volume and categories in an area — plus the free market report.
- **Competitor tracking**: compare your business with its 10 nearest competitors (`myBusiness`) and watch them with scheduled monitoring runs.
- **Reputation analysis**: up to 5,000 recent reviews per place with star ratings, per-aspect ratings and owner replies.
- **Data enrichment**: look up known places by Place ID, CID or Google Maps URL to refresh phone numbers, hours and status in your CRM.

### Advanced options

Everything below is optional; the defaults are what you get if you leave the field empty.

| Input | Default | What it does |
|---|---|---|
| `searchQueries` | – (form prefill: `restaurant`) | Search terms; every term is searched in every location |
| `locations` | – (form prefill: `Chelsea, Manhattan, New York`) | Cities, districts, regions, postcodes or countries |
| `customGeolocation` | – | Your own GeoJSON area: Polygon, MultiPolygon, Point with `radiusKm`, Feature/FeatureCollection |
| `countryCode` | – | Two-letter ISO code; disambiguates locations, sets Google's region, or searches the whole country |
| `startUrls` | – | Google Maps URLs (place, search, `?cid=`, share links) |
| `placeIds` | – | Place IDs, CIDs or feature ids |
| `maxPlacesPerQuery` | 200 (no limit in monitoring mode; form prefill 50) | Places per search term × location, `0` = no limit |
| `maxPlacesTotal` | `0` (no cap) | Hard cap for the whole run |
| `coverage` | `auto` | `auto` adaptive grid, `thorough` dense grid, `fast` single map view |
| `includeNearbyKm` | `0` | Also keep places up to this many km outside the boundary |
| `pointRadiusKm` | `3` | Search radius when a location resolves to a point |
| `minRating` | – | Minimum star rating |
| `minReviews` / `maxReviewsCount` | – | Minimum / maximum number of reviews |
| `categoryFilter` / `excludeCategories` | – | Keep / drop places whose category contains these words |
| `websiteFilter` | `all` | `withWebsite` / `withoutWebsite` |
| `phoneFilter` | `all` | `withPhone` / `withoutPhone` |
| `claimFilter` | `all` | `claimed` / `unclaimed` |
| `skipClosedPlaces` | `false` | Drop permanently and temporarily closed places |
| `nameMatch` | `any` | `containsQuery` or `exact` for brand searches |
| `scrapePlaceDetails` | `true` | Full details (rating distribution, top reviews, places inside…) |
| `maxReviews` | `5` | Reviews per place, up to 5,000 (first 5 included in the place price; above 5, each place's review history costs at least the review minimum) |
| `reviewsSort` | `newest` | `newest`, `mostRelevant`, `highestRating`, `lowestRating` |
| `reviewsStartDate` | – | Only reviews published on or after this date |
| `reviewsPersonalData` | `false` | Include reviewer names and profile links |
| `maxImages` | `5` | Photo URLs per place (0–10) |
| `placeExtras` | `false` | Extended profile: popular times, price level, people also search, review topics, attributes ($0.0022 per place looked up, also when Google has none) |
| `scrapeContacts` | `false` | Emails, phones and social profiles from the business website + website health |
| `maxContactPagesPerSite` | `4` | Pages visited per website, including the homepage (1–10) |
| `verifyEmailDomains` | `true` | Drop emails whose domain cannot receive mail (MX check) |
| `includePersonalEmails` | `false` | Also return addresses that may belong to a person |
| `leadScoring` | `true` | `leadScore`, `leadScoreBreakdown`, `leadSignals` |
| `monitoringMode` | `false` | Track changes between runs (NEW / UPDATED / UNCHANGED / REMOVED) |
| `monitoringName` | fingerprint of terms + locations | Runs with the same name share memory |
| `monitorFields` | name, category, address, phone, website, status, hours, ownership | Fields that count as a change |
| `outputOnlyChanges` | `false` | Output and charge only new and changed places (switches monitoring on) |
| `marketReport` | `true` | Free market & competitor report |
| `myBusiness` | – | Your Place ID / URL / CID for the competitor comparison. It is looked up and output (charged as one place) even when the filters would exclude it |
| `language` | `en` | Language of names, categories and reviews |
| `outputMap` | `true` | Save `results-map.html` |
| `proxyConfiguration` | Apify datacenter proxy | Proxy for Google Maps requests |
| `useProxyForWebsites` | `false` | Also use the proxy when visiting business websites |
| `maxConcurrency` | `10` | Parallel requests (1–50) |
| `limitedViewRetries` | `4` | Re-requests for places Google served in its "limited view" (0–10) |

### Example output (shortened)

One place from a `restaurant` search in `Chelsea, Manhattan, New York` with website contacts on (the same place as in the field table above):

```json
{
  "title": "LOULOU",
  "categoryName": "French restaurant",
  "address": "176 8th Ave, New York, NY 10011",
  "city": "New York",
  "postalCode": "10011",
  "location": { "lat": 40.7426678, "lng": -74.0001698 },
  "phone": "(212) 337-9577",
  "phoneUnformatted": "+12123379577",
  "website": "https://www.loulounyc.com/",
  "totalScore": 4.7,
  "reviewsCount": 4506,
  "reviewsDistribution": { "oneStar": 135, "twoStar": 101, "threeStar": 105, "fourStar": 427, "fiveStar": 3738 },
  "businessStatus": "OPERATIONAL",
  "openingHours": [{ "day": "Monday", "hours": "11 AM–12 AM", "periods": [{ "open": "11:00", "close": "24:00" }] }],
  "actionLinks": [{ "type": "reservation", "label": "Reserve a table", "provider": "Resy", "url": "https://resy.com/…" }],
  "reviewsTags": [{ "title": "speakeasy", "count": 49 }, { "title": "outdoor seating", "count": 64 }],
  "placesInside": [{ "title": "Live at Loulou", "category": "Cocktail bar", "totalScore": 4.3, "reviewsCount": 84 }],
  "reviews": [{
    "stars": 5,
    "text": "Dinner, Drinks & a Show — Good Vibes! 9/10 …",
    "publishedAtDate": "2026-08-26T22:19:32.347Z",
    "reviewDetailedRating": { "Food": 5, "Service": 5, "Atmosphere": 5 },
    "reviewContext": { "Meal type": "Dinner", "Price per person": "$90–100" },
    "responseFromOwnerText": "Thank you so much for such a wonderful and thoughtful review…"
  }],
  "contacts": {
    "websiteStatus": "ok",
    "emails": ["info@loulounyc.com"],
    "emailsDetailed": [{ "email": "info@loulounyc.com", "type": "generic", "domainMatchesWebsite": true, "freeProvider": false, "mxValid": true }],
    "socialProfiles": { "instagram": ["https://www.instagram.com/loulou.nyc"] },
    "websiteSignals": { "https": true, "mobileFriendly": true, "bookingSystems": ["Resy"], "googleAnalytics": ["G-TMTMDEEXE5"], "technologies": [] }
  },
  "leadScore": 95,
  "leadScoreCoverage": 1,
  "leadSignals": [],
  "searchQuery": "restaurant",
  "searchLocation": "Chelsea, Manhattan, New York",
  "dataQuality": { "source": "details", "googleView": "full", "detailAttempts": 1, "openingHoursPartial": false, "note": null }
}
```

### How coverage works

1. Every location is geocoded (OpenStreetMap) to its **real boundary**, not a bounding box. Ambiguous names that only match unrelated places are **refused with a warning** instead of scraping the wrong area.
2. The area is covered with tiles. Each tile is searched page by page while Google's results stay inside the tile.
3. If Google cuts the list while it is still local (it never returns more than ~120–200 places per map view), the tile is **split into four and zoomed in** — repeatedly, only where businesses are dense.
4. Places are deduplicated across tiles, search terms and locations, and filtered to the boundary (`includeNearbyKm` widens it).

Coverage modes, measured in **one test area (n=1)**: **Auto** found 84 % of all places with 14 % of the requests; **Thorough** 93 % with 34 %; **Fast** is a single map view per location.

**How these numbers were measured (September 2026, while tuning the grid):** one dense urban neighbourhood was searched with each mode, and the places found were compared against a reference exhaustive crawl of the same area (631 search requests, taken as 100 % of the places Google returns there). "Requests" are search requests relative to those 631. This is a single-area internal test, not an independent benchmark: real coverage depends on density, search term and area size, so treat the figures as indicative. Every run reports its own numbers (places per search term × location, search requests, tiles split) in `RUN_SUMMARY`.

### Lead score (0–100)

Points below add up to at most 95 and are scaled to 0–100. When the review count is unknown (Google's limited view), a neutral 6 points is given. Scores are comparable between records of the same run configuration; `leadScoreCoverage` shows how many of the 10 criteria were known.

| Points | Condition |
|---|---|
| +10 | Operational (permanently closed = score 0) |
| +15 | Has a phone number |
| +10 | Has a website |
| +20 / +12 | Email on the business's own domain with a valid mail server / any email |
| +10 / +5 | 3+ social profiles / at least one |
| +10 / +7 | Rating ≥ 4.5 / ≥ 4.0 |
| +10 / +6 | 100+ / 20+ reviews |
| +5 | Claimed listing |
| +5 | Opening hours published |

`leadSignals` lists opportunities and red flags: `no_website`, `website_is_social_profile`, `website_parked`, `website_dns_error`, `website_no_https`, `website_not_mobile_friendly`, `website_outdated`, `no_analytics`, `no_online_booking` (no booking widget on the site, no Google reservation/appointment link and the website is not a booking platform), `no_email_found`, `no_social_profiles`, `unclaimed_listing`, `low_rating`, `few_reviews`, `few_photos`, `no_opening_hours`, `temporarily_closed`. Every point is returned in `leadScoreBreakdown`, so you can re-weight it yourself.

### Free market & competitor report

Every run also saves **`market-report.html`** (and `MARKET_REPORT` as JSON) — computed only from the places of this run, with no extra requests and no AI, so every number is traceable to the dataset:

- **Overview & categories**: median rating and reviews, share rated 4.5★+, without a website, unclaimed, with online booking — per category.
- **Opportunities**: established businesses without a website, unclaimed listings, rising stars (≥4.6★ with 5–60 reviews), busy but poorly rated (<3.8★, 50+ reviews), appointment businesses without online booking.
- **Leaders**: most reviewed and best rated (only places with a typical number of reviews or more — no 5.0★ with 3 reviews).
- **What reviews talk about**: Google's review topics shared by 3+ businesses of the area.
- **Your business** (optional `myBusiness`): your place against its 10 nearest competitors of the same category — rating, reviews, photos, business posts, website, online booking — with strengths and weaknesses.

The report states its own limits: small samples (<30 places) and runs that stopped at a limit are flagged, and counts are lower bounds (in our one-area test, n=1, the grid found ~84 % / ~93 % of the area per run). For a real market view, run with `maxPlacesPerQuery: 0`.

### Run summary & map

Every run stores in its key-value store:

- **`RUN_SUMMARY`** — places per search term × location, search requests, failed search pages, tiles split, filtered places by reason, Google rate limits hit, places where Google only served its "limited view", **fill rate of every field**, unresolved inputs and monitoring counts.
- **`CHANGES`** (monitoring mode) — the NEW / UPDATED / REMOVED places of this run.
- **`results-map.html`** — an interactive, clustered map of all places.

### Pricing

**Typical cost:** 1,000 places with website contacts cost $3.50 on the Free plan and $1.12 on Gold (worst case, contacts charged on every place); Apify platform usage is billed separately.

**Apify Free plan:** Apify does not pay developers for usage on its Free plan, so on the Free plan the two add-ons that cost us data fees — the **full review history** (more than 5 reviews per place) and **extended profiles** — share an allowance of **$0.25 per calendar month**. Place data, contacts and the first 5 reviews are not limited. When the allowance is used up, those add-ons are skipped with a clear log message (not an error). Any paid Apify plan removes the limit; prices are the same.

Pay per result, no start fee and no add-on maze. Prices drop automatically on higher Apify plans:

| Event | Free plan | Bronze | Silver | Gold and above | When |
|---|---|---|---|---|---|
| Place scraped | **$2.00 / 1,000** | $2.00 | **$1.40** | **$0.75** | Each place in your dataset — full details, 5 top reviews, photos, lead score and monitoring included |
| Review scraped | **$0.25 / 1,000** | $0.25 | $0.25 | **$0.10** | Only reviews beyond the first 5 per place, when you ask for more (up to 5,000 per place) |
| Review history minimum | **$0.0001 per unit** | $0.0001 | $0.0001 | $0.0001 | Top-up units, only when a place's delivered reviews are worth less than the minimum of the review pages loaded for it (see below) |
| Place extras | **$2.20 / 1,000** | $2.20 | $2.20 | $2.20 | Only with "Extended profile" on, for places that return it (popular times, price level, people also search, review topics, attributes) |
| Place extras checked | **$2.20 / 1,000** | $2.20 | $2.20 | $2.20 | Only with "Extended profile" on, for places that were looked up but have no extended profile on Google |
| Contacts enriched | **$1.50 / 1,000** | $1.50 | $1.50 | **$0.37** | Only when the website visit added new information (an email, or a phone / social profile not already on the listing) |

**Review history minimum.** The full review history is loaded in pages of 10 reviews, and every page costs us money whether or not its reviews end up in your dataset. So each place's review history is charged at least a small minimum: pages × $0.001875 + $0.00025 on Free, Bronze and Silver, or pages × $0.0009375 + $0.00025 on Gold and above (economy mode, also used for any run asking for more than 20,000 reviews).

When the reviews you receive beyond the first 5 are worth less than that minimum, the difference is charged as `review-minimum` units of $0.0001. This happens with small review counts, when *Only reviews since* removes most reviews, or when Google returns fewer reviews than the place lists. Example amounts per place are in the [FAQ](#faq) ("Why was I charged `review-minimum` or `place-extras-checked`?").

The run never starts a review request that your *Maximum cost per run* could not pay, including this minimum.

**Apify platform usage** (compute, storage, proxy) is billed separately by Apify, on top of these prices. At the default 1024 MB a small run typically uses about $0.002–$0.01 and a 1,000-place lead run roughly $0.03–$0.15 (estimate); large area searches run longer and use proportionally more (1 compute unit = 1 GB of memory for 1 hour, at your plan's rate).

Free: places removed by your filters, unchanged places in monitoring mode, duplicates, the market report, the results map and the run summary. Identical website URLs (e.g. several locations linking the same homepage) are crawled once (`contacts.sharedWebsite: true` on the others), and contacts are charged at most once per business domain per run.

**Worked examples**

- **1,000 dentists with contact enrichment, 700 websites yield new contacts** — Free plan: $2.00 + $1.05 = **$3.05**; Silver: $1.40 + $1.05 = **$2.45**; Gold: $0.75 + $0.26 = **$1.01**.
- **10,000 places, no enrichment** — Free plan: **$20.00**; Silver: **$14.00**; Gold: **$7.50**.
- **100 restaurants with 100 reviews each** (95 charged reviews per place) — Free plan: $0.20 + 9,500 × $0.00025 = **$2.58**; Silver: $0.14 + $2.38 = **$2.52**; Gold: $0.075 + $0.95 + 200 × $0.0001 review minimum = **$1.05**.
- **20 places with 10 reviews each and the extended profile** (Free plan) — 20 × $0.002 places + 20 × $0.00215 reviews (5 reviews × $0.00025 = $0.00125 per place, plus 9 review-minimum units) + 20 × $0.0022 extras = **$0.127**. Plus Apify platform usage.

#### Price comparison for a typical lead-generation run

**1,000 places + website contacts + 5 reviews per place**, compared with the most-used Google Maps Actor on the Apify Store (~625,000 users). That Actor sells details, contacts, reviews and filters as separate add-ons; we price its base place + contacts add-on (per place) + 5 reviews (per review). We count our contacts event on all 1,000 places (worst case — in practice it is charged only when the website adds something new).

| Apify plan | Most-used Actor (~625k users), event prices | **Google Maps Scraper Plus**, event prices | Difference (event prices) |
|---|---|---|---|
| Free | $4.00 + $2.00 + $2.50 = **$8.50** | $2.00 + $1.50 = **$3.50** | 59 % less |
| Bronze | $3.00 + $2.00 + $2.50 = **$7.50** | $2.00 + $1.50 = **$3.50** | 53 % less |
| Silver | $2.00 + $1.50 + $1.85 = **$5.35** | $1.40 + $1.50 = **$2.90** | 46 % less |
| Gold | $1.50 + $1.05 + $1.30 = **$3.85** | $0.75 + $0.37 = **$1.12** | 71 % less |
| Platinum | $1.26 + $0.63 + $0.79 = **$2.68** | $0.75 + $0.37 = **$1.12** | 58 % less |
| Diamond | $0.76 + $0.38 + $0.47 = **$1.61** | $0.75 + $0.37 = **$1.12** | 30 % less |

**These totals exclude Apify platform usage.** Our runs bill platform usage (compute, storage, proxy) separately. At the default 1024 MB, a 1,000-place run with website contacts typically adds roughly **$0.03–$0.15** (about 5–20 minutes at 1 GB, i.e. 0.1–0.35 compute units at typical compute-unit rates — an estimate, not a benchmark; every run shows its exact usage in Console). Our measured small test runs (1–6 places) used $0.001–$0.01. Added to our price, the Diamond difference narrows to roughly 21–29 %, while the other plans stay more than 40 % below. The most-used Actor's public pricing data does not mark platform usage as billed to the user, so compare our price plus usage with its price alone. It also charges a start fee of $0.00005 per GB of memory.

Its optional "additional place details" add-on (reservation data, web results, questions) costs another $2.00 / 1,000 on Free and $1.05 on Gold, and each filter adds $1.00 / 1,000 on Free ($0.525 on Gold); filters are free here.

**Bare listing only** (no contacts, no reviews): the most-used Actor charges $4.00 / $3.00 / $2.00 / $1.50 / $1.26 / $0.76 per 1,000 places (Free → Diamond) against our $2.00 / $2.00 / $1.40 / $0.75 / $0.75 / $0.75 — on Diamond the two are practically the same, and ours costs slightly more once platform usage is added. Several smaller Actors (~3,000–4,000 users) charge **$0.50 / 1,000 on Free and $0.40 on Gold** for a bare listing, which is cheaper than us if the listing is all you need.

*Prices from public Apify Store listings, checked September 2026. Store prices change — check the listing of any alternative you consider.*

Set *Maximum cost per run* in the run options and the Actor stops gracefully when it is reached; prices for your plan are read at run time, so the budget is always respected.

### Integrations

- **Make, Zapier, n8n**: use the Apify apps/nodes to start a run and pull the dataset into your CRM, email tool or spreadsheet.
- **Google Sheets**: connect the dataset with Apify's Google Sheets integration, or export CSV/Excel from the Output tab.
- **Webhooks**: trigger your own endpoint when a run succeeds; in monitoring mode point it at the `CHANGES` record for alerts (Slack, email…).
- **Schedules**: run daily/weekly in Apify Schedules — combine with `monitoringMode` + `outputOnlyChanges` to pay only for new and changed places.
- **API & AI agents**: every feature is available through the Apify API (see examples above), the official JavaScript/Python clients and the Apify MCP server.

### FAQ

**Is there a limit on the Apify Free plan?** Only for two add-ons: the full review history (beyond the first 5 reviews) and extended profiles share $0.25 per calendar month on the Free plan. Apify pays developers nothing for Free-plan usage while those add-ons cost us data fees. Place data, contacts and the first 5 reviews are unlimited; the allowance resets on the 1st of the month. Any paid Apify plan has no limit.

**Is scraping Google Maps legal?** Scraping publicly available business data is generally allowed, but personal data (e.g. reviewer names) is protected by laws such as GDPR. Reviewer personal data is off by default; only enable it with a legitimate reason. Consult your lawyer if unsure.

**How fresh is the data?** Every run collects the data from Google Maps at the moment it runs — nothing is served from an old cache. Each record carries `scrapedAt`.

**Why did I get fewer results than expected?** Common reasons: the per-search limit (`maxPlacesPerQuery`, 200 when empty, 50 in the Console form), filters (minimum rating, website/phone/ownership filters, name match), places outside the location boundary (widen with `includeNearbyKm`), *Fast* coverage mode, or your *Maximum cost per run*. `RUN_SUMMARY` lists filtered places by reason, search terms that hit a limit and unresolved inputs. A location is skipped when OpenStreetMap only found unrelated places for that name — use the official name (e.g. "The Heights, Jersey City") or draw the area as `customGeolocation`; skipped inputs are listed in `RUN_SUMMARY.unresolvedInputs`.

**Why was I charged `review-minimum` or `place-extras-checked`?** `review-minimum`: reviews are loaded in pages of 10, and each page has a real cost even when its reviews are not delivered. If the reviews you received for a place (beyond the free first 5) are worth less than the minimum of the pages loaded for it — for example *Reviews per place* = 10 (only 5 charged reviews), *Only reviews since* removed most of them, or Google returned fewer reviews than the place lists — the difference is charged in $0.0001 units. Minimum per place (rounded up to $0.0001):

| Reviews per place | Free / Bronze / Silver | Gold and above |
|---|---|---|
| 6–10 | $0.0022 | $0.0012 |
| 11–20 | $0.0040 | $0.0022 |
| 21–30 | $0.0059 | $0.0031 |
| 100 | $0.0190 (95 reviews = $0.02375, no top-up) | $0.0097 (95 reviews = $0.0095, 2 units top-up) |

With 100 reviews per place on Free–Silver the reviews themselves cover the minimum and no top-up is charged.

`place-extras-checked`: with *Extended profile* on, every place is looked up; some places (often small businesses) have no extended profile on Google. The lookup still costs money, so it is charged at the same price as *Place extras*. Turn the option off if you do not need popular times or price levels.

**Is Apify platform usage included?** No. The prices on this page are for results only; Apify bills platform usage (compute, storage, proxy) separately. At the default 1024 MB a small run typically uses about $0.002–$0.01 and a 1,000-place lead run roughly $0.03–$0.15 (estimate).

**How do I control my spend?** Set *Maximum cost per run* in the run options — the Actor stops gracefully when it is reached. `maxPlacesPerQuery` and `maxPlacesTotal` cap the number of places, and filtered places are never charged.

**Can I monitor an area on a schedule?** Yes. Schedule the Actor with `monitoringMode` (and optionally `outputOnlyChanges`) and the same `monitoringName`. Each run labels places NEW / UPDATED / UNCHANGED, reports verified REMOVED places and writes a `CHANGES` record for alerts.

**Which export formats are supported?** JSON, CSV, Excel, XML, HTML and RSS from the dataset. Emails and social profiles are also in flat columns (`email`, `facebook`, `instagram`…) so CSV/Excel exports stay readable.

**Do I need residential proxies?** No. The default Apify datacenter proxy works for these endpoints and is the cheapest option.

**Can I get the names of owners, managers or employees?** No. The Actor returns business-level contacts only (business phone, business mailboxes, social profiles). `ownerName` is the display name of the Google Business Profile owner account — usually the business name itself — not a person.

**Where do I get help?** Open an issue in the Actor's **Issues** tab in Apify Console with your run ID (and the input, if you can share it).

### Honest limitations

- **Reviews**: every place includes its top 5 reviews. Set "Reviews per place" higher to get the most recent review history — up to 5,000 per place (Google itself stops listing older reviews after roughly 4,500). About half of Google reviews are star-only without text; that is how they were posted.
- **Popular times** exist only for place types where Google shows them (restaurants, bars, shops…, not e.g. dentists). Q\&A no longer exists on Google Maps. Extended profiles and long review histories add a few minutes to a run (longer for very large runs), because they are completed in one batch after the map search.
- **Limited view**: Google randomly serves a reduced place page (about half of all requests) without review data, without the ownership marker and with only today's opening hours. The Actor re-requests such places (`limitedViewRetries`), fills the week's hours from the search results when possible, and labels any record that stayed reduced (`dataQuality.googleView: limited`, `dataQuality.openingHoursPartial`). In monitoring, partial data never counts as a change.
- **Emails** come from the business's own website; MX checking confirms the domain accepts mail, not that a specific mailbox exists. **Privacy rule:** by default an address is returned only when it is clearly a business mailbox — one of its name parts is or starts with a role word (info@, bookings2@, front.desk@, events@…), it contains a word from the business name or website domain, or the business published it in its structured data (schema.org). On ordering/booking/site-builder pages only the business's own, free-mail (gmail…) or self-published addresses are kept, and vendor addresses found on privacy/terms pages are ignored. Everything else might be a person's mailbox and is hidden (counted in `personalEmailsHidden`) unless you enable `includePersonalEmails`. On franchise pages listing many branches, the address matching this branch's name or city is put first and `multiLocationPage` is set.
- **Ordering, booking and site-builder pages** (Toast, DoorDash, Booksy, Vagaro, GlossGenius, Wix sites…) are labelled `websiteStatus: platform_page`; the platform's own emails and social profiles are removed so they are never attributed to the business. `RUN_SUMMARY.suspectSharedContacts` lists any address found on 3+ different websites (usually a web agency footer).
- **Photos**: Google exposes about 3–5 photos per place to logged-out visitors; `imagesCount` shows the total on Google.
- **Very large areas** (whole countries in *Thorough* mode) are capped at 2,500 starting tiles closest to the centre; split them into regions for full coverage.
- **Not included**: employee or decision-maker names, job titles and personal LinkedIn profiles; mailbox-level email verification (MX only, no SMTP probing); anything that requires a Google login (e.g. full photo galleries).

### Privacy & personal data

The Actor collects business information that is publicly visible on Google Maps and on the businesses' own websites. Reviewer names and profile links are **off by default**, and email addresses that may belong to a person are hidden unless you enable `includePersonalEmails`. If you enable personal data, you are responsible for having a legitimate reason to process it under GDPR, CCPA and similar laws.

# Actor input Schema

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

What you would type into Google Maps, e.g. <code>dentist</code>, <code>coffee shop</code>, <code>Starbucks</code>. Every term is searched in every location below.

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

Any number of cities, districts, regions, postcodes or countries, e.g. <code>Austin, Texas, USA</code>, <code>10115 Berlin</code>, <code>Portugal</code>. Each one is geocoded to its real boundary and covered with an adaptive grid, so you are not limited to Google's ~120 results per map view. Leave empty to search without a location (Google picks the area).

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

Draw your own area on <a href='https://geojson.io' target='_blank'>geojson.io</a> and paste it here. Accepts Polygon, MultiPolygon, a Point with <code>"radiusKm"</code>, or a whole Feature/FeatureCollection.

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

Optional two-letter ISO country code (e.g. <code>US</code>, <code>DE</code>). Disambiguates location names and sets Google's region. With no locations, the whole country is searched.

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

Paste any Google Maps link: place pages, search result pages, <code>?cid=</code> links or <code>maps.app.goo.gl</code> share links. You can also link a text file with one URL per line.

## `placeIds` (type: `array`):

Look up places directly. Accepts Google Place IDs (<code>ChIJ…</code>), <code>place\_id:ChIJ…</code>, CIDs (<code>16434010972841156326</code>) and feature ids (<code>0x89c2…:0xe411…</code>).

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

Stops each search term × location combination after this many places. <code>0</code> = no limit (get everything in the area). If left empty: 200, or no limit in monitoring mode (so removals can be detected). The Console form is prefilled with 50 for a cheap first try.

## `maxPlacesTotal` (type: `integer`):

Hard cap for the whole run across all terms and locations. <code>0</code> = no cap. You can also cap spending with the platform's 'Maximum cost per run'.

## `coverage` (type: `string`):

<b>Auto</b> (recommended): splits the area into tiles and automatically subdivides every tile where Google truncated the list. <b>Thorough</b>: starts with small tiles for dense cities — slower, finds the most places. <b>Fast</b>: one map view per location (max ~120–200 places), cheapest.

## `includeNearbyKm` (type: `integer`):

By default only places strictly inside the location boundary are returned. Set e.g. <code>2</code> to also keep places up to ~2 km outside.

## `pointRadiusKm` (type: `integer`):

When a location resolves to a single point (e.g. a street address) instead of an area, search this radius around it.

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

Only places with at least this star rating (e.g. <code>4.2</code>). Filtered places are never charged.

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

Only places with at least this many Google reviews.

## `maxReviewsCount` (type: `integer`):

Useful to find new or under-marketed businesses (e.g. <code>10</code>).

## `categoryFilter` (type: `array`):

Keep places whose Google category contains any of these words (whole words, in the run's language), e.g. <code>bar</code> keeps "Cocktail bar" and "Wine bars" but not "Barbecue restaurant". Filtered places are never charged.

## `excludeCategories` (type: `array`):

Drop places whose category contains any of these words, e.g. <code>hotel</code>.

## `websiteFilter` (type: `string`):

Filter by whether the listing has a website. "Without website" is a classic web-design lead list.

## `phoneFilter` (type: `string`):

Filter by whether the listing has a phone number.

## `claimFilter` (type: `string`):

Unclaimed listings (owner never verified the Google Business Profile) are strong leads for local SEO agencies.

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

Drop permanently and temporarily closed places.

## `nameMatch` (type: `string`):

Useful for brand searches: <b>Contains</b> keeps only places whose name contains every word of the search term.

## `scrapePlaceDetails` (type: `boolean`):

Adds rating distribution, top reviews, review keywords, owner description, places located inside (malls, airports) and more. Included in the base price — no add-on fee. Turn off only for maximum speed.

## `maxReviews` (type: `integer`):

Reviews per place, up to 5,000 — the complete, most recent review history (text, stars, date, owner reply, photos, per-topic ratings). The first 5 reviews of every place are included in the place price; more reviews are charged per review, with a small minimum per place (see Pricing in the README). Set 0 for no reviews.

## `reviewsSort` (type: `string`):

Which reviews come first when a place has more reviews than you request.

## `reviewsStartDate` (type: `string`):

Optional. Keep only reviews published on or after this date (YYYY-MM-DD). Works best with 'Newest first'. Reviews removed by this date are not charged, but the review pages loaded still cost their minimum (review-minimum top-ups), so set 'Reviews per place' close to what you expect to keep.

## `reviewsPersonalData` (type: `boolean`):

Off by default for GDPR safety. Turn on only if you have a legitimate interest to process reviewers' personal data.

## `maxImages` (type: `integer`):

Full-size photo URLs per place. Google exposes about 3–5 photos per place to logged-out visitors, so values above that return what is available.

## `placeExtras` (type: `boolean`):

Adds popular times (busyness by day and hour), price level, 'people also search' places, review topics and the full list of available/unavailable attributes. Charged per place looked up ($0.0022): place-extras when data is returned, place-extras-checked when Google has no extended profile for the place. Adds a few minutes to the run (longer for very large runs).

## `scrapeContacts` (type: `boolean`):

Visits each business website (homepage + contact/about/imprint pages; only the listed page on ordering/booking platforms) and extracts emails, phones and 13 social networks. Emails are checked for a working mail server (MX) and filtered by a business-mailbox privacy rule; platform and template addresses are removed. Also reports website health (parked, dead, social-only, platform page…) and tech signals (CMS, analytics, pixel, online booking). Charged only when the website adds something the listing didn't have (an email, or a valid phone / social profile not already on the listing), at most once per business domain.

## `maxContactPagesPerSite` (type: `integer`):

Pages visited per website in total, including the homepage (contact/about/imprint pages are preferred).

## `verifyEmailDomains` (type: `boolean`):

Drops emails whose domain cannot receive mail. Free, DNS-based (no SMTP probing).

## `includePersonalEmails` (type: `boolean`):

Off by default for GDPR safety. By default an email is returned only if it is clearly a business mailbox: one of its name parts is or starts with a role word (info@, bookings2@, front.desk@, events@…), a word from the business name or website domain, or the business published it in its structured data. Other addresses (e.g. john@, gerry@) are hidden and counted in <code>personalEmailsHidden</code>. Turn on only if you have a legitimate interest to process personal data.

## `leadScoring` (type: `boolean`):

Adds a transparent 0–100 <code>leadScore</code> with a point-by-point breakdown and <code>leadSignals</code> such as <code>no\_website</code>, <code>unclaimed\_listing</code>, <code>website\_no\_https</code>, <code>no\_online\_booking</code>. Free.

## `monitoringMode` (type: `boolean`):

Remembers places across runs (schedule this Actor!) and labels each result NEW / UPDATED / UNCHANGED with a field-level diff (phone, website, status, hours…). REMOVED is reported only after Google confirms it (closed permanently, merged, moved out of the area or no longer known) for a place missing from 3 consecutive complete runs.

## `monitoringName` (type: `string`):

Runs with the same name share memory. Defaults to a fingerprint of your search terms and locations.

## `monitorFields` (type: `array`):

A place is UPDATED when one of these fields changes. Rating and review count move on almost every run, so by default they are only reported in <code>metricsDelta</code>.

## `outputOnlyChanges` (type: `boolean`):

Skip UNCHANGED places — they are not output and not charged, so recurring runs cost only for what is new or changed. Switches monitoring on automatically.

## `marketReport` (type: `boolean`):

Free. Builds <code>market-report.html</code> (and <code>MARKET\_REPORT</code> JSON) from this run's places: category breakdown, opportunity lists (established businesses without a website, unclaimed listings, rising stars, busy but poorly rated, no online booking), leaders and the review topics of the area. No extra requests, no AI — every number comes from the dataset. Skipped when only changes are output.

## `myBusiness` (type: `string`):

A Place ID, Google Maps URL or CID of your own (or your client's) business. It is looked up too, and the report compares it with its nearest competitors of the same category: rating, reviews, photos, business posts, website, online booking — with strengths and weaknesses.

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

Language of place names, categories and reviews (Google <code>hl</code> code, e.g. <code>en</code>, <code>de</code>, <code>tr</code>, <code>pt-BR</code>).

## `outputMap` (type: `boolean`):

Stores <code>results-map.html</code> (clustered map of all places) in the run's key-value store.

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

Default Apify datacenter proxy works best for Google Maps' internal endpoints and is the cheapest. Residential proxy is not needed.

## `useProxyForWebsites` (type: `boolean`):

Contact enrichment visits business websites directly by default. Enable if some sites block the platform's IPs.

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

Parallel requests. The default is safe; higher values finish faster on large runs.

## `limitedViewRetries` (type: `integer`):

Google randomly serves a reduced place page (~50% of requests) without review data and with only today's opening hours. Such places are re-requested up to this many times; records that stayed reduced are labelled in dataQuality.

## Actor input object example

```json
{
  "searchQueries": [
    "restaurant"
  ],
  "locations": [
    "Chelsea, Manhattan, New York"
  ],
  "maxPlacesPerQuery": 50,
  "maxPlacesTotal": 0,
  "coverage": "auto",
  "includeNearbyKm": 0,
  "pointRadiusKm": 3,
  "websiteFilter": "all",
  "phoneFilter": "all",
  "claimFilter": "all",
  "skipClosedPlaces": false,
  "nameMatch": "any",
  "scrapePlaceDetails": true,
  "maxReviews": 5,
  "reviewsSort": "newest",
  "reviewsPersonalData": false,
  "maxImages": 5,
  "placeExtras": false,
  "scrapeContacts": false,
  "maxContactPagesPerSite": 4,
  "verifyEmailDomains": true,
  "includePersonalEmails": false,
  "leadScoring": true,
  "monitoringMode": false,
  "monitorFields": [
    "title",
    "categoryName",
    "address",
    "phoneUnformatted",
    "website",
    "businessStatus",
    "openingHoursText",
    "claimThisBusiness"
  ],
  "outputOnlyChanges": false,
  "marketReport": true,
  "language": "en",
  "outputMap": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "useProxyForWebsites": false,
  "maxConcurrency": 10,
  "limitedViewRetries": 4
}
```

# Actor output Schema

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

No description

## `resultsMap` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

## `leads` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `marketReport` (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": [
        "restaurant"
    ],
    "locations": [
        "Chelsea, Manhattan, New York"
    ],
    "maxPlacesPerQuery": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cheapapi/google-maps-scraper-plus").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": ["Chelsea, Manhattan, New York"],
    "maxPlacesPerQuery": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("cheapapi/google-maps-scraper-plus").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": [
    "Chelsea, Manhattan, New York"
  ],
  "maxPlacesPerQuery": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call cheapapi/google-maps-scraper-plus --silent --output-dataset

```

## MCP server setup

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

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/piabgIbz7Car1Rxqi/builds/gNrJPWJRTPl4oWHzi/openapi.json
