Google Play Reviews Scraper - Replies, Versions, Monitor avatar

Google Play Reviews Scraper - Replies, Versions, Monitor

Pricing

$0.05 / 1,000 per review returneds

Go to Apify Store
Google Play Reviews Scraper - Replies, Versions, Monitor

Google Play Reviews Scraper - Replies, Versions, Monitor

$0.045 per 1,000 reviews, no start fee. Google Play reviews for any Android app: rating, text, author, date, thumbs-up, app version, developer reply, criteria. Many apps x languages x countries per run, star + date filters, newest-first early stop and a monitor mode that never bills a review twice.

Pricing

$0.05 / 1,000 per review returneds

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Google Play Reviews Scraper — Developer Replies, App Versions, Monitor Mode

Turn any Android app's Google Play reviews into clean rows, read from the same endpoint the Play Store website uses to render its own review list. One row per review: rating, body, userName, userImage, date, thumbsUp, appVersion, developerResponseBody, developerResponseDate, developerResponseAuthor, criteria, reviewUrl — plus the app's appName, appScore and appRatingsCount on every row. Many apps × languages × countries in one run, star and date filters, newest-first early stop, and a monitor mode that never bills the same review twice. No login. No API key. No browser.

$0.045 per 1,000 reviews ($0.000045 each), no start fee.

{
"apps": ["com.whatsapp"],
"languages": ["en"],
"countries": ["us"],
"sort": "newest",
"maxReviewsPerApp": 200
}

That block is the input the Actor ships with. Press Start and you get WhatsApp's 200 newest English reviews for $0.009 — measured on the live Actor 2026-09-23: 200 rows, 200 charged, 5 seconds.

This is the Android twin of our App Store Reviews Scraper: the same field names wherever the concept is the same (body, userName, date, appVersion, developerResponseBody, developerResponseDate, position, order, appUrl), so you can union the two datasets downstream.


