Google Play Scraper: Reviews, Apps, Charts, ASO avatar

Google Play Scraper: Reviews, Apps, Charts, ASO

Pricing

$0.05 / 1,000 result rows

Go to Apify Store
Google Play Scraper: Reviews, Apps, Charts, ASO

Google Play Scraper: Reviews, Apps, Charts, ASO

Google Play keeps reviews in separate buckets per language: 18 languages on one app gave 10800 different reviews and 0 duplicates. One-language scrapers see one bucket. Reviews, app details, search, top charts, developer listings and similar apps in one Actor - 81 columns.

Pricing

$0.05 / 1,000 result rows

Rating

0.0

(0)

Developer

Snow Leo Data

Snow Leo Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

an hour ago

Last modified

Share

Google Play does not keep one pile of reviews per app. It keeps one pile per language the review was written in, and the piles do not overlap. That single fact is the reason this Actor exists.

Measured on com.spotify.music on 13 September 2026: 18 languages, 600 reviews pulled from each, 10800 different review IDs and 0 duplicates between languages. A scraper with one language field in its input sees one pile out of many, reports success, and the buyer never learns what was missing.

This Actor takes a list of languages, a list of apps and a list of device types, and walks all of them in one run. It also does app details, keyword search, top charts, developer listings and similar apps — 6 modes, one Actor, one subscription.

What it returns

Every row is a flat record with a kind field: review or app. Both kinds live in the same dataset because a review score is meaningless without the app's own rating next to it, and splitting them across two datasets would only push the join onto you. 81 columns are declared; a live app row filled 50 of them and a live review row filled 20. Turn on Compact output and empty columns disappear from the rows entirely.

Review rows carry the review ID and its permanent URL, the author name, the author's Google user ID, the avatar, the 1-5 score, the text, the posting time as both ISO-8601 and a Unix timestamp, the thumbs-up count, the app version the reviewer was running, the developer's reply with its own author and date, and Google's own structured quality tags — machine labels such as vaf_app_quality_stability, vaf_app_quality_ads_frequency and vaf_app_quality_battery_efficiency that the store attaches to reviews and that no other Actor in this niche publishes.

App rows carry the title, the full description in both HTML and plain text, the short summary, the developer with their store page, e-mail, website and postal address, the privacy policy link, the genre and every category, the score and the complete 1-to-5 rating histogram, the ratings count and the reviews count separately, the install band, its lower bound and the exact install number Google publishes internally, price in both micros and currency units, the in-app purchase range, the "contains ads" flag, the content rating, the release and update dates, the version, the minimum Android version, the target SDK level, the "what's new" text, the icon, header image, video and screenshots, the Data safety section, the Android permissions on request, the average score per device class, and the IDs of similar apps.

Why one language is not enough

The buckets are the whole story, so here are the numbers behind it.

what was measuredresult
languages swept on com.spotify.music18
reviews taken per language600
unique review IDs collected10800
duplicates between languages0
the same run limited to English only600

Device type splits the reviews the same way. Phone and tablet on the same app returned 200 reviews each with 0 in common, so adding a device type adds reviews instead of repeating them.

The country storefront does not split reviews, and this matters because it is the field people assume does. English/United States and English/Germany returned the same 200 reviews — an overlap of 200 out of 200. So for reviews, pick languages, not countries. For charts, prices and availability it is the other way round: the overall chart of 8 countries returned 351 different apps against 84 from one country alone, and the same search keyword across those 8 storefronts returned 55 different apps against 30 from one.

Speed and the ceiling of one request

The reviews endpoint accepts a page size, and the ceiling was found by halving the interval: 4500 reviews come back in one call, 4501 returns HTTP 200 with an empty body. No error, no message — just nothing. Anything larger is clamped to the ceiling before the request leaves, because a silent empty answer is the worst kind of failure.

At 4500 per call the measured rate was 2.32 seconds per request. The default is 1000 reviews in one call, which keeps memory small and is still ten times the 100-per-page default of the largest competing Actor.

