# Google Play Scraper — Reviews, Ratings & App Details (`tortuga/google-play-scraper`) Actor

Scrape Google Play reviews (full history, developer replies, 73 languages), app details with exact installs, rating histogram and data safety, and keyword search results.

- **URL**: https://apify.com/tortuga/google-play-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** Developer tools, E-commerce, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.12 / 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.

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

## Google Play Scraper — Reviews, Ratings & App Details

Scrape **Google Play reviews** for any Android app: the **full review history** in any of the 73 review languages, newest first, most relevant first or highest rating first, with an optional star filter. Every review comes with its rating, text, date, the reviewer's display name and avatar, **thumbs-up count**, **app version** and the **developer's reply** with its date.

The same actor also collects **app details** (exact installs, rating, rating count, **rating histogram**, written-review count, price, in-app purchases, ads, category, content rating, version, update date, what's new, **full data safety section**, chart rank, developer contacts, description, screenshots, video) and **Google Play search results** for any keyword. No login, no Google account, no API key, no browser.

The field names match our App Store Scraper (`tortuga/appstore-scraper`), so you can put Android and iOS reviews of the same app into one table.

### What can this Google Play scraper do?

- **Reviews, full history**: up to 4,000 reviews per request, as deep as the app's review list goes (tested: all 15,045 English reviews of Loop Habit Tracker back to 2016 in 4 requests; 60,000 of WhatsApp's newest English reviews in 15 requests).
- **Developer replies**: reply text and reply date next to each review.
- **73 languages**: Google Play keeps a separate review list per language. List the languages you want (`en`, `de`, `pt`, `ja`, `zh-TW` ...) or enter `all`.
- **Sort and filter**: newest, most relevant or highest rating first; only 1-star (or any set of stars) reviews.
- **Only new reviews**: set **Only reviews newer than** and the run stops at the first older review, so daily monitoring costs a few cents.
- **App details**: one row per app, country and language, including the exact install count, the 1-5 star histogram, the number of written reviews and Google's data safety declarations (data shared, data collected, security practices).
- **Keyword search**: the 30 apps Google Play shows for a keyword, per country and language, in store order.

### How to scrape Google Play reviews

1. Paste Google Play links (`https://play.google.com/store/apps/details?id=com.whatsapp`) or package names (`com.whatsapp`) into **Apps**.
2. Choose the **Languages** (for example `en`, `es`, `de`) or enter `all`.
3. Pick the **Review order**, optionally **Star ratings**, and set **Max items** to cap the cost.
4. Click **Start**. Download the reviews as JSON, CSV or Excel from the **Dataset** tab, or read them through the API.

#### Input example: reviews

```json
{
  "mode": "reviews",
  "apps": ["https://play.google.com/store/apps/details?id=com.spotify.music", "org.isoron.uhabits"],
  "languages": ["en", "de"],
  "sort": "newest",
  "starRatings": ["1", "2"],
  "maxReviewsPerLanguage": 1000,
  "maxItems": 8000
}
```

#### Output example: review

```json
{
  "type": "review",
  "appId": "com.spotify.music",
  "appName": "Spotify: Music and Podcasts",
  "language": "en",
  "country": "us",
  "reviewId": "ad1d9be2-0959-4e98-8229-ee3ee4d9df0e",
  "rating": 3,
  "title": null,
  "text": "I love music, it's the only thing that keeps me focused on things like drawing, love Spotify ...",
  "date": "2026-04-10T00:04:24Z",
  "userName": "Angel Mead (Willow)",
  "userImage": "https://play-lh.googleusercontent.com/a-/ALV-UjVrcmkir02OyHXAex-UOTZ-uWCYMB1hqqyK7OFMFN37PtvbEX22",
  "appVersion": "9.1.36.1948",
  "helpfulVotes": 58,
  "developerResponse": "Hi. We'll pass this on to the ads team. Furthermore, have you tried a quick reinstall? ...",
  "developerResponseDate": "2026-04-10T11:17:33Z",
  "criteriaRatings": [{ "criterion": "vaf_app_quality_ads_frequency", "rating": 1 }],
  "reviewUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&reviewId=ad1d9be2-0959-4e98-8229-ee3ee4d9df0e",
  "appUrl": "https://play.google.com/store/apps/details?id=com.spotify.music"
}
```