What makes it different

  • Languages, not just countries — measured, not assumed. Google Play's review list is keyed by language far more than by country: WhatsApp's 200 newest English reviews were 87–100% identical across the us, gb, in, au and de stores (measured 2026-09-23), while hl=de returns a different, German corpus. languages takes a list, every app is crossed with every language and country, and a review already delivered for another country or language is dropped before billing (dedupeAcrossCountries, on by default). A scraper that sweeps five English-speaking countries without this bills you for largely the same reviews five times.
  • Monitor mode that cannot skip a gap. onlyNewReviews keeps a per app × language × country bookmark in a named key-value store in your account. The next run reads newest-first only down to the bookmark (with a one-hour overlap, because Google's frontends can lag each other by minutes — observed), skips every review it already delivered, and only advances the bookmark when the walk actually reached it — a run cut short by a cap re-reads the gap next time instead of losing it. Measured: a second run 90 s after a 10,000-review baseline read 1 page, recognised 105 overlap reviews as already delivered, and billed $0.
  • App context on every row. appName, appScore and appRatingsCount come from the app's own store page (its schema.org block), so a multi-app dataset is self-describing. The same read tells a non-existent package (HTTP 404 — skipped, $0, named in the run summary) apart from an app with no reviews in that language.
  • Integrity checks that end the run instead of shipping bad rows. Every page's review ids are checked against the walk so far; a repeated continuation token or a page of nothing but repeats is a pagination loop and stops the walk. If Google changes the review format, the run fails loudly rather than delivering a page of nulls. At the end, charged events must equal delivered rows exactly.

Read this before you buy rows

  1. date is when the review was written or last edited. A developer reply can therefore be older than the review it answers — measured on Robinhood: replies from 2022–2025 on reviews re-edited in September 2026. That is Google's data, not a bug.
  2. Google publishes reviews with a delay. The newest review Google served on 2026-09-23 ~05:00 UTC was stamped 2026-09-22 ~04:59 UTC — about a day old. A "last hour" window will usually be empty; that is the source, not the Actor.
  3. Country barely changes the newest list; language does. See above. For a worldwide sweep, list languages (en, es, pt-BR, de, fr, ja…) with one country each. Countries do change the most relevant order (27–35% overlap between us and gb/in/au/de for Spotify, measured).
  4. Reviews are not ratings. Spotify shows 36.4M ratings; the reviews Google serves are the written ones in the language you ask for. Home Assistant (English) ended at 1,574 reviews — the corpus end is a missing continuation token, and the run says "corpus exhausted at N reviews".
  5. criteria are Google's own raw keys (vaf_app_quality_usability, vaf_phase1_investing…) with the number Google stores. Google does not publish what each number means, so the Actor passes them through untranslated rather than guess.
  6. Reviewer fields are a name and an avatar only. userName is the public display name shown on the Play Store (sometimes a real name, sometimes "A Google user"); userImage is the public avatar URL. No email, phone or address exists in this data.

What you get

One row per review. Dates are ISO-8601 UTC. rating is an integer 1–5.

GroupFieldsNotes
IdentityreviewId, appId, appName, language, countrylanguage/country are the values you requested — Google does not label a review's language, so the Actor never infers one
Reviewrating, body, date, thumbsUpthumbsUp = "helpful" votes; 0 is a real zero
RevieweruserName, userImagepublic display name + avatar URL
VersionappVersionthe app version the reviewer had installed; null when Google shows none
Developer replydeveloperResponseBody, developerResponseDate, developerResponseAuthorall three null together when there is no reply — never ""
Quality questionscriteria[{criterion, rating}], Google's raw keys, or null
App contextappScore, appRatingsCountfrom the app's store page (turn off with includeAppDetails)
Provenanceposition, order, starFilter, reviewUrl, appUrl, source, scrapedAtposition = rank within its app × language × country walk

Field fill — measured on the live Actor, three frames

Frames were chosen to disagree on purpose (newest vs most relevant; ten apps vs one), so the spread between them is the honest range.

FieldA: 10 apps × 200 newest (2,000 rows)B: same 10 apps × 200 most relevant (2,000)C: WhatsApp 10,000 newest
reviewId, rating, body, date, userName, userImage, thumbsUp, reviewUrl100%100%100%
appName, appScore, appRatingsCount100%100%100%
appVersion81.8%98.9%82.2%
developerResponseBody / Date / Author27.9%27.3%0.0%
criteria19.6%35.4%25.8%
thumbsUp > 013.6%90.0%20.6%

Developer replies are publisher behaviour, not a parser rate. Per app in frame A (of 200 newest): Booking.com 193, Robinhood 175, TikTok 157, Spotify 31, Uber 1 — and 0 for WhatsApp, Netflix, Airbnb, Instagram and Duolingo. The offline validator checks the negative direction too: every raw reply, version and criteria block in the captured bytes is emitted (0 parser gaps across 400 fixture reviews). The ten apps were: WhatsApp, Spotify, Robinhood, Duolingo, Uber, Instagram, Netflix, Airbnb, Booking.com, TikTok. Every run prints its own fill in the log and in RUN_SUMMARY.


How to run it

1. The default — one app, newest 200

The shipped input above. $0.009.

2. Complaint mining across competitors

{
"apps": ["com.spotify.music", "com.soundcloud.android", "deezer.android.app"],
"languages": ["en"],
"countries": ["us"],
"stars": [1, 2],
"maxReviewsPerApp": 2000
}

Stars are filtered server-side by Google (one star at a time, 1★ first; the per-app cap is shared across them). 6,000 reviews max = $0.27.

3. Support-behaviour audit — only reviews the developer answered

{ "apps": ["com.booking"], "onlyWithDeveloperReply": true, "maxReviewsPerApp": 1000 }

Reviews without a reply are read but never billed.

4. A date window, cheaply

{ "apps": ["com.whatsapp"], "sort": "newest", "sinceDate": "2026-09-01", "untilDate": "2026-09-15", "maxReviewsPerApp": 0 }

With sort: "newest" paging stops at the first review older than sinceDate. (With the other sorts, dates are applied row by row and paging cannot stop early — the log warns you.)

5. Daily monitor across languages

{
"apps": ["com.yourcompany.app"],
"languages": ["en", "es", "pt-BR", "de", "fr"],
"countries": ["us"],
"onlyNewReviews": true,
"maxReviewsPerApp": 5000
}

Save it as a Task, attach a Schedule (0 7 * * *). The first run is your baseline (it reads newest-first up to your caps and sets the bookmark); every later run delivers only what is new. From the API:

curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~google-play-reviews-scraper/runs?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"apps":["com.whatsapp"],"languages":["en"],"onlyNewReviews":true}'

API callers used to other scrapers' inputs can send a single "language": "de" — it is accepted and replaces languages.


Sample row

A real row from the live run of 2026-09-23 (frame A, Robinhood, newest):

{
"reviewId": "398cee35-43b5-440f-8b02-5c2551876b84",
"appId": "com.robinhood.android",
"appName": "Robinhood: Trading & Investing",
"country": "us",
"language": "en",
"rating": 4,
"body": "Finally, updated and fixed all kinds of stuff. upgraded review from 2 to 4 stars, but now, it's got too much nonsense. 🤷‍♂️",
"userName": "Christian Zagarskas",
"userImage": "https://play-lh.googleusercontent.com/a-/ALV-UjUHIeC5rq2CH3qVdxD35VfDnE60V24nELfXX9mbOA1-OXnyqpNQ",
"date": "2026-09-22T01:53:49.801Z",
"thumbsUp": 4,
"appVersion": "2026.37.4",
"developerResponseBody": "Sorry to hear this wasn’t a great experience. If you’re up for it, send a note to reviews@robinhood.com with a few more details.",
"developerResponseDate": "2026-09-22T03:31:00.382Z",
"developerResponseAuthor": "Robinhood",
"criteria": [{ "criterion": "vaf_phase1_investing", "rating": 1 }],
"position": 2,
"order": "newest",
"starFilter": null,
"reviewUrl": "https://play.google.com/store/apps/details?id=com.robinhood.android&reviewId=398cee35-43b5-440f-8b02-5c2551876b84",
"appUrl": "https://play.google.com/store/apps/details?id=com.robinhood.android&hl=en&gl=us",
"appScore": 4.624,
"appRatingsCount": 574404,
"source": "google-play-batchexecute",
"scrapedAt": "2026-09-23T05:04:40.951Z"
}