Modes

  • reviews — reviews of the apps you list, across every language and device type you pick. With app details on, one app row is written per app first.
  • apps — full app pages for the apps you list, one row per country storefront.
  • search — Google Play's own search for your keywords. One search page returns 30 apps.
  • charts — the top chart of a country, optionally narrowed to one of 51 categories.
  • developer — every app published by a developer. Both the developer name as shown on the store and the numeric developer ID are accepted, and they live on different URLs, so the Actor picks the right one for you.
  • similar — the apps Google shows as similar. These arrive with the app page itself, so this mode costs no extra request.

50 languages and 53 country storefronts are offered as pick-lists; anything else you type is passed through as-is.

Monitoring: pay only for what moved

The niche runs at 90 runs per user, which means scheduled monitoring, not one-off exports. Turn on Only new and changed rows and the Actor keeps a record in a named key-value store of everything it has already delivered. Every row then arrives labelled NEW, UPDATED or UNCHANGED, and an updated row carries a changed_fields list with the old and the new value of each field that moved.

The fingerprint is taken from the fields worth watching, not from the whole row. For a review that is the score, the text, the thumbs-up count, the developer reply and the app version — so a developer answering an old review shows up as UPDATED with reply_text named, while the row merely being scraped again does not. For an app it is the score, the ratings and reviews counts, the installs, the version, the price, the ads flag, the title, the update date and the chart rank.

A run that finds nothing new writes nothing, charges nothing, and says so by name rather than finishing silently empty.

Empty results always have a name

A green run with an empty dataset is the worst outcome for a buyer: money spent on the start, nothing to show, no explanation. This Actor names the reason in the empty_reason field of the REPORT record and repeats it in the log as a warning. There are 18 named reasons, and 12 of them are decided from your input alone, before a single byte is downloaded:

no-target-given, all-app-ids-invalid, no-languages-selected, no-device-types-selected, star-filter-excludes-every-star, date-not-understood, app-filter-without-app-details, date-window-is-empty, score-range-is-empty, free-only-and-paid-only-at-once, category-not-recognised and charge-limit-reached.

The rest can only be known after the crawl: filtered-out, nothing-changed, source-returned-nothing, all-apps-unreachable, app-not-found and source-rate-limited.

The pattern behind the first group is always the same — one field narrows the result while another switches off the data the first one needs. Asking to keep only apps above a million installs while app details are off would drop every row, because the install count lives on the app page and nowhere else. That is caught and explained instead of being crawled and thrown away.

Filters run before you are charged

Every filter runs on our side, and a row a filter drops is never written to the dataset, so it is never billed. The REPORT record names which filter dropped how many rows.

One honest detail about how filtering works here. Google Play has no server-side star filter. Six different request shapes were tried — the value in five different positions of the request and two at the top level — and none was honoured: the same mix of scores came back every time. So the star filter, the keyword filter and the date window all run after the download. The crawler compensates by walking with headroom and stopping on the number of delivered rows rather than crawled ones. Ask for 30 one-star reviews and you get 30, not an empty dataset with a note that 30 reviews were collected and all of them dropped.

The other rule in the filters is that a missing value passes. An app whose install count Google does not publish is not dropped by a minimum-installs filter; absence of data is not a mismatch. Losing half a result set to one unfilled key at the source, with a green run to show for it, is not something you would find out about until much later.

How it works underneath

No proxy, no browser, no API key. Two paths on play.google.com are used: the store pages, which carry their data inside AF_initDataCallback blocks, and the internal batchexecute endpoint, which serves reviews and permissions.

Both were verified from Apify's own cloud before a single line of this Actor was written, using a probe Actor: twelve store URLs across five country storefronts and six RPC calls, all HTTP 200 with real data. That check is not ceremony. A neighbouring source in this portfolio answered 200 from a laptop and 403 from every Apify address, and the Actor built on it had to be thrown away whole.

