cd /news/developer-tools/how-to-scrape-app-store-google-play-… · home topics developer-tools article
[ARTICLE · art-111640] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

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.

read8 min views4 publishedAug 26, 2026

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.

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.

import requests, re, json

def fetch_playstore_reviews_raw(package_name: str):
    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"},
    )
    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 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.

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });

const run = await client.actor('plum_spear/aztec-apify-reviews').call({
  store: 'googleplay',
  appId: 'com.whatsapp',
  country: 'us',
  maxReviews: 200,
  enrich: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const negative = items.filter((r) => r.ai.sentiment === 'negative');
console.log(negative.length, 'negative reviews out of', items.length);
python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("plum_spear/aztec-apify-reviews").call(run_input={
    "store": "googleplay",
    "appId": "com.whatsapp",
    "country": "us",
    "maxReviews": 200,
    "enrich": True,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["ai"]["type"] == "bug":
        print(item["rating"], item["ai"]["summary"])
apify call plum_spear/aztec-apify-reviews --input '{"store": "appstore", "appId": "389801252", "maxReviews": 100}'
curl "https://api.apify.com/v2/acts/plum_spear~aztec-apify-reviews/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"store": "appstore", "appId": "389801252", "maxReviews": 100}'

rating <= 2

, and let ai.type

separate bugs from feature requests automatically - pipe new negatives to Slack so the team sees them within hours.appId

on a schedule to see exactly what their users praise and complain about, by country.Pay-per-event: $0.15 per 1,000 reviews scraped, plus a small optional fee per review enriched with AI analysis, and a minimal actor-start event. No subscription, no monthly minimum. Apify's free monthly platform credits are enough to run a real test against your own app before deciding whether it's worth it.

For context: if the DIY route costs you reverse-engineering the Google Play RPC endpoint plus standing up and paying for your own LLM pipeline, $0.15 per 1,000 reviews with sentiment already attached is the kind of number that stops being a debate fast. For a single App Store check, the RSS snippet above is genuinely fine on its own.

If you're wiring this into an agent instead of a script, actors published on Apify, including this one, are reachable through Apify's MCP server, which exposes them as callable tools for MCP-compatible clients. Same store

/ appId

/ enrich

input, no separate integration to write.

Pulling App Store reviews yourself is genuinely easy - the RSS feed is public and needs no proxy. Google Play is a different story, and sentiment classification at volume is a project of its own either way. That combination is what App Store & Google Play Reviews Scraper on Apify closes: point it at a store and an appId

, get back reviews with sentiment, topics, a summary and a bug/feature/praise label already attached, priced at $0.15 per 1,000 reviews with no monthly commitment.

── more in #developer-tools 4 stories · sorted by recency
── more on @apple app store 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-to-scrape-app-st…] indexed:0 read:8min 2026-08-26 ·