# Google Play Store Reviews Scraper (`lightmoon/google-play-store-reviews-scraper`) Actor

Export Google Play reviews for any app to CSV, Excel or JSON: stars, text, date, reviewer, app version, helpful votes and the developer's reply, plus the app's rating and installs on every row. Filter by stars and date, pick language and country. $0.09 per 1,000 reviews, empty apps free.

- **URL**: https://apify.com/lightmoon/google-play-store-reviews-scraper.md
- **Developed by:** [Stable](https://apify.com/lightmoon) (community)
- **Categories:** Developer tools, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.09 / 1,000 reviews

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

> **Unofficial.** This Actor is not affiliated with, endorsed by or sponsored by Google. It reads only what the Google Play website shows to any visitor, does not log in and does not bypass any access control. All product names and trademarks belong to their respective owners.

### One row of output

Paste package ids or Play Store links; get one flat table of reviews. This is a
real row from a real run, not a mock-up:

```json
{
  "store": "google-play",
  "appId": "com.spotify.music",
  "appTitle": "Spotify: Music and Podcasts",
  "developer": "Spotify AB",
  "reviewId": "3364f4ff-5163-4646-a9d1-f387649a1c28",
  "score": 3,
  "text": "it's good but the constant unsuitable ads and the limited skips drive me mad",
  "reviewedAt": "2026-09-18T18:32:00Z",
  "authorName": "George",
  "authorId": "106136528358480644221",
  "thumbsUp": 1,
  "appVersion": "9.1.80.2221",
  "replyText": "Hi. Thanks for sharing this. We hear you and we'll pass this along to the Ads team. If you're looking for an ad-free experience and greater flexibility, consider exploring our Premium options at spotify.com/premium.",
  "repliedAt": "2026-09-18T18:42:36Z",
  "replyFrom": "Spotify AB",
  "language": "en",
  "country": "us",
  "reviewUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&reviewId=3364f4ff-5163-4646-a9d1-f387649a1c28",
  "appUrl": "https://play.google.com/store/apps/details?id=com.spotify.music",
  "appRating": 4.346,
  "appRatingCount": 36331001,
  "installs": "1B+",
  "category": "MUSIC_AND_AUDIO"
}
```

**The app's own figures ride on every row** — name, developer, average rating,
rating count and install band — so a sheet of reviews from fifty apps sorts and
pivots by app without a second export.

### What you get

**26 columns per row**, all flat — one CSV, no nested cells to unpick.

| | |
|---|---|
| The review | `reviewId`, `score` (1–5), `text`, `reviewedAt` (UTC, to the second), `thumbsUp`, `appVersion`, `criteria` (aspect ratings, when the app asks for them), `reviewUrl` |
| Who wrote it | `authorName`, `authorId` |
| The developer's answer | `replyText`, `repliedAt`, `replyFrom` — empty when there is none |
| The app | `appId`, `appTitle`, `developer`, `category`, `appRating`, `appRatingCount`, `installs`, `appUrl` |
| Where it came from | `store`, `language`, `country`, `title` (reserved for stores that have review titles), `scrapedAt` |

Export to CSV, Excel, JSON or read it from the API. No Google account, no API
key, no cookies.

### Three things this does that are worth knowing

**1. Star filters are applied by the store, not by us.** Ask for 1-star reviews
and the run reads *only* 1-star reviews — a thousand complaints cost a thousand
rows, not five thousand rows with four thousand thrown away.

**2. A daily watch costs only the new reviews.** With `reviewedAfter` set and
the default newest-first order, the run stops reading the moment it reaches your
date. Schedule it, and a quiet day for an app is a few cents.

**3. Nothing is guessed, nothing empty is billed.** A field Google Play did not
publish comes back empty — never `"N/A"`. An app id the store has nothing for, a
review outside your dates, and a duplicate the store handed back twice are all
free, and the run summary says how many of each there were.

### Input

```json
{
  "apps": ["com.spotify.music", "https://play.google.com/store/apps/details?id=notion.id"],
  "maxReviewsPerApp": 500,
  "sort": "newest",
  "scores": ["1", "2"],
  "reviewedAfter": "2026-09-01",
  "language": "en",
  "country": "us",
  "includeAppDetails": true
}
```

Apps are accepted as a package id (`com.spotify.music`) or any Play Store link
carrying `?id=`. Up to 500 apps per run. Order is newest first, most relevant
(Google Play's own ranking) or by rating. One language per run — run again with
`language: "de"` for the German reviews.

### Price

**$0.09 per 1,000 reviews** on the Store card. A typical run — 5 apps, 500
reviews each — is 2,500 rows for **$0.23**.

Everything on the row is included at that price: the developer's reply, the
aspect ratings and the app's own figures. There is no per-run start fee and no
second event, so an app with nothing to return costs $0.

**Free plan:** runs work in full, with no feature held back. The $5 of monthly
platform credit on a free Apify plan covers roughly 50,000 reviews.

### Limits, stated plainly

- **One language and one country per run.** Google Play serves reviews per
  storefront; the actor reads the one you name. For "all languages" run once
  per language you care about — the rows carry `language` so they merge cleanly.
- **The date filter stops early only in newest-first order.** Sorted by
  relevance or rating, dates are not monotonic, so every page is read and the
  filter is applied row by row. Reviews it removes are still free.
- **Reviews arrive 100 at a time** with a pause between pages. A run for 10,000
  reviews takes about two minutes.
- **`installs` is Google's band** (`10M+`), not an exact number — the store does
  not publish one.
- This reads what Google Play shows to a visitor. Reviews the reviewer deleted
  or the store hid are not visible and are not returned.

### Typical uses

- Pull every 1- and 2-star review of your own app since the last release and
  group by `appVersion` to see whether the update caused the complaints.
- Compare five competing apps: reviews with `appRating`, `installs` and
  `developer` on every row, ready for a pivot table.
- Measure a developer's support: `repliedAt − reviewedAt` is the response time,
  and the share of rows with `replyText` is the response rate.
- Feed a sentiment or topic model with clean text and a star label per row.
- Schedule a daily run with `reviewedAfter` and get only what is new.

### FAQ

#### How much does it cost to scrape 10,000 reviews?

$0.90 at the Store card rate, plus nothing else — no start fee, no per-page
charge. A run that finds fewer reviews than you asked for charges only for the
rows it stored.

#### Can I get all reviews of an app?

Set `maxReviewsPerApp` to what you need, up to 50,000 in one run. Newest first
walks the store's own list as far as it goes; for a very large app that is
hundreds of thousands of reviews, and you can run again with a higher ceiling
or a date window.

#### Does it return the developer's reply?

Yes — `replyText`, `repliedAt` and the name it was posted under, on the same
row as the review, and empty when the developer never answered.

#### What are `criteria`?

Some apps ask reviewers to rate named aspects — stability, ads, usability — in
addition to the star rating. When a review carries them they are returned as
`name=stars` pairs in one cell; most reviews have none.

#### Why did an app come back with no rows?

Either the package id has no listing on the store, or the store has no reviews
for it in the language and country you set. The run summary names the app
either way, and nothing was charged for it.

# Actor input Schema

## `apps` (type: `array`):

One per line. A package id (com.spotify.music) or the app's Google Play link (https://play.google.com/store/apps/details?id=com.spotify.music) both work. The id is the part after id= in any Play Store URL.

## `maxReviewsPerApp` (type: `integer`):

How many reviews to return for each app, in the chosen order. Reviews arrive 100 at a time.

## `sort` (type: `string`):

Newest first is the default and the one that works with the date filter. Most relevant is Google Play's own ranking; By rating groups by stars.

## `scores` (type: `array`):

Only reviews with these star ratings. Leave empty for all. Filtered by the store itself, so asking for 1-star reviews reads only 1-star reviews.

## `reviewedAfter` (type: `string`):

Skip reviews older than this date. With Newest first the run stops reading as soon as it reaches the date, so a daily watch of new reviews costs only the new ones.

## `language` (type: `string`):

Reviews written in this language, as a two-letter code: en, de, es, pt, fr, ja, ko... One language per run; run again for another.

## `country` (type: `string`):

The Play Store country, as a two-letter code: us, gb, de, in, br...

## `includeAppDetails` (type: `boolean`):

Adds the app's name, developer, category, average rating, rating count and install band to each review. Reads the app's store page once per app. Turn off for the smallest possible rows.

## Actor input object example

```json
{
  "apps": [
    "com.spotify.music"
  ],
  "maxReviewsPerApp": 100,
  "sort": "newest",
  "language": "en",
  "country": "us",
  "includeAppDetails": true
}
```

# Actor output Schema

## `reviews` (type: `string`):

No description

## `replies` (type: `string`):

No description

## `all` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "apps": [
        "com.spotify.music"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lightmoon/google-play-store-reviews-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "apps": ["com.spotify.music"] }

# Run the Actor and wait for it to finish
run = client.actor("lightmoon/google-play-store-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "apps": [
    "com.spotify.music"
  ]
}' |
apify call lightmoon/google-play-store-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lightmoon/google-play-store-reviews-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/C3bONFgRsFBoAdzaV/builds/PrbUdFFjWB9oedcKt/openapi.json
