Google Maps Reviews Scraper - $0.27 per 1,000 Reviews avatar

Google Maps Reviews Scraper - $0.27 per 1,000 Reviews

Pricing

from $0.27 / 1,000 review scrapeds

Go to Apify Store
Google Maps Reviews Scraper - $0.27 per 1,000 Reviews

Google Maps Reviews Scraper - $0.27 per 1,000 Reviews

$0.27 per 1,000 reviews, the cheapest Google Maps reviews scraper on the market. Star rating, full text, exact date, reviewer with lifetime review count, owner reply and photos, from a place link, place ID, CID or business name. No API key, no quota, no five-review cap.

Pricing

from $0.27 / 1,000 review scrapeds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Google Maps Reviews Scraper

Point it at a Google Maps place — a link, a place ID, a CID or just the business name — and get its reviews back as flat rows: star rating, the full review text, the exact date, the reviewer with their lifetime review and photo counts, the owner's reply, and the photos attached to the review. It pages through the whole review history, not just the first screen. No account, no cookies, no login, no browser.

  • Four ways to name a place: a Maps link straight from the address bar, a short maps.app.goo.gl link, a place ID, a numeric CID, or the business name.
  • Full pagination — ask for 5 reviews or 5,000 and it keeps turning pages until it has them.
  • Sort newest, most relevant, highest or lowest, exactly like the Reviews tab does.
  • "Only reviews since" turns it into a monitor: with newest-first it stops as soon as it is past your cutoff, so a daily check costs a couple of requests.
  • Owner replies come back on the same row as the review they answer, so a complaint and the response to it are never separated.
  • Exact publication timestamps, not just "3 months ago" — both are on the row.
  • Reviews written in another language are translated into the language you asked for, with the writer's original wording kept alongside.
  • Runs with empty input return a labelled sample row, free, so you can see the shape first.

Price

$0.27 per 1,000 reviews, plus a $0.0005 start fee per run (billed per gigabyte of run memory, so exactly that on the default 1 GB).

This is the cheapest Google Maps reviews scraper on the market, and it is the same rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.

ReviewsTotal cost
100$0.0275
1,000$0.2705
10,000$2.7005
100,000$27.0005

What is actually charged

  • One review-scraped event per review row written to the dataset. Nothing else is metered per row.
  • Free: the sample row an empty run returns, and every diagnostic row — a blocked target, a dead URL, a search that matched nothing. Those rows all carry "charged": false.
  • Star-only ratings you filtered out with "Skip star-only ratings" are dropped before they are charged.
  • Reviews older than your "Only reviews since" cutoff are dropped before they are charged.
  • Reviews already returned earlier in the same run are deduplicated on review ID and charged once.
  • Looking a place up from its name, its short link or its place ID is part of the run, not a separate charge.
  • A run that finds nothing costs the start fee and nothing more.
  • Rows never leave the dataset without a charge, and are never charged without a row. The billed event is a named one, so there is no price quietly attached to apify-default-dataset-item — the trick that makes some scrapers bill you for their own error messages.

Input

{
"placeUrls": [
"https://www.google.com/maps/place/Katz's+Delicatessen/data=!4m2!3m1!1s0x89c2598f7ff4aa09:0x313547e757cb8cea",
"ChIJN1t_tDeuEmsRUsoyG83frY4"
],
"searchTerms": [
"Pike Place Chowder Seattle"
],
"maxReviews": 300,
"maxReviewsPerPlace": 100,
"sort": "newest",
"reviewsSince": "90 days",
"onlyWithText": true,
"language": "en"
}
FieldWhat it does
placeUrlsMaps links, short links, place IDs (ChIJ…) or numeric CIDs. Up to 50 per run. Paste the link exactly as the address bar gives it to you — the coordinates and tracking parameters in it are ignored.
searchTermsBusiness names to look up first, e.g. Katz's Delicatessen New York. The top matching place is used. Add the town or city: a bare Starbucks can match anywhere on earth.
maxReviewsTotal reviews across every place in the run. The budget is split evenly between the places, so five places and 100 reviews gives you twenty each. Default 20, hard ceiling 5,000. Keep it low while testing — you pay per review.
maxReviewsPerPlaceOptional per-place cap on top of the total. Use it when one place has 40,000 reviews and you only want the newest hundred from each.
sortnewest, relevant, highest or lowest. Newest for monitoring; relevant returns the longer, photo-heavy reviews Google promotes to the top of the tab.
reviewsSinceA cutoff, written either as a date (2026-01-01) or as a window (30 days). Combined with newest-first it also ends the paging, which is what makes a scheduled monitor cheap.
onlyWithTextGoogle counts a bare star rating with no words as a review. Turn this on and those are dropped before they are charged.
languageTwo-letter code for the language you want the reviews in (en, de, fr, es, …). Foreign-language reviews are translated into it and the original wording is kept in originalText.
countryTwo-letter country code for the Google edition to read (us, gb, de, …). Mostly affects how dates and place names are written.
proxyUrlsLeave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for, as http://user:pass@host:port.

