How to Scrape App Store & Google Play Reviews (with Sentiment Analysis) 2026 A developer's guide details the technical challenges of scraping Apple App Store and Google Play reviews, including sentiment analysis. The Apple App Store offers a public RSS feed, while Google Play relies on an undocumented internal endpoint that is prone to breaking. The post also covers the additional complexity of building a sentiment analysis layer and suggests pre-enriched data as an alternative. If you are trying to scrape app reviews - from the Apple App Store, Google Play, or both - to feed a product feedback loop, watch a competitor's rating, or build a RAG pipeline on top of real user complaints, you already know the two-part problem: getting the raw review text out is only half the job, and turning thousands of reviews into "here's what users are actually saying" is a whole separate pipeline you now have to build and maintain. This post walks through what a DIY App Store and Google Play reviews scraper actually costs you to build and keep alive, including the sentiment analysis layer, and how to get pre-enriched review data without any of it. Whether you searched "app reviews scraper", "google play reviews api" or "app store reviews sentiment analysis" to get here, the tradeoffs below apply either way. The two stores are not equally hard, which is worth knowing before you start. Apple App Store exposes a public, unauthenticated RSS feed for reviews itunes.apple.com/.../rss/customerreviews/... . No headless browser, no proxy - a plain HTTP request gets you JSON. The catch: it is capped at roughly the 500 most recent reviews per country per app, so covering more means looping over country codes. Google Play has no such feed. There is no public, documented API for reading another app's reviews. The data lives behind Google's internal batchexecute RPC endpoint that the Play Store web page itself calls - undocumented, unversioned, and returning a deeply nested JSON array addressed by numeric position instead of named keys. Reverse-engineering it works, until Google ships a frontend change and the index you were reading review.author from now holds something else, silently. On top of fetching the raw text, there is the part most scrapers skip: making sense of it . Raw review text is not actionable on its own - you need sentiment, a category bug vs. feature request vs. praise , and a short summary, per review, at volume. Building that yourself means picking an LLM or NLP approach, tuning a prompt, batching calls without blowing through rate limits, and handling malformed responses - a second project stacked on top of the scraper itself. python import requests def fetch appstore reviews app id: str, country: str = "us" : url = f"https://itunes.apple.com/{country}/rss/customerreviews/id={app id}/sortBy=mostRecent/json" r = requests.get url, timeout=10 entries = r.json .get "feed", {} .get "entry", return { "title": e.get "title", {} .get "label" , "text": e.get "content", {} .get "label" , "rating": e.get "im:rating", {} .get "label" , } for e in entries 1: first entry is the app itself, not a review This genuinely works with no proxy and no browser. The limit is the feed itself: roughly 500 most recent reviews per country, and Apple does not document a hard rate limit, so you find it the hard way if you hammer this across many apps and countries. python import requests, re, json def fetch playstore reviews raw package name: str : Google Play has no public reviews API. The web page calls an internal batchexecute RPC endpoint that returns a deeply nested, positionally-indexed array - not a documented JSON schema. resp = requests.post "https://play.google.com/ /PlayStoreUi/data/batchexecute", data={"f.req": build batchexecute payload package name }, illustrative headers={"content-type": "application/x-www-form-urlencoded"}, response is prefixed with " }'" and wrapped in nested arrays that must be unwrapped by array index, not by key name raw = json.loads resp.text.split "\n", 1 1 return raw index-hunting for author/text/rating starts here build batchexecute payload above is illustrative - the real payload is an opaque encoded string that community scraping libraries maintain by trial and error, and it changes without notice. This is the wall: it works today, but there is no contract with Google saying it will work tomorrow, and when it breaks you get an empty or malformed response with no error message pointing at why. Getting text out is still not the deliverable. Turning "the new update is so laggy, please add dark mode back" into {sentiment: negative, type: bug, topics: performance } at volume means calling an LLM per review, writing a prompt that reliably returns the same JSON shape, and retrying malformed completions - infrastructure most scraper projects never get around to, so the review text just sits there unread. Pulling twenty App Store reviews for a one-off check is genuinely fine to hand-roll. The cost shows up when you need Google Play coverage plus real sentiment classification, running continuously. | DIY Python + your own infra | Managed scraper API | | |---|---|---| | App Store reviews | Free public RSS, capped ~500/country | Same feed, handled and paginated across countries | | Google Play reviews | Reverse-engineered internal endpoint, breaks silently | Maintained on the provider's side | | Sentiment / topics / type | You build and pay for an LLM pipeline | Comes back pre-attached on every review | | Output format | Raw text you still have to enrich | Structured JSON, already classified | | Scheduling | You wire up cron + monitoring | Native scheduler on the platform | Neither column is objectively "right." If you need a handful of App Store reviews once, the RSS snippet above is all you need. If you need Google Play coverage and per-review sentiment running continuously, the reverse-engineering plus the LLM pipeline is the real cost - not the initial script. This is the part where I show you the shortcut. App Store & Google Play Reviews Scraper https://apify.com/plum spear/aztec-apify-reviews is an Apify actor that pulls reviews from both stores and attaches sentiment, topics, a summary, and a bug/feature/praise/complaint label to every single review - with zero setup required, and an optional bring-your-own-LLM mode for deeper analysis. | Field | Description | Example | |---|---|---| store | appstore or googleplay | appstore | appId | App Store numeric ID or Google Play package name | 389801252 / com.whatsapp | url | Full store URL alternative to appId | https://apps.apple.com/us/app/id389801252 | country | Two-letter store country code | us , br , de | language | Language code Google Play | en , pt | maxReviews | Maximum number of reviews to fetch | 100 | enrich | Enable AI analysis on each review | true | llmBaseUrl / llmModel / llmApiKey | Optional OpenAI-compatible endpoint for richer analysis | https://api.openai.com/v1 | { "store": "appstore", "appId": "389801252", "country": "us", "maxReviews": 100, "enrich": true } Leave the LLM fields empty and the AI fields still populate, via built-in keyword analysis - no external API key required to get sentiment out of the box. { "store": "appstore", "appId": "389801252", "country": "us", "title": "Please bring back the old feed", "text": "I think Instagram is generally great but the new feed is...", "rating": 2, "version": "350.1", "author": "user handle", "ai": { "sentiment": "negative", "topics": "feed algorithm", "user experience" , "summary": "User dislikes the new feed and wants the old one back.", "type": "feature request", "method": "llm" } } Every review comes back with the ai block already attached - sentiment , topics , summary , type bug / feature request / praise / complaint / question / other , and method telling you whether it ran through your LLM or the built-in analyzer. No separate classification step to write. js import { ApifyClient } from 'apify-client'; const client = new ApifyClient { token: '