Tutorial
· Updated August 29, 2026
How to Scrape Google Play Reviews
Fetch and filter public Google Play reviews as structured JSON. Package names, rating filters, version tracking, and release regression detection.
Written by Alex P.
- Google Play reviews API
- Play Store scraper
- Android app reviews
- release monitoring
- app feedback
Google Play reviews are the most direct feedback channel most Android apps have. They name specific crashes, specific devices, and specific versions — often before the same issue shows up in your crash reporting, because a user who force-quits an app that hangs never generates a stack trace.
The problem is that the Play Store web UI is built for browsing, not analysis. You can’t filter by version, you can’t diff between releases, and you can’t get it into a spreadsheet without copy-pasting.
This guide covers pulling Google Play reviews as structured JSON with server-side filtering, and using them to detect release regressions.
Need the endpoint? See the Google Play Reviews API, or the developer reference for the full parameter list.
What makes the Play Store endpoint different
Most review APIs return everything and leave filtering to you. This one pushes filtering server-side, which matters because it changes what you pay for — you’re billed per page retrieved, so filtering before pagination is filtering before cost.
The parameters that do real work:
| Parameter | What it does |
|---|---|
appIdOrUrl | Package name (com.spotify.music) or a Play Store URL |
sortBy | newest, mostRelevant, or rating |
rating | Return a single star rating |
ratingFilter | Array of star ratings to include, e.g. [1, 2] |
keywords | Only reviews matching these terms |
appVersion | Array of versions to include |
recentDays | Only reviews from the last N days |
endDate | Latest review date, YYYY-MM-DD |
language | Array of language codes |
deviceType | mobile, tablet, or chromebook |
uniqueOnly | Drop duplicate reviews |
reviewsPerPage | Up to 200 (much higher than most review APIs) |
appVersion and recentDays together are what make regression detection practical, and reviewsPerPage at 200 means far fewer requests than platforms capped at 20.
1. Find the package name
Every Play Store URL contains it:
https://play.google.com/store/apps/details?id=com.spotify.music
^^^^^^^^^^^^^^^^^^
The id parameter is the package name. You can pass either the package name or the whole URL as appIdOrUrl — the endpoint accepts both.
Package names are stable identifiers. Unlike App Store numeric IDs, they’re human-readable and tell you the developer’s reverse-domain namespace, which is handy when you’re tracking a competitor’s portfolio.
2. Fetch reviews
Only appIdOrUrl is required. Everything else is optional:
curl -X POST https://api.fetchlayer.dev/playstore/reviews \
-H "Authorization: Bearer ss-your-key" \
-H "Content-Type: application/json" \
-d '{
"appIdOrUrl": "com.spotify.music",
"pages": 1,
"reviewsPerPage": 20,
"sortBy": "newest"
}'
Response:
{
"reviews": [
{
"rating": 1,
"reviewer": "Example user",
"body": "Crashes on launch since the last update...",
"appVersion": "8.9.0",
"timestamp": "2026-08-18T10:00:00.000Z"
}
],
"analysis": { "averageRating": 3.4 }
}
Two things to note versus other review platforms:
timestamp is a real ISO date, not a relative string like “3 days ago”. That means you can sort, filter, and diff on it directly, and deduplicate on timestamp + reviewer rather than fingerprinting text.
appVersion is included per review. This is the field that makes everything below possible.
3. Filter server-side
Pulling everything and filtering in your own code works, but you pay for pages you throw away. Push the filter into the request instead.
Only one- and two-star reviews from the last week:
-d '{
"appIdOrUrl": "com.spotify.music",
"ratingFilter": [1, 2],
"recentDays": 7,
"sortBy": "newest",
"reviewsPerPage": 200
}'
Reviews mentioning specific problems:
-d '{
"appIdOrUrl": "com.spotify.music",
"keywords": ["crash", "battery", "login"],
"recentDays": 30,
"sortBy": "newest"
}'
Reviews for one release, on tablets only:
-d '{
"appIdOrUrl": "com.spotify.music",
"appVersion": ["8.9.0"],
"deviceType": "tablet",
"sortBy": "newest"
}'
That last one is hard to do any other way and answers a real question: did the release that looked fine on phones break the tablet layout?
4. Detect release regressions
This is the highest-value thing to build on this endpoint. The logic: compare rating distribution for the current version against the previous one, and alert if it degrades.
async function fetchVersion(appId, version, { days = 14 } = {}) {
const res = await fetch('https://api.fetchlayer.dev/playstore/reviews', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FETCHLAYER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
appIdOrUrl: appId,
appVersion: [version],
recentDays: days,
reviewsPerPage: 200,
pages: 3,
sortBy: 'newest',
uniqueOnly: true,
}),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
return (await res.json()).reviews;
}
function ratingStats(reviews) {
if (!reviews.length) return null;
const total = reviews.reduce((sum, r) => sum + r.rating, 0);
const negative = reviews.filter((r) => r.rating <= 2).length;
return {
count: reviews.length,
mean: total / reviews.length,
negativeShare: negative / reviews.length,
};
}
async function checkRegression(appId, current, previous) {
const [now, before] = await Promise.all([
fetchVersion(appId, current),
fetchVersion(appId, previous),
]);
const a = ratingStats(now);
const b = ratingStats(before);
// Not enough reviews on the new version yet to draw a conclusion.
if (!a || !b || a.count < 25) return { verdict: 'insufficient-data', a, b };
const meanDrop = b.mean - a.mean;
const negativeRise = a.negativeShare - b.negativeShare;
return {
verdict: meanDrop > 0.4 || negativeRise > 0.15 ? 'regression' : 'ok',
meanDrop: meanDrop.toFixed(2),
negativeRise: `${(negativeRise * 100).toFixed(1)}pp`,
a, b,
};
}
The a.count < 25 guard is important. In the first hours after a staged rollout you’ll have a handful of reviews, and small samples swing wildly — three angry users out of five is not a regression signal. Wait for volume before trusting the comparison.
5. Find what actually broke
Knowing the rating dropped isn’t actionable. Knowing why is. Pull the negative reviews for the new version and cluster them:
const bad = await fetch('https://api.fetchlayer.dev/playstore/reviews', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FETCHLAYER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
appIdOrUrl: 'com.example.app',
appVersion: ['8.9.0'],
ratingFilter: [1, 2],
reviewsPerPage: 200,
pages: 2,
sortBy: 'newest',
uniqueOnly: true,
}),
}).then((r) => r.json());
const prompt = `
These are 1- and 2-star reviews for version 8.9.0 of an Android app.
Group them into distinct problems. For each:
- a one-line description of the problem
- how many reviews report it
- 2 verbatim quotes
- any device or Android version mentioned
Only report problems actually stated in the reviews. Do not speculate about causes.
${bad.reviews.map((r, i) => `[${i}] (${r.rating}★) ${r.body}`).join('\n')}
`;
Keeping the verbatim quotes is what makes this usable in a bug report. A summary saying “users report login issues” gets sent back with “which users?” — quotes and counts don’t.
If you’d rather have an agent do this interactively, the endpoint is available as an MCP tool for Claude, Cursor, and Windsurf.
Play Store vs. Play Console
If it’s your own app, the Play Console gives you reviews plus private data you can’t get any other way: crash rates, ANRs, install/uninstall metrics, and the ability to reply.
The public review API complements it rather than replacing it:
- Competitor apps. Play Console only covers apps you own.
- Cross-app category analysis. Pull reviews across every app in a category.
- A pipeline you control. Console exports are awkward to automate against.
- Pre-launch research. Understand complaints about incumbents before you build.
Use both. Console for your own app’s private telemetry, the public API for everything comparative.
Practical notes
- Public reviews only. This reads what any signed-out browser sees on a listing. It cannot post replies — that’s Play Console.
- Reviewer names are personal data. Public display names are still personal data under GDPR. Drop or hash the field if you don’t need it.
uniqueOnlyis worth setting. The Play Store listing can surface the same review across pages during pagination; this drops the duplicates before you’re billed for reasoning about them.- Reviews get edited. Users update reviews after a fix ships, which changes both rating and text. Re-pull rather than assuming your stored copy is current.
Next steps
- Google Play Reviews API — endpoint and pricing
- Google Play Reviews API alternatives — how the options compare
- Export Google Play reviews to CSV — getting this into a spreadsheet
- App Store & Play Store MCP — connect it to an AI agent