# Google Play Scraper: Apps, Reviews, Charts & Exact Installs (`foxlabs/google-play-scraper`) Actor

Google Play apps from app IDs, top charts, search, similar apps or a developer's catalogue: exact install count, installs gained between runs, ratings histogram, developer email, phone and address, permissions, data safety. Reviews with replies and star, device and date filters.

- **URL**: https://apify.com/foxlabs/google-play-scraper.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Marketing, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 apps

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: Apps, Reviews, Charts & Exact Installs

Get Google Play apps from **app IDs, top charts, search, "Similar apps" or a developer's catalogue**, with **Google's exact install count, installs gained since the last run, rating and 1–5 star histogram, price and in-app purchase range, version and release notes, developer email, phone, website and address, permissions and data-safety labels** wherever Google Play shows them for the app. Switch on reviews and set `maxReviewsPerApp` to 0 to get **every review Google Play shows for an app** (read 1,000 per request; 200 per app by default), with developer replies, app version and star, device and date filters.

- 📈 **Exact installs, and what changed since your last run:** Google Play pages carry the exact install counter behind the "100M+" label. Each run compares every app with your previous run: `installsGained`, `installsPerDay`, rating, version and chart-rank changes. Google moves the counter in steps, within a day for most big apps and less often for smaller ones (measured below), so read install growth over a week.
- 🏆 **Top charts at scale:** Top free, Top grossing and Top paid for any of 53 categories in any of 52 countries (up to 20 countries per run), up to 200 apps per chart, in one run. Google Play has no chart for some pairs: 14 of the 159 chart and category pairs in the US were empty, all Top paid (run `VfmPT2vO7RGO5ejr3`).
- 📇 **Developer contacts:** email, website, phone, address and legal name as Google Play publishes them.
- ⭐ **Reviews:** newest, most relevant or by rating; star, device and period filters; replies with dates.

No Google account, no API key, no browser.

### Quick start (API)

```bash
curl -X POST "https://api.apify.com/v2/acts/foxlabs~google-play-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"appIds": ["com.whatsapp", "com.duolingo"]}'
```

### What you get

One dataset, three row types (`rowType`): `app`, `review` and `status` (free rows that say why something could not be delivered).

**App rows**