Run it with empty input and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.

Output

One row per review. A real row from a real run:

{
"ok": true,
"charged": true,
"recordType": "review",
"target": "https://www.google.com/maps/place/Starbucks+Reserve+Roastery/data=!4m2!3m1!1s0x54906acdccf44db1:0x6da0c1f2d7a6736e",
"placeName": "Starbucks Reserve Roastery",
"placeUrl": "https://www.google.com/maps?cid=7899526995152106350",
"placeId": "ChIJsU30zM1qkFQRbnOm1_LBoG0",
"cid": "7899526995152106350",
"featureId": "0x54906acdccf44db1:0x6da0c1f2d7a6736e",
"reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2toT0xYYzNkWFJqVlRaUFlVNHRObG94TWtWU05tYxAB",
"rating": 5,
"text": "Tried the flight and tiramisu latte. Both were pretty good. Prices are pretty high though around 10-15 dollars for coffee and the wait time to get your order was around 40 minutes.",
"originalText": null,
"language": "en",
"publishedAt": "2025-09-25T07:31:34.977Z",
"relativeDate": "10 months ago",
"reviewerName": "A. Reviewer",
"reviewerUrl": "https://www.google.com/maps/contrib/000000000000000000000/reviews?hl=en-US",
"reviewerPhotoUrl": "https://lh3.googleusercontent.com/a-/example=s64-c-rp-mo-ba12-br100",
"reviewerReviewsCount": 580,
"reviewerPhotosCount": 1322,
"isLocalGuide": true,
"ownerReply": "Hi there, we apologize for the long wait time you experienced. If you would like to share additional details or feedback, please get in touch through our Contact Us page!",
"ownerReplyRelativeDate": "10 months ago",
"reviewPhotosCount": 1,
"reviewPhotoUrls": [
"https://lh3.googleusercontent.com/grass-cs/ACvplmOBJA3PJJYg_dTGWYHdAOjL4eb8rXecb_4RtOrVKLk8D0IJyiV7LHm4q09eqGFjK1zeSep2lmcmGrgmFyVTvnLL8yQpPvmE0R1C5LkVb7aZ2rvhNzMEvGQAQHyAl9Jj1IHfGMWHQIuNF8yS=k-no"
],
"reviewUrl": "https://www.google.com/maps/reviews/data=!4m8!14m7!1m6!2m5!1sCi9DQUlRQUNvZENodHljRjlvT2toT0xYYzNkWFJqVlRaUFlVNHRObG94TWtWU05tYxAB",
"scrapedAt": "2026-08-15T22:44:25.870Z"
}