The parser finds app cards by their shape, not by their position in the page. A card is a list whose first element is a package-name-and-device pair and whose fourth element is a title. The same cards sit at different paths in search results, charts, developer pages and the similar-apps block, so a hard-coded path would break on the first layout change.

By default 4 app pages are fetched at once and the timeout per request is 30 seconds. The defaults ask for 200 reviews per app, 1000 reviews in one call and 120 apps per query.

Honest limitations

These are the places where a competing Actor does something we do not, or where the source itself stops us. None of them is hidden in a footnote elsewhere.

  • No star filter at the source. As described above, six request shapes were tried and none worked. Filtering happens after the download. This costs requests, not correctness.
  • Search and charts are not deeply paginated. One search page returns 30 apps and a chart page returns roughly a hundred; the continuation token that the store's own front end uses was tried in three shapes and answered with an error each time. Depth is bought with more storefronts and more keywords instead — 8 countries turned 30 search results into 55 and 84 chart entries into 351.
  • Per-device scores come as numeric device classes, not names. Google publishes the averages with codes and does not label them anywhere on the page in a way that survives checking across apps, so the code is published as a code. Inventing names for them would be worse than leaving them numeric.
  • version and android_version are often empty. That is the source telling the truth: large apps ship per-device builds and the store shows "Varies with device". The field is left null rather than filled with a guess.
  • Reviews cannot be filtered by country. This is a property of Google Play, measured above: the storefront does not change which reviews you get.
  • No sentiment, no topic model, no scoring. Everything in a row comes from Google Play. Nothing is inferred, generated or estimated.
  • A free Apify account has a compute quota. Long multi-language sweeps of very large apps are best run on a paid plan or split across scheduled runs with monitoring turned on.

How we compare

The niche has 97 Actors and 11172 users between them, at 90 runs per user. The largest of them, neatrat/google-play-store-reviews-scraper with 2605 users, does reviews only: 1 app per run, 1 language storefront per run, 13 published fields, 100 reviews per page by default, no app details, no charts, no search, no developer listing, no monitor mode and no named reason when a run comes back empty.

Against that, this Actor takes a list of apps, a list of languages and a list of device types in one run; publishes 20 filled fields on a live review row including the developer reply and Google's quality tags; asks for up to 4500 reviews in a single call; and covers 6 modes where the competition sells them as separate Actors.

A full line-by-line comparison, including every place where a rival is ahead of us, is in COMPARISON.md in the source repository.

FAQ

Which languages should I pick? The ones your users write in. Reviews are bucketed by the language of the text, so picking German gets you German-language reviews regardless of where the reviewer lives. If you want everything, pick everything: the buckets do not overlap, so there is no duplicate cost.

Do I need a proxy? No. Both paths answer from Apify's cloud without one, and that was checked from the cloud before the Actor was built.

Why do I get an app row when I asked for reviews? Because "Fetch full app details" is on by default and it writes one app row per app before the reviews, so the review scores have the app's own rating, histogram and install count to be read against. Turn it off and you get reviews only.

How do I get every review of an app? Set Max reviews per app to 0, pick your languages, and let it run. Google Play gives no total for a language bucket in advance, so the Actor walks until the cursor stops coming back.

What does the rank field mean? In search, charts and developer modes it is the position the app held in that result list, starting at 1. It is part of the monitored fields, so in monitor mode a rank change arrives as an UPDATED row with the old and new position.

Can I chain this with another Actor? Yes. Put another run's dataset ID into Read app IDs from another dataset and name the field that holds the package name or the store URL.

What happens if a package does not exist? The store answers HTTP 404 and the package is listed in the REPORT record under not_found. If every package you gave is missing, the run ends with app-not-found rather than an unexplained empty dataset.

Is the data real-time? It is whatever the store serves at the moment of the request. Review counts and scores on the app page can lag the review list by a few minutes; that is the source, not the Actor.