#### Review fields

| Field | Description |
|---|---|
| `appId`, `appName` | Package name and app title |
| `language`, `country` | Review language list the review comes from, and the country used for the request |
| `reviewId` | Google's review ID (unique, stable) |
| `rating` | Stars, 1-5 |
| `title`, `text` | Review text; a few old reviews also have a title |
| `date` | When the review was posted or last edited (UTC, ISO 8601) |
| `userName`, `userImage` | The display name and avatar Google Play shows next to the review |
| `appVersion` | App version the review was written for (empty when the reviewer's device did not report it) |
| `helpfulVotes` | How many people marked the review as helpful (thumbs up) |
| `developerResponse`, `developerResponseDate` | The developer's public reply, when it exists |
| `criteriaRatings` | Google's per-topic answers attached to some reviews (e.g. ads, performance), as raw codes |
| `reviewUrl`, `appUrl` | Link to the review and to the app page |

### How to get Google Play app details

Set **Mode** to `details`. You get one row per app, country and language. Price, currency, rating and histogram are per country; texts follow the language.

```json
{ "mode": "details", "apps": ["com.whatsapp", "com.mojang.minecraftpe"], "countries": ["us", "in"], "languages": ["en"] }
```

| Field | Description |
|---|---|
| `appId`, `name`, `subtitle`, `url` | Package name, title, short description, store link |
| `developer`, `developerId`, `developerUrl` | Developer as shown on the store |
| `developerEmail`, `developerWebsite`, `developerLegalName`, `developerCountry` | Contact data from "About the developer" (country only, no street address) |
| `price`, `currency`, `priceFormatted`, `isFree` | Price in that country's currency |
| `hasInAppPurchases`, `inAppPurchaseRange`, `containsAds` | Monetisation flags, e.g. `"$0.99 - $49.99 per item"` |
| `rating`, `ratingCount`, `reviewCount`, `ratingHistogram` | Average stars, number of ratings, number of written reviews, ratings per star (`{"1": ..., "5": ...}`) |
| `installs`, `minInstalls`, `realInstalls` | Install bucket (`"10,000,000,000+"`), its lower bound, and Google's exact install count |
| `primaryGenre`, `genreId`, `genres` | Category |
| `contentRating`, `contentRatingDescription`, `interactiveElements` | Age rating and notes such as "In-Game Purchases" |
| `chartPosition`, `chartName` | Top-chart rank, e.g. `1` in `"top free communication"`, when the app is charting |
| `version`, `currentVersionReleaseDate`, `releaseNotes`, `releaseDate` | Current version (empty when it "varies with device"), last update, what's new, first release |
| `minAndroidVersion`, `minSdk` | Minimum Android version |
| `description`, `descriptionHtml` | Full store description as text and as HTML |
| `dataSafety` | `summary` lines plus `sharedData`, `collectedData` (category, data types, optional flag, purposes) and `securityPractices` |
| `privacyPolicyUrl`, `iconUrl`, `headerImageUrl`, `screenshots`, `videoUrl` | Links and images |

Output excerpt:

```json
{
  "type": "app",
  "appId": "com.whatsapp",
  "country": "us",
  "language": "en",
  "name": "WhatsApp Messenger",
  "developer": "WhatsApp LLC",
  "developerLegalName": "Meta Platforms, Inc.",
  "isFree": true,
  "inAppPurchaseRange": "$0.40 - $199.99 per item",
  "containsAds": false,
  "rating": 4.6176,
  "ratingCount": 245284017,
  "reviewCount": 1952357,
  "ratingHistogram": { "1": 10912733, "2": 3464798, "3": 8925609, "4": 21905948, "5": 200074850 },
  "installs": "10,000,000,000+",
  "realInstalls": 12386768041,
  "primaryGenre": "Communication",
  "chartPosition": 1,
  "chartName": "top free communication",
  "currentVersionReleaseDate": "2026-10-03T02:15:46Z",
  "dataSafety": {
    "summary": ["No data shared with third parties", "This app may collect these data types: Location, Personal info and 5 others", "Data is encrypted in transit", "You can request that data be deleted"],
    "sharedData": [],
    "collectedData": [{ "category": "Contacts", "types": [{ "type": "Contacts", "optional": true, "purposes": "App functionality, Fraud prevention, security, and compliance" }] }],
    "securityPractices": [{ "practice": "Data is encrypted in transit", "description": "Your data is transferred over a secure connection" }]
  }
}
```

### How to scrape Google Play search results

Set **Mode** to `search` and add keywords. Each keyword returns the apps Google Play shows (up to 30) per country and language, in store order, with `searchTerm`, `position`, `appId`, `name`, `developer`, `rating`, `installs`, `price`, `priceFormatted`, `primaryGenre`, `contentRating`, `description`, `iconUrl`, `screenshots` and `url`. When the keyword is an app's name, that app comes first. Feed the package names into `details` mode when you need histograms and data safety.

```json
{ "mode": "search", "searchTerms": ["habit tracker", "budget app"], "countries": ["us", "gb"], "languages": ["en"], "maxItems": 200 }
```

### Monitor changes on a schedule

Turn on **Only new or changed items** and save the input as a task with a Schedule (Apify Console: **Saved tasks** -> **Schedule**, for example daily). Each run remembers what earlier runs of the same input returned and outputs only what is new: reviews not seen before in `reviews` mode (read newest first, starting with a small page and stopping where the previous run's reviews begin), apps whose price or version changed in `details` mode, and apps that are new in the results or changed price in `search` mode. Unchanged items are skipped before they are saved, so they are not charged. **Only reviews newer than** also takes a period such as `3 days` (counted back from each run; Google Play shows reviews about a day late, so use at least `2 days` daily). Monitored items carry `changeType` (`new` or `changed`) and, for changed apps, `previousValues` with the old values of the fields that changed (e.g. `{"price": 6.99}`); use **Monitoring key** to keep several schedules on the same input apart.

### How many Google Play reviews can I get per app?

All the written reviews Google keeps for the app in each language. The actor follows the same continuation token the Play website uses, 4,000 reviews per request, with no fixed cap. Two things to know:

- **Reviews are grouped by language, not country.** English reviews are the same whether you ask with `us`, `gb` or `in`. To get more reviews, add languages (or enter `all`). Regional variants share one list (`pt-BR` = `pt`, `en-GB` = `en`).
- **Reviews show up about a day late.** Google Play publishes a review roughly 24 hours after it is written, so the newest review you get is about a day old (the store website shows the same).
- **Most ratings have no text.** WhatsApp has 245 million ratings but about 2 million written reviews; only written reviews can be listed. The details row tells you both numbers (`ratingCount`, `reviewCount`).

### Can I get the developer's replies to reviews?

Yes. `developerResponse` holds the reply text and `developerResponseDate` its date. Reviews without a reply have `null`.

### Does it work without login or a Google account?

Yes. It reads the public data the Google Play website shows to every visitor, through the same web endpoints the site itself calls. No cookies, no account, no API key. The default Apify datacenter proxy works; each request goes out from a new IP to spread Google's per-IP limits.

### How much does it cost to scrape Google Play?

You pay per result: **$0.12 per 1,000 reviews**, **$1.00 per 1,000 app detail rows**, **$0.50 per 1,000 search results**, plus Apify platform usage, which was about $0.005 per 1,000 reviews in our tests (20,000 reviews: $0.10). There is no start fee. **Max items** caps every run, and **Only reviews newer than** lets you pay only for new reviews when you monitor an app.

### Integrations and API

Run it from Zapier, Make, n8n or Google Sheets, or call it from Python or Node with the Apify client. Schedule it daily with **Only reviews newer than** to track new reviews and developer replies. See the **API** tab for ready-made snippets.

### Is it legal to scrape Google Play?

This actor collects only data that Google Play shows publicly to every visitor without an account: app pages, search results, and reviews with the display name and avatar each reviewer publishes on the store. It does not log in and does not collect anything private; for developers it exports the contact data published in "About the developer" and only the country of their address. You are responsible for how you use the data, including data-protection law (for example GDPR) when you process reviewer names, and for respecting Google's terms.

### Support

Found a bug or need a field? Open an issue in the **Issues** tab; it is usually answered within a day.

# Actor input Schema

## `mode` (type: `string`):

What to scrape. `reviews`: user reviews of each app in **Apps**, per language. `details`: one app item per app, country and language (installs, rating histogram, price, data safety and more). `search`: Google Play search results for each keyword in **Search terms**.

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

Google Play app URLs or package names, one per line: `https://play.google.com/store/apps/details?id=com.whatsapp` or `com.whatsapp`. `hl`/`gl` in a URL are used when **Languages**/**Countries** are empty. Used by the `reviews` and `details` modes.

## `searchTerms` (type: `array`):

Keywords for the `search` mode, one per line, e.g. `habit tracker`. Google Play returns up to 30 apps per keyword, country and language, in store order.

## `languages` (type: `array`):

Language codes, e.g. `en`, `de`, `pt`, `ja`, `zh-TW`. Google Play groups reviews by language, so each language is a separate review history; regional variants share one (`pt-BR` = `pt`). Enter `all` for all 73 review languages. Also sets the language of app details and search results. Default: `en`.

## `countries` (type: `array`):

Two-letter country codes, e.g. `us`, `gb`, `de`, `in`, `br`. Each country has its own prices, availability and search results. Reviews do not change by country (only by language), so `reviews` mode uses the first one. Default: `us`.

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

`newest` (default), `mostRelevant` (Google's default order on the store page) or `rating` (highest rating first). All orders reach the full review history.

## `starRatings` (type: `array`):

Only reviews with these star ratings, e.g. `1` and `2` for complaints. Each star is fetched separately, so results come grouped by star. Leave empty for all reviews.

## `maxReviewsPerLanguage` (type: `integer`):

Cap on reviews collected for each app in each language (and star rating, if set) in `reviews` mode. Leave empty to use only **Max items**.

## `reviewsNewerThan` (type: `string`):

A date (YYYY-MM-DD) or a period such as '3 days' or '2 weeks' (counted back from the start of each run, so a schedule always gets the latest reviews). With **Review order** = Newest, the scraper stops at the first older review, so you pay only for new ones. This is the date input for daily or weekly monitoring; combine it with **Only new or changed items**. Google Play shows new reviews about a day late, so use at least '2 days' on a daily schedule.

## `maxItems` (type: `integer`):

Overall cap on dataset items for the run (reviews, apps or search results). Keeps cost predictable.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this input returned and output only new items: in `reviews` mode only reviews not output before (read newest first, starting with a small page and stopping where the previous run's reviews begin; forces **Review order** = Newest); in `details` mode apps whose price or version changed per country and language; in `search` mode apps that are new in the results or whose price changed. Use with a Schedule; unchanged items are not charged.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input. Only used with **Only new or changed items**.

## `proxyConfiguration` (type: `object`):

Apify datacenter proxy (default) is enough: Google Play's web endpoints are public. Each request uses a new IP to spread Google's per-IP rate limits.

## Actor input object example

```json
{
  "mode": "reviews",
  "apps": [
    "https://play.google.com/store/apps/details?id=com.whatsapp"
  ],
  "searchTerms": [
    "habit tracker"
  ],
  "languages": [
    "en"
  ],
  "countries": [
    "us"
  ],
  "sort": "newest",
  "starRatings": [],
  "maxItems": 100,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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": [
        "https://play.google.com/store/apps/details?id=com.whatsapp"
    ],
    "searchTerms": [
        "habit tracker"
    ],
    "languages": [
        "en"
    ],
    "countries": [
        "us"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/google-play-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": ["https://play.google.com/store/apps/details?id=com.whatsapp"],
    "searchTerms": ["habit tracker"],
    "languages": ["en"],
    "countries": ["us"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/google-play-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": [
    "https://play.google.com/store/apps/details?id=com.whatsapp"
  ],
  "searchTerms": [
    "habit tracker"
  ],
  "languages": [
    "en"
  ],
  "countries": [
    "us"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call tortuga/google-play-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tortuga/google-play-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/2jD2YhNKAYA5XenYs/builds/eN5aeQSbOyFfZVzMV/openapi.json