Field notes

  • target — the exact string you supplied for this place, so you can join the rows back to your input list.
  • placeName — filled in when the link you pasted carried the name in it, or when Google's answer to a name lookup included it. A bare place ID or CID carries no name, and Google sometimes answers a lookup with the identifier only, so this can be null — the place is still fully identified by placeUrl, placeId, cid and featureId, and target still holds what you asked for.
  • placeUrl — a clean, permanent link to the place. Safe to store; it does not expire the way a copied address-bar URL does.
  • placeId — the ChIJ… identifier Google publishes. Null when the run only ever knew the place by CID.
  • rating — the star rating, 1 to 5, as an integer.
  • text — the review in the language you asked for. Null when the reviewer left stars and no words.
  • originalText — the reviewer's own wording, present only when it differs from text — i.e. only when the review was translated.
  • publishedAt — the exact moment the review was posted, ISO 8601 in UTC. This is the field to sort and diff on.
  • relativeDate — the wording Google shows ("3 months ago"). Convenient for display, useless for sorting.
  • reviewerReviewsCount — how many reviews that person has written in total, across all places — a decent proxy for how seriously to take the review.
  • reviewerPhotosCount — how many photos that person has contributed in total.
  • isLocalGuide — true when Google badges the reviewer as a Local Guide.
  • ownerReply — the business owner's public response, on the same row as the review it answers. Null when there is none.
  • reviewPhotosCount — how many photos are attached to this review.
  • reviewPhotoUrls — direct links to those photos, up to ten per review. They are Google-hosted and can be resized by editing the size suffix.
  • reviewUrl — a permanent link to this single review on Google Maps.

Every real row carries "charged": true. Sample rows carry "_sample": true and diagnostic rows carry "_diagnostic": true with an errorCode you can filter on, and neither is ever billed.

How it works

  • It reads reviews from the same public data service the Reviews tab itself calls, and asks it directly for the reviews of one place — no page rendering, no headless browser, no login, no cookies.
  • A Maps link, a place ID and a CID all already contain the identifier the service needs, so those are decoded locally and cost no request at all. Only a business name needs a lookup first, and that is one small call.
  • Reviews are pulled in pages of up to fifty, following Google's own pagination token, and the run asks for only as many as you still need — sampling three reviews does not download fifty.
  • Requests leave through a large pool of rotating datacenter addresses, so a per-address rate limit is answered by moving rather than by waiting.
  • Review IDs are remembered for the whole run, so the same review is never returned — or charged — twice.

What people use it for

  • Reputation monitoring: schedule the same places daily with sort: newest and reviewsSince: "2 days", and you get only what is new — a handful of billed rows each time instead of the whole history.
  • Answering complaints: ownerReply sits on the same row as the review, so filtering for rating <= 2 AND ownerReply == null gives you the exact list of unhappy customers nobody has replied to yet.
  • Competitive benchmarking across a set of locations: pull the same number of newest reviews for each place and compare average rating, reply rate and review velocity side by side.
  • Sentiment and topic analysis: text is the full review body, already translated into one language, which is what a classifier or an LLM wants as input.
  • Location audits for chains and franchises: feed a list of place IDs, cap maxReviewsPerPlace, and get a comparable sample from every branch in one dataset.
  • Due diligence on a business before buying, partnering with or franchising it — the full history, with dates, rather than the handful of reviews the page shows.
  • Academic and market research where you need reviews with exact timestamps, reviewer experience counts and photo counts as structured columns.

Naming a place four different ways

All of these point at the same restaurant, and all of them work:

https://www.google.com/maps/place/Katz's+Delicatessen/@40.7223,-73.9874,17z/data=!4m2!3m1!1s0x89c2598f7ff4aa09:0x313547e757cb8cea
https://maps.app.goo.gl/xxxxxxxxxxxxxxxx
ChIJCar0f49ZwokR6ozLV-dHNTE
3545819340560108778

Put any of them in placeUrls. Put a plain business name in searchTerms instead and the run looks it up first. The three identifier forms are decoded without contacting Google at all, so they are the fastest and the most precise — if you are running the same list every day, store the placeId or cid from your first run and use that from then on.

Reading the output

Every run writes three kinds of row, and they are easy to tell apart:

  • Real rows carry "charged": true and "recordType": "review". These are the rows you paid for, one billed event each.
  • The sample row carries "_sample": true and "charged": false. There is exactly one, it only appears when the input named no place at all, and it exists so you can look at the shape of the output before you spend anything.
  • Diagnostic rows carry "_diagnostic": true, "charged": false and an errorCode you can switch on: NO_PLACE_FOUND when a name matched nothing on Maps, NO_REVIEWS when a place has no reviews (or none matching your filters), SHORTLINK_UNRESOLVED or PLACE_ID_UNRESOLVED when an identifier could not be expanded, REVIEWS_403/REVIEWS_429 when Google throttled the run, NETWORK when the service could not be reached, and TIME_BUDGET when the run ran out of time before reaching a place. Each one carries a plain-English error and the target it belongs to.

