Google Maps Scraper - $0.27/1k, Places & Reviews
Pricing
from $0.27 / 1,000 place returneds
Google Maps Scraper - $0.27/1k, Places & Reviews
The coordinates and IDs of a place sit inside its result link, before you open anything, and a real browser scrolls the feed reading them off, with name, category, address and rating. Phone, website and review count appear only when Google shows them. $0.27 per 1,000 rows.
Pricing
from $0.27 / 1,000 place returneds
Rating
5.0
(2)
Developer
Dami's Studio
Maintained by CommunityActor stats
1
Bookmarked
11
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Google Maps Scraper
A Google Maps result link tells you more than it looks like it does. The place's latitude,
longitude and its two identifiers are all sitting in the /maps/place/... href, so reading the
results feed is most of the job.
Two things in one actor. Search queries collect places off that feed. Place URLs open each place and pull the public reviews. It drives a real browser, because Maps doesn't hand this over any other way.
$0.27 per 1,000 rows ($0.00027 each), plus $0.001 when a run starts. Flat on every plan, from your first run.
What a place row actually contains
Here's a real one, unedited:
{"ok": true,"type": "place","searchQuery": "coffee in Brooklyn","name": "Qahwah House Coffee - Williamsburg Brooklyn","category": "Coffee shop","address": "162 Bedford Ave","rating": 4.7,"reviewCount": null,"phone": null,"website": null,"lat": 40.7185755,"lng": -73.9571869,"placeId": "0x89c25953721fc19d:0x185b29b3e7559d6e","cid": "1755042332447972718","placeUrl": "https://www.google.com/maps/place/Qahwah+House+Coffee…"}
Look at the nulls in that row. They're normal, not a bad sample.
Reliable on search results: name, category, address, rating, lat, lng, placeId,
cid, placeUrl.
Usually null: phone, website, reviewCount. Google only prints these on the results card for
some businesses, and a search reads the card rather than opening every place. Counted over 342 place
rows from recent runs: phone was null on 99% of them, website on 84%, reviewCount on 43%.
Re-counted 2026-08-29 over 127 place rows, and website is scarcer than that older figure suggests:
phone was present on 2 rows and website on 2 — both about 1.6%, so website is null on ~98%,
not 84%. Treat this as name, address, rating and coordinates data. If you need contact details, the
second pass below is not optional.
So if phone numbers are the point of your project, a plain search will not give you them. Feed the
placeUrl values into a second pass, or set includeReviews so every place gets opened.
placeId and cid are always there, which is what you want if you're joining this against anything
else.
Reviews
Pass place URLs instead of, or as well as, search queries:
{"reviewUrls": ["https://www.google.com/maps/place/Example+Business/@40.7,-74,17z/data=..."],"maxReviewsPerPlace": 100,"includeOwnerReplies": true}
It opens the rendered review pane, scrolls until it hits your count or the end of the list, and
writes one row per unique review with type: "review", placeId, placeName, placeUrl,
reviewId, reviewerName, rating, relativeDate, text, ownerReply and photoUrls.
Deduplication uses Google's own data-review-id where it exists and falls back to
reviewer + date + text.
Set includeReviews: true alongside searchQueries to discover places and pull their reviews in one
run. That opens every place, so it's a lot more browser time than a plain place search.
Dates are Google's relative strings ("3 months ago"), not timestamps. Google doesn't expose the exact date in that pane.
Input
| Field | Notes |
|---|---|
searchQueries | One or more searches with the location in the text, like coffee in Brooklyn. |
searchQuery | Single-search alternative if you'd rather use one box. |
maxItems | Places per search. Default 60, max 500. |
reviewUrls | Place URLs to pull reviews from. placeUrls is an accepted alias. |
maxReviewsPerPlace | Default 50, max 500. |
includeReviews | Also open every place found by a search. Default false. |
includeOwnerReplies | Include the owner's visible reply. Default true. |
language | Google language code for the Maps interface, en by default. Any code Google accepts works, not just the common few. |
countryCode | Two-letter country to exit from. Leave empty for the actor's US pool, which is the most reliable lane. |
Billing
| Event | Price |
|---|---|
| Actor start | $0.001 per run |
| Row returned | $0.00027 |
A review row costs the same $0.00027 as a place row. They're the same billing event, so a run
that pulls 100 reviews from one place is billed for 101 rows, not 1. Worth knowing before you set
maxReviewsPerPlace to 500 across a long list of URLs.
Nothing is charged for blocked runs, bad input, or a place that yields no reviews — those write an
ok: false diagnostic row instead. An entirely empty input emits one _sample row without even
launching a browser.
Proxy, and where this breaks
The actor brings its own egress: static addresses Google tolerates, with rotating datacenter behind
them. When it sees a captcha it rebuilds the browser and moves to the next address. Metered Apify
groups (RESIDENTIAL, GOOGLE_SERP) aren't offered and get swapped for datacenter if requested, so
a run can't quietly bill you per gigabyte. Your own proxy servers are used exactly as given.
Review extraction depends on Google's rendered pane, so it can come back BLOCKED when Google
challenges the browser. No review is charged when that happens. This reads rendered markup, so
Google changing its layout will need selector updates from time to time.
If you want the rows in Notion rather than a dataset, authorize a Notion MCP connector once under
Settings → API & Integrations, then set notionConnector and notionParentId and the run writes
them there when it finishes.

