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:

ParameterWhat it does
appIdOrUrlPackage name (com.spotify.music) or a Play Store URL
sortBynewest, mostRelevant, or rating
ratingReturn a single star rating
ratingFilterArray of star ratings to include, e.g. [1, 2]
keywordsOnly reviews matching these terms
appVersionArray of versions to include
recentDaysOnly reviews from the last N days
endDateLatest review date, YYYY-MM-DD
languageArray of language codes
deviceTypemobile, tablet, or chromebook
uniqueOnlyDrop duplicate reviews
reviewsPerPageUp 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.
  • uniqueOnly is 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