If you only want the data, filter on charged == true. The count of those rows always equals the number of events you were billed for, so the dataset is its own invoice.

Making a scheduled monitor cheap

The combination that matters is sort: "newest" plus reviewsSince. With newest-first, the run can stop paging a place the moment it crosses your cutoff, because everything after that point is older still. A place that got two new reviews since yesterday therefore costs one request and two billed rows — not a walk through its 40,000-review history.

Set reviewsSince a little wider than your schedule interval ("2 days" for a daily run) so a late-arriving review is never missed, and deduplicate on reviewId on your side. Review IDs are stable, so the same review always arrives with the same ID.

Limitations

  • Reviews only. Place details — opening hours, phone number, address, category, the overall star average and the total review count — are not part of this Actor's output.
  • placeName is null when the place was identified by a bare place ID or CID, and also when a name lookup came back with the identifier but no label — Google decides which of its two answer shapes to send. Everything else on the row still identifies the place, and target still holds exactly what you asked for.
  • Pagination is Google's, and it is bound to the page size the run started with. That is handled internally, but it does mean a place is read in one consistent pass rather than resumed later from a stored cursor.
  • Google does not expose its entire review history through this route. Very large places return several thousand reviews and then stop handing out pagination tokens; the hard ceiling here is 5,000 per run in any case.
  • A review can be edited or deleted after it was written. Rows are a snapshot at read time, and scrapedAt records when that was.
  • Star-only ratings have no text — roughly a quarter of what a busy place receives. text is null for those unless you turn on "Skip star-only ratings".
  • Translation is Google's own. text comes back in the language you asked for and originalText holds the writer's wording; the quality of the translation is not something this Actor controls.
  • "Local Guide" is a badge, not a level. The badge is reported as true or false; the numeric level Google shows on some profiles is not.
  • Photo links are Google-hosted CDN URLs. They are stable for a long time but are not archival — download anything you need to keep.
  • A business name that matches several places resolves to Google's top match. If you need a specific branch, use its link or place ID instead of its name.
  • Sorting is Google's, applied before pagination. Asking for 20 reviews sorted "highest" gives you the 20 highest Google is willing to serve first, not a full sort of the whole history.
  • Very heavy runs can be throttled by Google; the run reports that as an uncharged diagnostic row and keeps whatever it already collected rather than failing.
  • The row budget is split evenly between your places. A place with fewer reviews than its share simply returns fewer rows; the leftovers are not handed to the other places.

Questions

Do I need a Google API key?

No. Nothing here uses the Google Maps Platform, so there is no key to obtain, no quota to manage and no per-place billing from Google. There is also no five-review cap, which is what the official Places API gives you.

How many reviews can I get for one place?

As many as Google will page out, up to the 5,000-per-run ceiling. Several thousand is normal for a large, busy place; small places simply run out of reviews and the run moves on to the next place.

What happens if a place has no reviews at all?

You get one uncharged diagnostic row with errorCode: "NO_REVIEWS", and the run carries on to your other places. You are never billed for a place that returned nothing.

Will the run fail if Google throttles it?

No. It moves to a different address and tries again, and if it still cannot get through it writes an uncharged diagnostic row and finishes as succeeded, keeping everything it already collected. A failed run would still bill you the start fee, which would mean paying to be told something went wrong.

Can I get the reviews in English for a place in Japan?

Yes — set language to en. Google translates the reviews into it and the reviewer's original wording arrives in originalText, so you can keep both.

Do I need a proxy?

No. The run brings its own egress and the cost of it is already inside the price you see. The proxyUrls field exists only for callers who specifically want traffic to leave through servers they already own.

Can I run this on a schedule?

Yes. Nothing is held between runs, so the same input is safe to repeat. Use sort: "newest" with reviewsSince to pay only for what is new, and deduplicate on reviewId.

How do I get exactly the rows I paid for?

Filter the dataset on "charged": true. Sample and diagnostic rows are always false, and the number of charged rows always equals the number of billed events.