Google Play Reviews - App Ratings, Replies & Countries
Pricing
from $1.00 / 1,000 reviews
Google Play Reviews - App Ratings, Replies & Countries
Collect Google Play reviews and ratings for any app across country storefronts. Search by app name or use a Google Play URL. Returns one row per review with rating, text, author, date, app version, thumbs-up count, developer reply, and storefront country.
Pricing
from $1.00 / 1,000 reviews
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
4
Total users
0
Monthly active users
7 days ago
Last modified
Categories
Security
Limited permissionsShare
Google Play Reviews Scraper: Ratings, Text & Developer Replies
Google Play Reviews Scraper turns Google Play Store reviews into a clean, structured dataset. Get the star rating, review text, author, date, app version, thumbs-up count and developer replies for any app, across as many country storefronts as you choose. Search by app name or paste Google Play links (a bare package id like com.example.app works too), then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
All filters run before the Max reviews budget is counted and before anything is billed, so reviews that do not match are never charged. Reviews are always de-duplicated by their unique review id (across countries too), so there is no separate unique-only switch. Each review carries position, its 1-based rank in the order Google Play served it for that app and country in this run; it changes with sort order and is ignored by Incremental mode change detection. Google Play does not expose a device type or a "reviewed in" label on the reviews feed, so those are not offered.
Why This Scraper?
- Reviews are the primary output. Every row is one full review: rating, body, author, date, app version, thumbs-up, developer reply, plus the storefront country and language.
- Two ways in. Search by app name or keyword, or paste Google Play links: app pages, search pages, developer pages, category pages or collection pages. A bare package id works too, no need to build a full URL.
- Multi-country sweep. Reviews are storefront-specific, so pass a list of country codes, or use
"all"for a broad built-in set of major markets, to gather the full picture for one app. - Sort and filter. Order by newest, most helpful, or by rating, and keep only reviews within a minimum/maximum star band.
- Optional app-metadata enrichment. Attach developer, average score, ratings count, installs, category, price and more to every review from that app.
- Built for schedules. Incremental mode returns only new and changed reviews on recurring runs, and a refused run fails loudly instead of returning an empty dataset.
- Resume without re-billing. Continue an interrupted run from its own run or dataset id; reviews it already collected are skipped and not re-charged.
Use Cases
- App Store Optimization (ASO) teams: track rating trends and recurring complaint themes across storefronts to prioritize the release roadmap.
- Customer support and QA teams: catch new low-rated reviews quickly and route them into a ticketing pipeline before ratings drop further.
- Competitive analysis: compare review volume, rating trends and developer response patterns across competing apps.
- Localization and product teams: sweep multiple countries to see how the same app is received in different markets and languages.
- Data science and NLP teams: build labeled datasets of app reviews for sentiment or topic modeling.
Data You Get
Sample shape: values are illustrative placeholders, not from a live review.
| Field | Example |
|---|---|
reviewId | "00000000-0000-0000-0000-000000000000" |
appId | "com.example.app" |
appName | null unless Enrich with app details is on |
country | "us" |
language | "en" |
rating | 5 |
body | "Full review text appears here." |
author | "Reviewer Name" |
authorId | "100000000000000000000" |
authorImage | "https://play-lh.googleusercontent.com/..." |
thumbsUp | 12 |
appVersion | "1.0.0" |
reviewDate | "2026-01-01T00:00:00+00:00" |
reviewTimestamp | 1767225600 |
reviewUrl | Google Play link to that specific review |
developerReply | "Thanks for the feedback!" |
developerReplyDate | "2026-01-02T00:00:00+00:00" |
Every review also carries appName, which is null unless Enrich with app details is on. With enrichment turned on, every review from that app additionally carries appDeveloper, appDeveloperEmail, appDeveloperWebsite, appScore, appRatingsCount, appInstalls, appCategory, appContentRating, appPrice, appCurrency, appIconUrl, appUpdated and appDescription, plus an appStoreUrl back to the app's listing. With enrichment off (the default), all of those appXxx fields and appStoreUrl are absent. In Incremental mode, every row also carries changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields, firstSeenAt and lastSeenAt; see How to Use below for exactly when each fires.
How to Use
- Pick a mode:
search(by app name or keyword) orurl(paste Google Play links or bare package ids). - Choose the countries to sweep, the sort order, the review language, and an optional minimum/maximum rating.
- Optionally turn on Enrich with app details, and set Max reviews to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search by app name:
{"mode": "search","queries": ["Instagram", "Spotify"],"appsPerQuery": 1,"countries": ["us"],"sortBy": "newest","maxItems": 100}
URL shapes accepted in URL mode:
| Shape | Example |
|---|---|
App page with hl/gl | https://play.google.com/store/apps/details?id=com.instagram.android&hl=en&gl=us |
| App page, id only | https://play.google.com/store/apps/details?id=com.instagram.android |
| App page, other locale | https://play.google.com/store/apps/details?id=com.instagram.android&hl=de&gl=de (hl/gl seed language and country) |
| Shared review link | https://play.google.com/store/apps/details?id=com.instagram.android&reviewId=x (the app is read from id) |
| Show all reviews link | https://play.google.com/store/apps/details?id=com.instagram.android&showAllReviews=true |
| Legacy host | https://market.android.com/details?id=com.instagram.android |
| Search page | https://play.google.com/store/search?q=instagram&c=apps&hl=en&gl=us |
| Developer page (numeric id) | https://play.google.com/store/apps/dev?id=5700313618786177705 |
| Developer page (name) | https://play.google.com/store/apps/developer?id=Instagram |
| Category page | https://play.google.com/store/apps/category/SOCIAL |
| Collection page | https://play.google.com/store/apps/collection/topselling_free |
Search, developer, category and collection pages expand to the top apps on the page, at most Apps per search term per URL (default 1). An app link whose package does not exist (HTTP 404) is skipped with a warning in the log. Behaviour change: URL mode run with Google Play URLs left untouched now scrapes one example of each shape above (previously it had no default and needed your own links). Replace the list with your own links to scrape specific apps.
Sweep several countries for one app via its link:
{"mode": "url","urls": ["https://play.google.com/store/apps/details?id=com.instagram.android"],"countries": ["us", "gb", "de", "jp"],"sortBy": "mostHelpful","maxItems": 500}
Only high-rated reviews, with app details attached:
{"mode": "url","urls": ["https://play.google.com/store/apps/details?id=com.instagram.android"],"countries": ["us"],"minRating": 4,"fetchDetails": true,"maxItems": 200}
Recurring monitoring, including reviews that disappear:
{"mode": "url","urls": ["https://play.google.com/store/apps/details?id=com.instagram.android"],"countries": ["us"],"incrementalMode": true,"stateKey": "instagram-us-daily","emitExpired": true,"maxItems": 0,"maxPages": 0}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/google-play-reviews-scraper").call(run_input={"mode": "url", "urls": ["com.instagram.android"], "countries": ["us"], "maxItems": 100})for review in client.dataset(run["defaultDatasetId"]).iterate_items():print(review["rating"], review["author"], review["body"][:80])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/google-play-reviews-scraper').call({mode: 'url', urls: ['com.instagram.android'], countries: ['us'], maxItems: 100,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Output views
The dataset has four views: Reviews (the main table, including position), Changes (incremental changeType, changedFields, first/last seen), App details (the app-level fields when enrichment is on) and Developer replies (reviews that have a developer reply). Multi-app or multi-country runs also store a run summary under the OUTPUT key of the key-value store.
Multi-country sweeps and "all"
Reviews are storefront-specific: each country returns its own review stream for the same app. Leave Countries empty to use the country from each URL (url mode) or us (search mode), or set it to ["all"] to sweep a broad built-in set of major storefronts in one run. Max reviews is split evenly across every app/country stream so one popular storefront cannot use up the whole budget before the others get a turn.
Resume and Incremental mode (recurring monitoring)
- Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset id and the actor skips every review it already collected there, so you don't pay twice. - Incremental mode (
incrementalMode) is for scheduled runs against the same app/country/filter set. Each review is classifiedNEW,UPDATED(withchangedFields), orUNCHANGED(suppressed and not billed unlessemitUnchangedis on). Rating, review text, author, thumbs-up count, app version and developer replies are all compared; app-level metadata attached byfetchDetails(developer, install count, category, ...) is excluded from comparison, since it describes the app, not the individual review, and changes on its own as other users interact with the app. - Ignore fields when detecting changes (
ignoreFieldsForChanges, incremental mode only) lists output field names (for examplethumbsUp,developerReplyordeveloperReplyDate; review rows are flat, so dotted paths are accepted but not needed) left out of the changed/unchanged decision and out ofchangedFields; they still appear in each row. Changing the list never turns already-tracked reviews intoUPDATED. EXPIREDis only ever reported after a run that fully re-walked every tracked app/country stream, with no Max reviews/Max pages cut, no resume, and no checkpoint recovery mid-run, ANDemitExpiredis on. A capped, partial or resumed run cannot tell "review deleted" apart from "not reached yet", so it skips this detection entirely and leaves the previous state untouched.REAPPEAREDnever happens "by default": it only fires for a review that a PRIOR complete scan already markedEXPIRED, and a LATER run finds again.stateKeynames or shares the tracked campaign; leave it empty to derive one automatically from mode, queries/urls, countries, language, sort order, rating filters andfetchDetails(Max reviews, Max pages and delivery options do not affect it).- Combining
resumeFromRunIdwithincrementalModeonly works to bootstrap a brand-new campaign from an earlier non-incremental run's dataset. Once a campaign already has tracked state, the run fails fast instead of silently reclassifying a partial delta against a baseline it never fully built.
Send results into your apps (MCP connectors)
Optionally pipe the scraped reviews into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each review, not the full JSON. Each review becomes one entry with a heading (the app's name when Enrich with app details is on, otherwise its package id, plus the star rating) and its key fields (country, rating, author, date, app version) flattened to plain text, plus the review body. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where review pages should be created. maxNotifyListingscaps how many reviews are sent to each connector per run; it never affects the dataset.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (by app name) or url (paste app links). |
queries | array | no default (prefill: ["Instagram"]) | App names or keywords to look up (search mode). |
appsPerQuery | integer | 1 | Top matching apps to take per search term (search mode). |
urls | array | one example of each URL shape listed above | Google Play app, search, developer, category or collection links, or bare package ids (url mode). |
countries | array | no default (prefill: ["us"]) | Country codes to collect reviews from. Empty uses the link's country or us. "all" sweeps a broad built-in set of major markets. |
language | string | en | Review language / locale code. |
sortBy | string | newest | newest, mostHelpful, rating, or mostRelevant (Google Play's relevance order, the same order as mostHelpful). |
minRating | integer | no default (optional) | Keep only reviews at or above this star rating (1-5). |
maxRating | integer | no default (optional) | Keep only reviews at or below this star rating (1-5). |
appIdOrUrl | string | no default (optional) | Single-app shortcut: one app link or package id. Switches the run to URL mode and replaces the Start URLs list, so only this app is scraped. |
rating | integer | no default (optional) | Exact star rating (1-5); shortcut for setting Min and Max rating to the same value. |
keywords | array | no default (optional) | Keep only reviews whose text contains at least one of these words (case-insensitive). |
appVersion | array | no default (optional) | Keep only reviews written on one of these app versions. |
startDate | string | no default (optional) | Keep reviews from this day on (YYYY-MM-DD, UTC). With newest-first sorting the walk stops once it passes the start date. |
endDate | string | no default (optional) | Keep reviews up to this day (YYYY-MM-DD, UTC). |
recentDays | integer | 0 | Keep only reviews from the last N days (0 = no limit). |
fetchDetails | boolean | false | Attach app metadata to each review from that app. |
maxItems | integer | 20 | Total review cap. 0 means no limit. |
maxPages | integer | no default (optional) | Safety bound on review pages read per country (about 40 reviews per page). Leave empty to walk every page Google Play serves, up to a hard limit of 1000 pages per country. Max reviews is capped at 1000000. |
resumeFromRunId | string | no default (optional) | Run id (or dataset id) of a previous run. Reviews it already collected are skipped. |
incrementalMode | boolean | false | Recurring monitoring: classifies each review as NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED against the previous run of the same campaign. |
stateKey | string | no default (optional; auto-derived) | Name this monitoring campaign explicitly, instead of an automatic hash of your search/filter settings. |
emitUnchanged | boolean | false | Also return (and bill for) reviews unchanged since the last run. |
emitExpired | boolean | false | Also return (and bill for) a tombstone row for reviews the previous run saw that this run no longer finds. |
ignoreFieldsForChanges | array | none (optional) | Incremental mode only: output field names (for example thumbsUp) ignored when deciding whether a review changed. |
proxy | object | {"useApifyProxy": true} | Connection settings. |
mcpConnectors | array | no default (optional) | Send a summary of each review to apps you authorized under Integrations. |
notionParentPageUrl | string | no default (optional) | Notion connector only: page under which review pages are created. |
maxNotifyListings | integer | 50 | Cap on reviews written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live review.
{"reviewId": "00000000-0000-0000-0000-000000000000","appId": "com.example.app","country": "us","language": "en","rating": 5,"body": "Full review text appears here.","author": "Reviewer Name","authorId": "100000000000000000000","authorImage": "https://play-lh.googleusercontent.com/...","thumbsUp": 12,"appVersion": "1.0.0","reviewDate": "2026-01-01T00:00:00+00:00","reviewTimestamp": 1767225600,"reviewUrl": "https://play.google.com/store/apps/details?id=com.example.app&reviewId=00000000-0000-0000-0000-000000000000","developerReply": "Thanks for the feedback!","developerReplyDate": "2026-01-02T00:00:00+00:00"}
Plan Requirement
The default connection uses Apify datacenter proxies, which were measured to load Google Play app pages, search pages and the reviews feed the same as residential, so no residential proxy or paid proxy add-on is required for normal runs. A paid Apify plan is still needed for any Apify proxy access: Apify free plans do not include proxies, and a run whose proxy connection is refused for the account stops with a clear message (see the FAQ). If you supply your own proxy URLs, they are used as given.
When the default connection becomes unavailable mid-run, the log prints "The selected connection is unavailable to this run." and the run continues on a backup connection. Once pages are served that way it logs "Served over the backup connection (the default connection was unavailable)." Any cost of the backup connection is billed to the user's own proxy usage; no extra Actor charge applies.
FAQ
How much does it cost?
You pay per review returned (one billed unit for each review row delivered to the dataset), plus a detail-enrichment charge of one unit per app, applied once per run only to an app whose enriched reviews you actually receive when Enrich with app details is on, never for the metadata lookup alone. Reviews removed by your filters, duplicates, unchanged incremental rows and failed requests are never billed. The Pricing tab shows the current rates. Use Max reviews to cap the cost of any run.
Is it legal to scrape Google Play?
This actor collects only publicly available review data. You are responsible for how you use it: follow Google Play's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Reviews are user-generated content and may carry rights beyond the platform's own terms.
Can I get only new or changed reviews on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated reviews for the tracked app/country set, and unchanged ones are not billed.
Why is appName / appDeveloper / appScore empty on my reviews?
Those fields, along with the rest of the app-metadata block, are only attached when Enrich with app details is turned on. With it off (the default), appName is null and every other appXxx field is absent from the output.
Why did my run fail instead of returning an empty dataset?
If every request was refused, or if none of your search terms, URLs or package ids could be resolved into an app, the run stops with a clear message so a genuine block is never confused with "this app has no reviews". Which message you see tells you why:
- "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required" means the account has no proxy access (or your own proxy URLs have invalid credentials).
- "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Google Play pages." means the default and backup connections both failed; upgrade your plan or supply your own proxy URLs.
- "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." means a transient connection problem; try again later.
A run that reached Google Play fine and simply found zero matching reviews still completes normally with an empty dataset.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more social media data?
Pair this actor with these related scrapers from the same team:
| 📇 Apple App Store Reviews Scrape Apple App Store reviews across 150+ country storefronts. Search by app name or URL... | 🤖 Suno Scraper Collect public Suno music data: songs with lyrics, style tags, model version, play and... |
| 📱 TrustMate.io Scrape complete company review histories from TrustMate.io. Paste company profile URLs... | 📇 Google Maps Scraper Extract business data from Google Maps at scale. Get names, addresses, phone numbers... |
| 🤖 Udio Scraper Collect public Udio tracks with the complete generation prompt, full lyrics, style tags... | 🧩 Trustpilot Scraper Scrape Trustpilot business profiles and reviews on any country domain: TrustScore, the... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.