| Group | Fields |
|---|---|
| App | `appId`, `title`, `developer`, `genre`, `genreId`, `tags`, `summary`, `description`, `icon`, `headerImage`, `screenshots`, `videoUrl`, `url` |
| Installs | `realInstalls` (Google's exact counter), `installs` (the label, e.g. "100,000,000+"), `minInstalls` |
| Changes since the last run | `installsGained`, `installsPerDay`, `ratingsGained`, `scoreChange`, `versionChanged`, `previousVersion`, `previousRank`, `hoursSinceSnapshot`, `trackingStatus` |
| Ratings | `score`, `ratings`, `reviewsCount`, `ratingHistogram` (ratings per star), `deviceRatings` (phone, tablet, watch, chromebook, xr; device types Google Play's data does not name come as `device6`, `device7`, `device8`) |
| Rank | `sourceType`, `source`, `rank` (position in the chart, search, similar list or catalogue), `chartType`, `chartCategory`, `chartBadge` ("#2 top grossing puzzle") |
| Price | `price`, `currency`, `priceText`, `free`, `inAppPurchases`, `iapRange`, `containsAds` |
| Versions | `version`, `updatedAt`, `releasedAt`, `recentChanges`, `minAndroidVersion` |
| Developer | `developerEmail`, `developerWebsite`, `developerPhone`, `developerAddress`, `developerLegalName`, `developerLegalEmail`, `developerId`, `developerUrl`, `privacyPolicyUrl` |
| Content & privacy | `contentRating`, `contentRatingDescription`, `interactiveElements`, `permissions` (grouped as on Google Play), `dataSafety` (the data-safety labels shown on the app page) |
| Run | `country`, `language`, `scrapedAt`, `error` |

**Review rows:** `reviewId`, `appId`, `appTitle`, `score`, `text`, `date`, `thumbsUp`, `appVersion`, `replyText`, `replyDate`, `userName`, `userImage`, `criteria`, `device`, `language`, `country`, `sort`, `reviewUrl`, `scrapedAt`, `error`.

**Status rows:** `sourceType`, `source`, `appId`, `country`, `language`, `error` (for example `not-found: Google Play has no app "com.example" in US …`), `scrapedAt`.

`appTitle` is filled when the run also reads app details (mode "App details + reviews"). `version` and `minAndroidVersion` are `null` when Google Play's data gives no single version (WhatsApp, for example). A number Google Play's data does not carry is `null`, never `0`; a `0` is Google's own value, such as `thumbsUp` of a review nobody marked helpful. `containsAds` and `inAppPurchases` are `false` when the app page shows no such label.

#### Sample output

A real app row (trimmed) from run `hjqW9KucL3qylkvbw` on 2026-09-30, Top grossing · All games · Germany:

```json
{
  "rowType": "app",
  "appId": "com.dreamgames.royalmatch",
  "title": "Royal Match",
  "developer": "Dream Games, Ltd.",
  "realInstalls": 373340703,
  "installs": "100,000,000+",
  "trackingStatus": "first-snapshot",
  "score": 4.461531,
  "ratings": 10826357,
  "ratingHistogram": { "1": 517472, "2": 192562, "3": 662578, "4": 1856772, "5": 7596869 },
  "sourceType": "chart",
  "source": "Top grossing · All games · DE",
  "rank": 3,
  "chartBadge": "#2 top grossing puzzle",
  "version": "38911",
  "updatedAt": "2026-09-25T15:43:39.000Z",
  "price": 0,
  "currency": "EUR",
  "inAppPurchases": true,
  "iapRange": "€0.99 - €99.99 if billed through Play",
  "developerEmail": "contact@dreamgames.com",
  "developerWebsite": "https://dreamgames.com",
  "developerPhone": "+90 537 721 27 21",
  "developerAddress": "13TH FLOOR, ONE ANGEL COURT, LONDON, EC2R 7HJ, United Kingdom",
  "developerLegalName": "DREAM GAMES PUBLISHING LIMITED",
  "contentRating": "USK: Ages 12+",
  "dataSafety": [{ "label": "This app may share these data types with third parties", "detail": "Location and Device or other IDs" }],
  "permissions": [{ "group": "Other", "permissions": ["view network connections", "full network access", "control vibration", "prevent device from sleeping", "Google Play license check"] }],
  "deviceRatings": { "phone": 4.46, "tablet": 4.58, "chromebook": 4.53 },
  "country": "de"
}
```

A review row from run `kn5zYXWMMI8q85R6E` on the same day (reviewer name and picture replaced here):

```json
{
  "rowType": "review",
  "reviewId": "bba5a811-2652-4fe8-be90-eaad904b12a3",
  "appId": "com.calm.android",
  "appTitle": "Calm - Sleep, Meditate, Relax",
  "score": 1,
  "text": "this is the absolute buggiest app I've ever used, absolutely insane that they charge money for it and have this many issues with basic stuff",
  "date": "2026-09-28T17:47:19.000Z",
  "thumbsUp": 0,
  "appVersion": "7.1.2",
  "replyText": "We’re sorry to hear you’ve been dealing with so many issues. …",
  "replyDate": "2026-09-28T20:49:00.000Z",
  "userName": "<reviewer's display name>",
  "device": "all",
  "reviewUrl": "https://play.google.com/store/apps/details?id=com.calm.android&reviewId=bba5a811-2652-4fe8-be90-eaad904b12a3"
}
```

### Input

| Input | What it does | Default |
|---|---|---|
| `mode` | `apps` (app rows), `reviews` (review rows only) or `appsAndReviews` | `apps` |
| `appIds` | Package names (`com.whatsapp`) or Google Play links | — |
| `chartTypes`, `chartCategories` | Top charts × categories. A chart alone reads All apps; a category alone reads Top free | none |
| `searchTerms` | Google Play search terms | none |
| `similarTo` | Apps whose "Similar apps" list to read (Google Play's separate "More by" list of the same developer is left out) | none |
| `developers` | Developer name as shown on Google Play, numeric developer ID, or developer page link | none |
| `maxAppsPerSource` | Apps per chart, search, similar list or developer. The Console form starts at 20 | 50 |
| `countries` | Google Play countries, up to 20 per run; every source runs once per country | `["us"]` |
| `language` | Language of texts; reviews come from this language's list | `en` |
| `trackChanges`, `trackingStoreName` | Compare with the previous run; snapshot store name in your account | on, `google-play-tracker` |
| `maxReviewsPerApp` | Reviews per app; `0` = every review Google Play shows for the filter. The Console form starts at 100 | 200 |
| `reviewsSort` | `newest`, `relevant`, `rating` | `newest` |
| `reviewsStars` | `all` or `1`–`5` | `all` |
| `reviewsDevice` | `all`, `phone`, `tablet`, `watch`, `chromebook`, `xr` | `all` |
| `reviewsPeriod`, `reviewsSince` | `last_24_hours` … `last_365_days`, or `custom` with a `YYYY-MM-DD` date. A period reads newest first and stops at its start | `all` |
| `includeReviewerNames` | Reviewer display name and picture | on |
| `proxyConfiguration`, `autoProxyFallback` | Proxy is not needed; the Actor moves to Apify proxy by itself if Google Play limits it | off / on |

Fill in any combination of sources; the Actor runs all of them. Input that cannot work (no source, an unknown country or chart category, a review period without a valid date) stops the run at once with the reason. In mode `apps` the review settings are not used. A single bad entry (a line that is not a package name, an app Google Play does not have) becomes a free status row and the rest of the run continues.

**Which rows repeat.** Top-chart and search rows always keep their own row, because their rank is part of the data: an app in two charts is two rows. App IDs, similar-apps lists and developer catalogues deliver an app once per country and skip apps an earlier source in the run already delivered (counted as `duplicates` in `SOURCE_REPORT`). With several countries an app gets one row per country, with that country's price and currency.

### Example inputs (copy & paste)

Top grossing games in Türkiye and Germany, 200 each:

```json
{ "chartTypes": ["topgrossing"], "chartCategories": ["GAME"], "countries": ["tr", "de"], "maxAppsPerSource": 200 }
```

Competitor watch-list, scheduled daily for installs gained, rating and version changes:

```json
{ "appIds": ["com.duolingo", "com.calm.android", "com.spotify.music"], "trackChanges": true, "trackingStoreName": "my-competitors" }
```

Keyword ranks (ASO) in two markets, scheduled daily:

```json
{ "searchTerms": ["habit tracker", "meditation"], "countries": ["us", "gb"], "trackChanges": true, "trackingStoreName": "my-aso" }
```

Developer contacts for B2B outreach:

```json
{ "chartTypes": ["topselling_free"], "chartCategories": ["BUSINESS"], "countries": ["de"], "maxAppsPerSource": 200, "trackChanges": false }
```

Every review of an app:

```json
{ "mode": "reviews", "appIds": ["com.lafitness.lafitness"], "maxReviewsPerApp": 0 }
```

One-star reviews of the last 30 days:

```json
{ "mode": "reviews", "appIds": ["com.calm.android"], "reviewsStars": "1", "reviewsPeriod": "last_30_days", "maxReviewsPerApp": 0 }
```

### How change tracking works

With `trackChanges` on, the Actor keeps a small snapshot of each app in the named key-value store `trackingStoreName` in your Apify account: the install counter (one worldwide figure), the ratings count, score, version and update time per country (Google's rating differs by country), and the app's rank in each chart and search term. The next run compares every app with its snapshot and fills:

- `installsGained`, `ratingsGained`, `scoreChange`, `versionChanged` and `previousVersion`, and `previousRank` on chart and search rows;
- `installsPerDay` (`installsGained` per 24 hours since the snapshot), only when the snapshots are at least 20 hours apart (`trackingStatus: "compared"`). A shorter gap gives `compared-under-20h` and no per-day figure.

The first run of an app is `first-snapshot`. Give each schedule its own `trackingStoreName`: runs that share a store compare with each other's snapshots, so a test run between two scheduled runs shortens the next comparison.

The tracking fields of a real row, 20.1 hours after the first run (run `U3SZ9oSKb8N3GQ7bg`):

```json
{
  "appId": "com.whatsapp",
  "source": "Top free · All apps · US",
  "rank": 5,
  "previousRank": 5,
  "realInstalls": 12355354655,
  "trackingStatus": "compared",
  "hoursSinceSnapshot": 20.1,
  "installsGained": 2838195,
  "installsPerDay": 3389578,
  "ratingsGained": 74658,
  "scoreChange": 0.0005
}
```

#### How often Google moves the install counter

`realInstalls` is the exact install counter the app page carries behind the "100M+" label. Google moves it in steps, not continuously, and not at the same time or as often for every app. We read the counters of two groups of apps 9 times between 2026-09-30 20:08 and 2026-10-01 20:10 UTC:

| Apps | First run | ~20 hours later | 24 hours later |
|---|---|---|---|
| Top free · All apps · US and 19 app IDs | `eJqK7r5Uk9uvnBl7J` | `U3SZ9oSKb8N3GQ7bg`: 161 of 209 had a new count (77%) | `jGshD5o3homKvWDHx`: 151 of 186 (81%) |
| Top grossing · All games · Türkiye and Germany | `hjqW9KucL3qylkvbw` | `T2biIv3r0YhUoRAgQ`: 138 of 281 (49%) | `VnxdUcfpBJoQwcSlW`: 159 of 279 (57%) |

The 24-hour column counts the apps that were in both runs (the charts changed during the day). Between runs, no count went down. Bigger apps move more often: within 24 hours, 77 of the 84 US apps and 56 of the 65 games with 100 million or more installs had a new count, but only 74 of 102 US apps and 103 of 214 games with fewer installs.

What this means for your numbers:

- `installsGained` is exact: it is the difference of Google's own counter between your two runs.
- `installsGained: 0` usually means Google has not moved the counter since your last run, not that nobody installed the app: 46 of the 48 US apps whose count had not moved after 20.1 hours had more ratings than at the first run (5 to 17,934 more; one had 1 fewer, one has no ratings).
- Steps differ in size. WhatsApp's count rose by 2,838,195 in the 20.1 hours after the first run and by 5,764,502 in the 3.9 hours after that (runs `U3SZ9oSKb8N3GQ7bg`, `jGshD5o3homKvWDHx`). So one day's `installsGained` or `installsPerDay` can be 0, one step or two. For a steady rate per day, compare runs a week apart, or add up a week of daily runs.
- While Google moves a counter, two requests seconds apart can get the old and the new value (13,069 apart for one app in run `hjqW9KucL3qylkvbw`).

#### Ranks, versions and ratings

- The US Top free chart had the same 200 apps in the same order 20.1 hours after the first run, and was reshuffled 4 hours later: 11 of the 176 apps still in it kept their rank (run `jGshD5o3homKvWDHx`). The Top grossing games charts of Türkiye and Germany kept 41 and 27 of their 200 apps at the same rank over 20.3 hours.
- Google Play changes the version on the page and its update date separately: of 12 version changes in the US group between the first two runs (20.1 hours), 8 kept the earlier `updatedAt`.
- Ratings counts differ between requests seconds apart: in run `hjqW9KucL3qylkvbw` the same app had the same ratings count in the Turkish and the German chart for only 42 of 114 apps. Read `ratingsGained` over a day, not over minutes.

### Use cases

- **ASO and market intelligence:** category landscapes from the top charts, keyword ranks from search, install growth of the apps that matter to you.
- **Competitor tracking:** a daily schedule on a watch-list: installs gained, rating trend, new versions and their release notes.
- **Investors and app scouting:** which apps gain installs fastest in a category week over week, from Google's own counter rather than a model.
- **B2B sales to app developers:** SDK, ad network, user-acquisition and localization vendors get the developer's email, phone, website and address with the app's installs and category.
- **Product and customer feedback:** every review of your app or a competitor's, with replies and, where Google shows it, the app version the reviewer used, for dashboards or language models.
- **Privacy and compliance research:** permissions and data-safety labels across a category.

### Performance

One details request returns up to 40 apps (the Actor sends 40 per request and runs 4 at a time), a top chart comes in one request, and a review request returns up to 1,000 reviews. Measured on the Apify platform on 2026-09-30 and 2026-10-01 with the default settings (1 GB memory, no proxy):

| Run | Input | Rows | Requests | Time |
|---|---|---|---|---|
| `FK2rq7N3aVg1dJzfM` | The Console form's first input (3 app IDs) | 3 apps | 1 | 2 s |
| `Mpfgz4MaSYhncRvtS` | Top grossing · All games · Türkiye and Germany, 200 each | 400 apps | 12 | 19 s |
| `63eQ16al6XO48gCJL` | Top free · Business · Germany, 200 | 200 apps | 11 | 15 s |
| `jGshD5o3homKvWDHx` | 20 app IDs + Top free · All apps · US, 200, change tracking (third run) | 219 apps | 9 | 8 s |
| `M9f6djYo96mW9paEO` | 2 search terms × US and UK | 111 apps | 10 | 21 s |
| `NDgkbsOoeTXhumHDb` | Every review of LA Fitness | 5,456 reviews | 6 | 8 s |
| `MD7abR9UdVvN5VHSq` | 5,000 one-star reviews of Duolingo | 5,000 reviews | 5 | 10 s |

In the Actor's first 137 platform runs no request was rate-limited or blocked and none needed a proxy (315 requests, none failed). Of the 3,210 app details asked for in batches, 21 came back without data; the Actor asked for each of them again: the 18 apps that exist all came back, and the other 3 are not on Google Play (their page returns 404).

### Integrations

Use the dataset from the API (`run-sync-get-dataset-items`, the Apify JavaScript and Python clients), schedule the Actor in Apify Console for daily tracking, or connect it to Make, Zapier, n8n, Google Sheets or a webhook. The dataset has three views: Apps, Developer contacts and Reviews.

### Data quality

Measured on the Apify platform on 2026-09-30 and 2026-10-01:

- **Top grossing games, Türkiye and Germany (400 rows, `Mpfgz4MaSYhncRvtS`):** exact installs, rating histogram, developer email, address and permissions on all 400, phone on 399; chart ranks 1–200 without gaps in both countries; no row twice.
- **Top free Business apps, Germany (200 rows, `63eQ16al6XO48gCJL`):** developer email 200, address 200, website 188, phone 173; 192 have ratings (Google Play shows no rating for the other 8).
- **Every review of LA Fitness (`NDgkbsOoeTXhumHDb`, 2026-10-01):** 5,456 reviews from 2026-09-30 back to the app's release in June 2010, none twice. The all-devices list is the largest: on 2026-09-30 it held all 5,364 reviews of the phone list (`mMHGpsRzKfCI4iAWb`) and 91 more.
- **App version per review:** Google shows it for some reviews only. `appVersion` was filled for 90.1% of LA Fitness reviews (4,914 of 5,456, `NDgkbsOoeTXhumHDb`) and 74.7% of Duolingo's one-star reviews (3,734 of 5,000, `MD7abR9UdVvN5VHSq`). In Google's raw answer, checked on 1,000 Duolingo reviews, the reviews without `appVersion` carry no version at all.
- **Checked against Google Play:** 20 app rows from three runs, 18 fields each (title, installs, rating, version, price, developer email, phone, address, website, privacy policy…), were compared with the data of the live app page, read within 19 minutes: all 346 values that were filled agreed (installs, ratings count and rating within a small margin, since they move), and 14 were empty on both sides. The `reviewUrl` pages of 5 reviews all contained the review.
- **Format checks on 935 app rows:** every developer email, website and privacy-policy link well formed; no release day later than the update day; no HTML left in descriptions and release notes.

### Pricing

Each delivered app row is one `app` event, change tracking included. Each delivered review is one `review` event. Status rows are free: an app Google Play does not have, an empty chart or search, an invalid entry, a failed source. Each run also has Apify's small Actor-start event (per GB of memory). Current prices are on the Pricing tab. A run stops fetching when it reaches the maximum cost you set for it: with a $0.01 maximum, run `DmF9zWHLfAjBPdfQ2` delivered 24 apps, listed the source it did not reach as stopped and said so in its log.

### FAQ

**What is the difference between `installs` and `realInstalls`?** `installs` is the label Google Play shows ("100,000,000+"). `realInstalls` is the exact counter the page carries (373,340,703 for Royal Match on 2026-09-30). It is a worldwide figure: of the 114 apps in both the Turkish and the German top-grossing chart of run `hjqW9KucL3qylkvbw`, 113 had the same count in both countries (for the other one, two reads 3 seconds apart differed by 13,069), and all 113 such apps did in run `T2biIv3r0YhUoRAgQ` a day later.

**Why is `installsPerDay` empty?** It needs an earlier snapshot at least 20 hours old. The first run of an app is `first-snapshot`; a second run on the same day is `compared-under-20h`.

**Why is `installsGained` 0 for a popular app?** Google had not moved that app's counter since your last run (see "How often Google moves the install counter"). 20.1 hours after the first run, 48 of 209 US apps still had the same count, Amazon Shopping and Temu among them (run `U3SZ9oSKb8N3GQ7bg`).

**Why do I get fewer apps than `maxAppsPerSource`?** A top chart holds at most 200 apps. Google Play shows a few dozen results per search term: 21 for "habit tracker" and 30 for "meditation" in the US (run `M9f6djYo96mW9paEO`). A developer page's count can be higher than the apps it lists: Supercell's count was 9 and 7 apps were listed in the US, Finland, the UK and Germany (Japan: 8 and 7; run `MCP6n6whW0GROltbe`).

**Why does an app appear twice?** It is in two charts, in a chart and a search, in your app IDs and in a chart or search, or you chose two countries. See "Which rows repeat" above.

**Why is the number of reviews lower than `reviewsCount`?** `reviewsCount` is Google's review count for the chosen country (Royal Match had 22,282 in Türkiye and 27,893 in Germany in run `hjqW9KucL3qylkvbw`); the review list holds the reviews in the `language` you choose, so the two numbers count different reviews and need not match (LA Fitness in the US, in English, on 2026-10-01: 5,456 reviews listed, `reviewsCount` 5,628). Old reviews without text are listed with `text: null` (22 of the 5,456 LA Fitness reviews, all from 2014–2015), and old reviews without stars have `score: null` (1 of 5,456, from 2011).

**Is `date` the day the review was written?** It is the date Google Play gives for the review, and it can be later than the developer's reply: 1 of 28 replies in 50 Calm reviews was older than its review (runs `kn5zYXWMMI8q85R6E`, `j48E9VpoKz9N6CWrU`), most likely because the review was edited after the reply.

**Can I get reviews in several languages?** Run once per language; each language has its own review list.

**Are reviewer names included?** Yes, as Google Play shows them; set `includeReviewerNames` to false to leave names and pictures empty. Google account IDs are never included.

**Does it need a proxy?** No. Requests start without one; if Google Play limits them, the Actor moves to Apify datacenter proxy and then residential proxy by itself and notes it in the log and in `SOURCE_REPORT.requests` (tested with a proxy that refused connections: run `kyG5sLujiWemiK2BZ` switched to datacenter proxy and delivered the app).

### Troubleshooting

- **A status row says `not-found`:** Google Play has no app with that package name in that country (its page returns 404). Check the name; the app may be unpublished or not offered there.
- **A developer returns nothing:** use the name exactly as the developer page shows it, or paste the developer page link.
- **The run stopped early:** it reached the maximum cost you set for the run; the log says so.
- **Nothing was delivered:** every source that returned nothing leaves a free status row, and the `SOURCE_REPORT` record in the key-value store explains each source.

### Notes, limits & legal

- The Actor reads public Google Play pages and the data they load for any visitor, without a login. Keep `scrapedAt` with the data; ratings, ranks and prices change often.
- Developer contact details are published by Google Play; for individual developers they can be personal data, so use them in line with the privacy rules that apply to you.
- "Google Play" is a trademark of Google LLC. This Actor is not affiliated with or endorsed by Google.

### Support

Open an issue on the Issues tab with the run ID and the input.

### Changelog

#### 0.1 — 2026-10-01

First version. See CHANGELOG.md.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/google-play-scraper/changelog.md

# Actor input Schema

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

App details: one row per app (exact install count, ratings, developer contacts, permissions, chart rank…). Reviews: review rows for the same apps, no app rows. Both: app rows and their reviews.

## `appIds` (type: `array`):

Package names (com.whatsapp) or Google Play app links, one per line. Every source below can be combined with these in one run.

## `chartTypes` (type: `array`):

Google Play top charts to read, up to 200 apps each. Every chosen chart runs for every chosen category and country. Leave empty for none; choosing a category alone reads Top free.

## `chartCategories` (type: `array`):

Categories for the top charts. Leave empty for All apps when a chart is chosen.

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

Keywords as typed into Google Play search (fitness tracker, photo editor). Google Play shows a few dozen apps per term (21 to 30 in our tests); each row keeps its search rank.

## `similarTo` (type: `array`):

Package names or links. Returns the apps Google Play lists as "Similar apps" for each (Google Play's separate "More by" list of the same developer is left out).

## `developers` (type: `array`):

A developer's name as shown on Google Play (Google LLC), its numeric developer ID, or a developer page link. Returns the developer's apps.

## `maxAppsPerSource` (type: `integer`):

Top charts hold at most 200 apps and a search a few dozen. App IDs you list are always all read. The form starts at 20 for a quick first run; an API call without this field gets 50.

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

Google Play country (price, currency, availability, charts and search results follow it), up to 20 per run. Every source runs once per country, so an app can have one row per country.

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

Language of descriptions, release notes and labels. Reviews come from this language's review list.

## `trackChanges` (type: `boolean`):

Compares each app with the previous run and adds installsGained, installsPerDay, ratingsGained, scoreChange, versionChanged and previousRank. A small snapshot per app is kept in a named key-value store in your Apify account. Google moves the exact install counter in steps, at different times for different apps, so compare runs at least a day apart (README: "How change tracking works").

## `trackingStoreName` (type: `string`):

Named key-value store for the snapshots (letters, digits, hyphens). Use a different name to track a separate set of apps.

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

Stop after this many reviews for each app (Google Play pages 1,000 at a time). 0 means every review Google Play shows for the filter. The form starts at 100.

## `reviewsSort` (type: `string`):

Newest first, Google's "most relevant" order, or by rating. A review period needs newest order and uses it.

## `reviewsStars` (type: `string`):

Only reviews with this star rating (filtered by Google Play).

## `reviewsDevice` (type: `string`):

All devices, or only reviews written on one device type (Google Play keeps a review list per device type; its page shows the phone list).

## `reviewsPeriod` (type: `string`):

Only reviews written in this period. The Actor reads newest first and stops at the start of the period, so recent reviews of large apps come back fast.

## `reviewsSince` (type: `string`):

YYYY-MM-DD, used when Review period is "Since a date".

## `includeReviewerNames` (type: `boolean`):

Reviewer display name and profile picture as Google Play shows them. Turn off to leave both empty. Google account IDs are never included.

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

Not needed for most runs. Leave off and the Actor moves to Apify datacenter and then residential proxy by itself if Google Play limits it.

## `autoProxyFallback` (type: `boolean`):

On by default.

## Actor input object example

```json
{
  "mode": "apps",
  "appIds": [
    "com.whatsapp",
    "com.duolingo",
    "com.spotify.music"
  ],
  "chartTypes": [],
  "chartCategories": [],
  "maxAppsPerSource": 20,
  "countries": [
    "us"
  ],
  "language": "en",
  "trackChanges": true,
  "trackingStoreName": "google-play-tracker",
  "maxReviewsPerApp": 100,
  "reviewsSort": "newest",
  "reviewsStars": "all",
  "reviewsDevice": "all",
  "reviewsPeriod": "all",
  "includeReviewerNames": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "autoProxyFallback": true
}
```

# Actor output Schema

## `dataset` (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 = {
    "appIds": [
        "com.whatsapp",
        "com.duolingo",
        "com.spotify.music"
    ],
    "maxAppsPerSource": 20,
    "maxReviewsPerApp": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/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 = {
    "appIds": [
        "com.whatsapp",
        "com.duolingo",
        "com.spotify.music",
    ],
    "maxAppsPerSource": 20,
    "maxReviewsPerApp": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/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 '{
  "appIds": [
    "com.whatsapp",
    "com.duolingo",
    "com.spotify.music"
  ],
  "maxAppsPerSource": 20,
  "maxReviewsPerApp": 100
}' |
apify call foxlabs/google-play-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,foxlabs/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/zgrKBbXOz85NOtrjR/builds/gv9SZ1SvgUwKpen77/openapi.json