Input

FieldTypeDefaultWhat it does
🎯 What to scrape
appsstring list["com.whatsapp"]Package names or Play Store links. A package that does not exist is reported and skipped, $0. Empty = the WhatsApp sample.
languagesstring list["en"]hl codes, one per entry (en, de, pt-BR…). Each is its own corpus.
countriesstring list["us"]gl codes, one per entry.
sortselectnewestnewest, mostRelevant (the store page's default) or rating (highest first).
🎚️ Filters (applied before billing)
starsinteger list[] (all)e.g. [1, 2]. Server-side.
sinceDate / untilDatedate—UTC; a plain date covers the whole day. Early stop with newest.
onlyWithDeveloperReplybooleanfalseOnly reviews carrying a developer reply.
onlyNewReviewsbooleanfalseMonitor mode (above). Forces newest.
📊 Output & cost
maxReviewsPerAppinteger200Per app × language × country. A review already delivered for another country/language fills a slot without being billed again. 0 = no cap.
maxItemsinteger10000Hard cap for the whole run ($0.45). 0 = none.
dedupeAcrossCountriesbooleantrueDrop a review already delivered for another country or language.
⚙️ Advanced
includeAppDetailsbooleantrueAdds appName/appScore/appRatingsCount and detects non-existent packages; one ~1.3 MB page per app.
maxConcurrencyinteger3Parallel app × language × country walks, max 5.
proxyConfigurationproxyoffDirect from Apify's network by default; retries after two failures move to a RESIDENTIAL session automatically.
saveRawResponsesbooleanfalseDebugging: stores up to 40 raw Google responses in the run's key-value store.

Pricing

$0.000045 per review returned — $0.045 per 1,000. Charged on the review-scraped event, as rows are pushed. No start fee, no monthly fee from this Actor.

RunReviewsCost
The shipped default200$0.009
10 apps × 200 newest2,000$0.09
One app's newest 10,00010,000$0.45
A 100,000-review corpus100,000$4.50
  • You pay for rows delivered. Repeats, reviews removed by your star/date/reply filters, reviews a previous monitor run already delivered and packages that do not exist are never charged.
  • A run's maximum total charge is honoured to the row: whole reviews, clean stop.
  • Every run ends with a billing integrity check — charged events must equal delivered rows exactly, or the run errors instead of mis-billing.

Honest limits

  • Only written reviews. Star-only ratings are not public and not in any Play endpoint.
  • Language is Google's matching, not a detector. Google serves reviews for the hl you ask; a few may be in another language. The language field states what was requested.
  • Replies depend on the publisher — 0% for many large apps (see the fill table).
  • criteria values are unlabelled by Google and passed through raw.
  • One Google field is not emitted: about 1% of reviews carry a second short text next to the review body whose meaning Google does not label. Rather than guess a name for it, the Actor leaves it out.
  • Throughput: measured 10,000 reviews in 37.6 s at the default 512 MB (one app, one walk). Continuation tokens are sequential by nature; parallelism comes from multiple app × language × country walks.
  • This is Google's internal web endpoint, not a published API. If Google changes the response shape, the run fails loudly (it will not ship nulls) and the parser gets updated.

How it works

The Actor POSTs to play.google.com/_/PlayStoreUi/data/batchexecute (rpc UsvDTd) — the call the Play Store website makes when you scroll its review list — with the sort, the page size (200), the star filter and the continuation token, and reads the JSON the page itself renders from. The request and response shape were derived from bytes captured on 2026-09-23, and the offline test suite (29,553 assertions) runs the parser over real captured pages: an 8-page corpus walked to its end (1,574 reviews, 0 duplicates, dates strictly newest-first), star-filtered pages, a German store page, and a non-existent package.

Transport, measured: direct from Apify's network, 0 retries across every test run (including 51 consecutive pages for a 10,000-review walk). A non-200, a body that is not a batchexecute answer, or a Google "unusual traffic" page is treated as a transport failure — retried with backoff and, from the third attempt, on a fresh RESIDENTIAL session — never as "no more reviews".


Support

Found an app that returns nothing, or need another field? Open an issue on the Issues tab with the package name, language and country.