DEV Community

Roberto Kerber
Roberto Kerber

Posted on

How to Scrape App Store & Google Play Reviews (with Sentiment Analysis) 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.

Why scraping app reviews is harder than it looks

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.

Approach 1: DIY with Python

Attempt 1: App Store reviews via the RSS feed (the easy part)

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
    ]
Enter fullscreen mode Exit fullscreen mode

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.

Attempt 2: Google Play reviews (the hard part)

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
Enter fullscreen mode Exit fullscreen mode

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.

Attempt 3: bolting sentiment analysis on top

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.

The real cost of DIY isn't the scraper, it's the pipeline behind it

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.

Approach 2: a ready-made reviews scraper with built-in AI enrichment

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.

Input

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
}
Enter fullscreen mode Exit fullscreen mode

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.

Output

{
  "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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Calling it from JavaScript

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);
Enter fullscreen mode Exit fullscreen mode

Calling it from 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"])
Enter fullscreen mode Exit fullscreen mode

Calling it from the CLI or plain REST

apify call plum_spear/aztec-apify-reviews --input '{"store": "appstore", "appId": "389801252", "maxReviews": 100}'
Enter fullscreen mode Exit fullscreen mode
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}'
Enter fullscreen mode Exit fullscreen mode

What people actually build with this

  • Daily 1-star review triage. Run daily, filter to rating <= 2, and let ai.type separate bugs from feature requests automatically - pipe new negatives to Slack so the team sees them within hours.
  • Weekly sentiment trend reports. Track the positive/neutral/negative split week over week and watch whether a release moved the needle before it tanks the store rating.
  • Competitor review monitoring. Point it at a rival's appId on a schedule to see exactly what their users praise and complain about, by country.
  • ASO and growth research. Mine review language for keyword opportunities and understand what actually drives ratings up or down.
  • AI / RAG pipelines. Feed pre-classified, structured review data straight into an LLM app without a separate preprocessing stage.

Pricing

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.

Using it from an AI agent (MCP)

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.

Wrap-up

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.

Top comments (0)