# App Store & Google Play Scraper — Reviews, Apps & Charts (`cheapapi/app-store-google-play-scraper`) Actor

Scrape Google Play & App Store reviews, app details, keyword search rankings and top charts. Paste app links or IDs. No login, pay per result.

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

## Pricing

Pay per event + usage

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## App Store & Google Play Scraper: Reviews, Apps, Charts

Get **app reviews, full app details, keyword search rankings and top charts** from **Google Play and the Apple App Store** in one Actor. Paste store links or app IDs and the store is detected automatically. No login, no browser, no proxies. Every row has a stable `id`, so repeated runs are easy to de-duplicate.

**Typical cost:** 1,000 reviews of one app ≈ **$0.09**, plus a small Apify platform usage charge (usually under $0.01 for a run like that), on every Apify plan.

### Why this Actor

- **Both stores, four data types, one Actor.** Reviews, app details, keyword search results and top charts from Google Play *and* the App Store. You can mix Google Play and App Store apps in the same run.
- **$0.09 per 1,000 reviews** on either store, on every Apify plan, plus Apify platform usage billed separately by Apify (see the comparison below).
- **Cheap rankings:** $0.10 per 1,000 top chart positions and $0.15 per 1,000 search results. The full Top 100 free apps in both stores costs **$0.02**. Most review tools do not offer charts or keyword rankings at all.
- **Rich rows:** 27 fields per review (including developer reply and app version) and 40+ fields per app (installs, developer email and website, screenshots, similar apps, release notes and more).
- **249 country storefronts, 73 languages**, 8 chart types, 80+ chart categories, star-rating filter, newest or most-relevant order.
- **Privacy by default:** reviewer names and avatars are *not* collected unless you turn on `includeReviewerDetails`.

What is **not** included: in-app purchase lists, historical rank charts, and more than 500 App Store reviews per app, country and sort order (an App Store limit). Full list: [Not included](#not-included).

### Compared with alternatives

Typical run: **1,000 reviews of one app**. Prices are per run, taken from the public Apify Store listings (event fees only). Platform usage terms differ between Actors and are not shown in this table; check each listing for how its compute is billed. For this Actor, Apify platform usage is billed separately by Apify on top of the prices shown ("+ usage"; at the default 256 MB a small run typically uses about $0.001–$0.005, more when a standard-speed run waits several minutes).

| Option | Users | Price on Free plan | Price on Gold plan | Google Play | App Store | App details, keyword ranks, top charts |
|---|---|---|---|---|---|---|
| Most-used Google Play reviews Actor | ~2,800 | $0.15 | $0.10 | ✓ | ✗ | ✗ (reviews only) |
| Most-used App Store reviews Actor | ~2,300 | $0.10 | $0.10 | ✗ | ✓ | ✗ (reviews only) |
| Most-used general Google Play scraper | ~2,700 | $0.30 | $0.30 | ✓ | ✗ | Google Play app details and app lists; no App Store |
| Dual-store reviews Actor | ~150 | $0.50 | $0.30 | ✓ | ✓ | ✗ (reviews only) |
| **This Actor** | new | **$0.09** + usage | **$0.09** + usage | ✓ | ✓ | ✓ |

Prices from public Apify Store listings, checked September 2026. Honest reading: on the Gold plan our review fee is only $0.01 below the two single-store leaders ($0.09 vs $0.10), and once our platform usage is added a single-store run there can cost about the same or less; the bigger differences are on the Free plan (vs. the Google Play reviews leader), against the general Google Play scraper ($0.30 per 1,000 rows on every plan), against the dual-store option, and that one run covers both stores plus app details, keyword rankings and top charts. Competitor prices can change at any time; check their listing before you decide.

### Not included

| Not included | Why | Alternative |
|---|---|---|
| In-app purchase lists and prices | Not part of the store data we read | App details give `price`, `isFree` and `priceText` of the app itself |
| Historical rank charts | Each run is a snapshot of today's ranks | Schedule a search or chart run daily and join rows on `id` (see [Monitoring](#monitoring-new-reviews-and-rank-changes)) |
| More than 500 App Store reviews per app, country and sort order | App Store limit | Run more countries or both sort orders |
| Keyword search volume / difficulty for app stores | Not available from the stores | For **web** search keywords use our [Keyword Research Tool](https://apify.com/cheapapi/keyword-research-tool) |
| Web search rankings of an app's website | Different data source | [Google SERP Scraper](https://apify.com/cheapapi/google-serp-scraper) and [Domain SEO Analyzer](https://apify.com/cheapapi/domain-seo-analyzer) |
| Mentions of your app in AI assistants (ChatGPT, Perplexity, Gemini) | Different data source | [AI Search Visibility Tracker](https://apify.com/cheapapi/ai-search-visibility-tracker) |
| YouTube reviews and comments about an app | Different platform | [YouTube Comments Scraper](https://apify.com/cheapapi/youtube-comments-scraper) |
| Automatic "only new reviews since the last run" filter | Every run is priced per row returned; see the recipe below | [Monitoring recipe](#monitoring-new-reviews-and-rank-changes) |

### What data you get

The **What to get** option (`mode`) picks one of four outputs. You get one dataset row per review, app, search result or chart position.

#### Reviews (`mode: "reviews"`)

| Field | Type | Example |
|---|---|---|
| `id` | string | `b7b80a3cc3dd2f59`: stable row id; the same review always gets the same id |
| `store` / `storeName` | string | `googlePlay` / `Google Play` |
| `appId` | string | `org.telegram.messenger` |
| `appTitle` | string | `Telegram` |
| `appRating` | number | `3.8` |
| `appRatingsCount` | number | `17260048` |
| `appUrl` | string | `https://play.google.com/store/apps/details?id=org.telegram.messenger&hl=en&gl=us` |
| `reviewId` | string | `592d5883-48fa-4228-814d-be853329f163` |
| `position` | number | `1` |
| `rating` | number | `5` |
| `title` | string | `‘Scrolling in view’` (App Store only) |
| `text` | string | `I downloaded this app onto my Kindle Fire…` |
| `publishedAt` | string (ISO date) | `2026-09-17T15:57:06.000Z` |
| `appVersion` | string | `12.10.1` |
| `helpfulCount` | number | `2` (Google Play) |
| `developerReply` / `developerReplyAt` / `developerReplyAuthor` | string | reply text / ISO date / `Telegram FZ-LLC` |
| `userName` / `userImageUrl` | string | `null` by default; filled only with `includeReviewerDetails: true` |
| `sortedBy` / `ratingFilter` | string / number | `newest` / `null` |
| `inputApp`, `country`, `language`, `scrapedAt` | string | the app exactly as you entered it, `US`, `en`, collection time `2026-09-28T09:00:00.000Z` |

Meaning of the less obvious fields: `position` is the review's place in the store's list for the chosen order; `appRating` / `appRatingsCount` describe the whole app (not the review); `appVersion` is the app version the reviewer used; `sortedBy` and `ratingFilter` repeat the options used for the run.

#### App details (`mode: "appDetails"`)

| Field | Type | Example |
|---|---|---|
| `appId`, `title`, `subtitle`, `url`, `iconUrl` | string | `835599320`, `TikTok - Videos, Shop & LIVE`, `Watch, discover and stream!` |
| `description` | string | full store description |
| `rating`, `ratingsCount`, `reviewsCount` | number | `4.7`, `18373452` |
| `installs`, `installsCount` | string, number | `1,000,000,000+`, `2981239566` (Google Play) |
| `isFree`, `price`, `currency`, `priceText` | boolean, number, string | `true`, `0`, `USD` |
| `category`, `categories`, `tags`, `contentRating`, `languages` | string / array | `Communication`, `["13+"]` (category and tags come from Google Play; App Store listings usually have none) |
| `developer`, `developerId`, `developerUrl`, `developerWebsite`, `developerEmail`, `developerAddress` | string | `TikTok Ltd.`, `support@telegram.org` |
| `version`, `minimumOsVersion`, `size` | string | `47.0.0`, `Requires iOS 14.0 or later.`, `1 GB` |
| `releasedAt`, `updatedAt`, `updateNotes` | string | `2026-09-22T02:34:27.000Z`, release notes |
| `screenshots`, `videos` | array | image / video URLs |
| `similarApps`, `moreAppsByDeveloper` | array of `{appId, title, url}` | `[{ "appId": "1338605092", "title": "Kwai…" }]` |
| `id` | string | stable id per app, country and language |
| `store`, `storeName`, `inputApp`, `country`, `language`, `scrapedAt` | string | as in review rows |
| `extra` | object | store fields we do not map to a column yet (only present when there are any) |

`ratingsCount` counts star ratings, `reviewsCount` counts written reviews; `installs` is the public range and `installsCount` the exact number (Google Play); `contentRating` is the age rating; `minimumOsVersion` is the store's text.

#### Search results and top charts (`mode: "search"` / `"charts"`)

| Field | Type | Example |
|---|---|---|
| `type` | string | `search` or `chart` |
| `keyword` / `chart` / `category` | string | `vpn` / `topFree` / `games` |
| `rank` | number | `1` |
| `appId`, `title`, `url`, `iconUrl`, `developer`, `developerUrl` | string | `632064380`, `Vinted: Pre-loved marketplace`, developer `Proton AG` (Google Play; `null` in App Store charts) |
| `rating`, `ratingsCount` | number | `4.86`, `339202` |
| `isFree`, `price`, `currency`, `priceText` | boolean, number, string | `true`, `0`, `USD` |
| `totalResults` | number | total results for the keyword (search only; `null` when the store does not report it) |
| `id` | string | stable id per app in the same keyword or chart, category, country and language: join runs on it to track rank changes |
| `store`, `storeName`, `country`, `language`, `scrapedAt` | string | `appStore`, `App Store`, `US`, `en`, collection time |

The dataset has three table views (**Reviews**, **App details**, **Search results & charts**). A `RUN_SUMMARY` record in the key-value store lists how many results were saved, which inputs returned nothing and why.

### How to use

#### In Apify Console

1. Open the Actor and click **Try for free** (or **Start** if you already have it).
2. Choose **What to get**: Reviews (default), App details, Search results or Top charts.
3. Paste apps into **Apps**. Any of these work:

   - `https://play.google.com/store/apps/details?id=org.telegram.messenger`
   - `https://apps.apple.com/us/app/telegram-messenger/id686449807`
   - `org.telegram.messenger` (Google Play package name)
   - `686449807` or `id686449807` (App Store ID)

   For search, fill **Search keywords** instead. Top charts need no input (default: Top free, both stores).
4. Set **Max results** (reviews per app, results per keyword or positions per chart).
5. Optional: open the **Advanced** sections for country, language, review order, star filter, chart type and category.
6. Click **Start**, then download the results as JSON, CSV, Excel, XML or HTML from the **Output** tab.

#### Input JSON

```json
{
    "mode": "reviews",
    "apps": [
        "https://play.google.com/store/apps/details?id=org.telegram.messenger",
        "https://apps.apple.com/us/app/telegram-messenger/id686449807"
    ],
    "maxResults": 100,
    "country": "US",
    "language": "en"
}
```

Keyword rankings: `{ "mode": "search", "keywords": ["vpn", "photo editor"], "store": "both" }`. Top charts: `{ "mode": "charts", "charts": ["topFree", "topGrossing"], "category": "games" }`.

#### API: curl

```bash
curl -X POST "https://api.apify.com/v2/acts/cheapapi~app-store-google-play-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"mode":"reviews","apps":["org.telegram.messenger","686449807"],"maxResults":100}'
```

#### API: JavaScript (`apify-client`)

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

const client = new ApifyClient({ token: '<YOUR_TOKEN>' });
const run = await client.actor('cheapapi/app-store-google-play-scraper').call({
    mode: 'reviews',
    apps: ['org.telegram.messenger', '686449807'],
    maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

#### API: Python (`apify-client`)

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("cheapapi/app-store-google-play-scraper").call(run_input={
    "mode": "charts",
    "charts": ["topFree"],
    "store": "both",
    "country": "GB",
    "maxResults": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["rank"], item["storeName"], item["title"])
```

### Use cases

- **Review monitoring and customer feedback:** track new 1-star reviews of your app (or a competitor's) every day and route them to Slack or a support tool.
- **Sentiment analysis and AI training data:** export thousands of reviews with rating, text, date and app version for NLP or LLM analysis.
- **App Store Optimization (ASO):** check where your app ranks for target keywords in each country and store.
- **Market and competitor research:** read the Top grossing / Top free charts per category and country, and pull full app details (installs, price, update cadence).
- **Lead generation:** collect developer names, websites and support emails from the details of apps in a category.
- **Release tracking:** watch `version`, `updatedAt` and `updateNotes` of competitor apps.

### Advanced options

| Option | Default | Values | Meaning |
|---|---|---|---|
| `country` | `US` | 249 ISO codes (dropdown) | Store storefront. Reviews, ranks, charts and prices differ per country. |
| `language` | `en` | 73 ISO language codes (dropdown) | Google Play returns reviews written in this language; App Store reviews belong to the country storefront. |
| `reviewSort` | `newest` | `newest`, `mostRelevant` | Most relevant = Google Play relevance / App Store "most helpful". |
| `reviewRating` | `any` | `any`, `1` to `5` | Only reviews with this star rating (Google Play only). |
| `includeReviewerDetails` | `false` | `true` / `false` | Fill `userName` and `userImageUrl` (personal data). Same price either way. |
| `store` | `both` | `both`, `googlePlay`, `appStore` | Which store(s) to search or read charts from. For reviews and app details the store comes from each app link. |
| `charts` | `["topFree"]` | `topFree`, `topPaid`, `topGrossing`, `newFree`, `newPaid`, `newApps` (App Store), `featured` (Google Play), `moversShakers` (Google Play) | One or more charts. |
| `category` | none (overall chart) | 80+ categories, e.g. `games`, `game_puzzle`, `finance`, `health_and_fitness`, `social_networking` | Chart category. Similar names are matched across stores (`games` ↔ `game`, `social` ↔ `social_networking`, `tools` ↔ `utilities`). |
| `ageRating` | `all` | `all`, `upTo5`, `from6to8`, `from9to12` | Google Play Family charts by kids' age group. |
| `speed` | `auto` | `auto`, `fast`, `economy` | Same result prices in every mode (the rarely charged request minimum is lower at standard speed). Auto = fast for small runs (up to 10 requests), standard for larger ones. |

`maxResults` (main section, default 100) means reviews per app (minimum 50; up to 100,000 on Google Play and 500 on the App Store), results per keyword (30 to 200) or positions per chart (50 to 200; App Store charts have 100).

Advanced example, 1-star German reviews of Spotify, most relevant first:

```json
{ "mode": "reviews", "apps": ["com.spotify.music"], "country": "DE", "language": "de", "reviewSort": "mostRelevant", "reviewRating": "1", "maxResults": 2000 }
```

### Output example

Example output (fields as the Actor writes them; long texts shortened). Review with default settings, reviewer details off:

```json
{
    "id": "b7b80a3cc3dd2f59",
    "store": "googlePlay",
    "storeName": "Google Play",
    "appId": "org.telegram.messenger",
    "appTitle": "Telegram",
    "appRating": 3.8,
    "appRatingsCount": 17260048,
    "appUrl": "https://play.google.com/store/apps/details?id=org.telegram.messenger&hl=en&gl=us",
    "reviewId": "592d5883-48fa-4228-814d-be853329f163",
    "position": 1,
    "rating": 5,
    "title": null,
    "text": "I downloaded this app onto my Kindle Fire to get updates and communicate more easily…",
    "userName": null,
    "userImageUrl": null,
    "publishedAt": "2026-09-17T15:57:06.000Z",
    "appVersion": "12.10.1",
    "helpfulCount": 2,
    "developerReply": null,
    "developerReplyAt": null,
    "developerReplyAuthor": null,
    "sortedBy": "newest",
    "ratingFilter": null,
    "inputApp": "https://play.google.com/store/apps/details?id=org.telegram.messenger",
    "country": "US",
    "language": "en",
    "scrapedAt": "2026-09-28T09:00:00.000Z"
}
```

With `"includeReviewerDetails": true` the same row has `"userName": "MK"` and `"userImageUrl": "https://play-lh.googleusercontent.com/a-/…"`.

App details (shortened):

```json
{
    "id": "9b1f56f5e83f008b",
    "store": "appStore",
    "appId": "835599320",
    "title": "TikTok - Videos, Shop & LIVE",
    "subtitle": "Watch, discover and stream!",
    "rating": 4.7,
    "ratingsCount": 18373452,
    "isFree": true,
    "contentRating": ["13+"],
    "developer": "TikTok Ltd.",
    "version": "47.0.0",
    "size": "1 GB",
    "minimumOsVersion": "Requires iOS 14.0 or later.",
    "updatedAt": "2026-09-22T02:34:27.000Z",
    "updateNotes": "Easily post videos and photos on TikTok with bug fixes and performance improvements.",
    "screenshots": ["https://is1-ssl.mzstatic.com/image/thumb/…"],
    "similarApps": [{ "appId": "1338605092", "title": "Kwai - Short Video Community", "url": "https://apps.apple.com/us/app/…/id1338605092" }],
    "moreAppsByDeveloper": [{ "appId": "1498607143", "title": "Lemon8 - Lifestyle Community", "url": "https://apps.apple.com/us/app/…/id1498607143" }]
}
```

Google Play app details also include `installs` ("1,000,000,000+"), `installsCount` (exact), `developerEmail`, `developerAddress`, `developerWebsite`, `tags` and `releasedAt`.

Example output, App Store top chart entry (App Store charts do not list the developer, so `developer` is `null`; Google Play search and chart rows usually have it, e.g. `"developer": "Proton AG"`):

```json
{
    "id": "2b41ed608328e39b",
    "type": "chart",
    "store": "appStore",
    "storeName": "App Store",
    "keyword": null,
    "chart": "topFree",
    "category": null,
    "rank": 1,
    "appId": "632064380",
    "title": "Vinted: Pre-loved marketplace",
    "url": "https://apps.apple.com/us/app/vinted-pre-loved-marketplace/id632064380",
    "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/…/512x512bb.jpg",
    "developer": null,
    "developerUrl": null,
    "rating": 4.86,
    "ratingsCount": 339202,
    "isFree": true,
    "price": 0,
    "currency": "USD",
    "priceText": null,
    "totalResults": null,
    "country": "US",
    "language": "en",
    "scrapedAt": "2026-09-28T09:00:00.000Z"
}
```

### Pricing

**Typical cost: 1,000 reviews ≈ $0.09 + a small Apify platform usage charge** (billed separately by Apify; at the default 256 MB a small run typically uses about $0.001–$0.005). Every Apify plan (Free, Bronze, Silver, Gold and above) pays the same event prices, with no start fee:

**Review $0.09 per 1,000** (both stores) · **App details $3.00 per 1,000** ($0.003 per app) · **Search result $0.15 per 1,000** · **Top chart entry $0.10 per 1,000**.

**Smallest request sizes:** 50 reviews per app, 30 search results per keyword and 50 chart positions. A smaller **Max results** is raised to these numbers (the run log warns at the start), so 10 reviews asked means up to 50 collected and charged (about $0.0045 per app).

**Apify Free plan:** Apify does not pay developers for Free-plan usage, so on the Free plan this Actor can be used for up to **$0.25 of results per calendar month**. When the allowance is used up, the run ends with a clear message (not an error). Any paid Apify plan removes the limit; prices are the same.

**Minimum per request (`minimum-top-up`, $0.0005 steps).** Each request has a small minimum that its results normally cover, so normal runs never see this fee. It is charged only when a request delivers less (an app not available in the country, an app with few reviews, a star filter or keyword with few results), and only the difference. Unrecognised links and rejected requests are free; a request accepted but not collected in time (or lost to a network problem) pays its full minimum.

| Request | Minimum (fast / standard speed) | Covered by |
|---|---|---|
| Reviews, Google Play | $0.00025 + $0.001875 / $0.0009375 per 150 reviews requested | 24 reviews (fast) |
| Reviews, App Store (standard only) | $0.00025 + $0.0009375 per 25 reviews requested | 45 reviews for 100 requested |
| App details | $0.00175 / $0.001 per app | 1 app |
| Search, Google Play / App Store | $0.00025 + $0.003 / $0.0015 per 30 (GP) or 100 (AS) results requested | 22 results (fast) |
| Top charts (both stores) | $0.00025 + $0.003 / $0.0015 per 100 positions requested | 33 positions (fast) |

Large review requests are fetched in growing rounds (Google Play: 150, then about 4× deeper each round; App Store: 100, then up to 500), so an app with fewer reviews than you asked for is never fetched much deeper than it has reviews. Each round counts as one request.

Worked examples:

- **1,000 reviews** of one app = 1,000 × $0.00009 = **$0.09**.
- **500 App Store reviews for each of 10 apps** = 5,000 reviews = **$0.45**.
- **Top 100 free apps in both stores** = 200 chart entries = **$0.02**; **100 app details** = **$0.30**; **30 search results** for one keyword = **$0.0045**.
- **App details for 3 apps, one of them not available in the chosen country** (fast): 2 × $0.003 + 4 × $0.0005 = **$0.008**.
- **An App Store app with only 10 reviews**, asked for 100 (standard): 10 × $0.00009 + 7 × $0.0005 = **$0.0044** (the request's minimum is $0.00400).
- **A Google Play search returning 5 results** for a keyword, asked for 30 (fast): 5 × $0.00015 + 5 × $0.0005 = **$0.00325**.

Set **Maximum cost per run** in the run options to cap spending. The Actor reads your plan's prices at run time, sets aside each request's worst case (results or minimum) before making it, stops collecting when the limit is reached, and shares the review budget fairly across your apps. Top-ups never push a run over your limit.

### Monitoring new reviews and rank changes

There is no built-in "only new since the last run" mode: the stores do not tell us which reviews are new, so every run has to fetch the list again, and each fetched review is a result. Recipe that keeps repeats cheap:

1. Create a **Schedule** (daily is usually enough) with `reviewSort: "newest"` and a `maxResults` a bit above the reviews your app gets per day (minimum 50; 50 newest reviews cost $0.0045 per app and run).
2. Keep the `id`s you already have (a Google Sheet column, a database table, or a named key-value store). Each review has the same `id` in every run and every country.
3. In your webhook, Make or Zapier step, keep only rows whose `id` is new, and send those to Slack, email or your support tool. Add `reviewRating: "1"` to watch only 1-star Google Play reviews.
4. **Ranks:** schedule a `search` or `charts` run and store the rows. The `id` of an app in the same keyword or chart, category, country and language stays the same, so you can join days on `id` and compare `rank`.

### Integrations

- **Make and Zapier:** use the Apify app to start the Actor and send new reviews or chart rows to thousands of other apps.
- **Google Sheets:** export the dataset directly, or use the Google Sheets integration to append rows after every run.
- **Webhooks:** get a POST request when a run succeeds or fails and pull the dataset from your own backend.
- **Schedules:** run daily or hourly from **Schedules** in Apify Console (for example, newest reviews every morning).
- **API and MCP:** call the Actor from any language via the Apify API (examples above), or let AI agents use it through the Apify MCP server.

### FAQ

**Is Apify platform usage included, and is there a Free-plan limit?** Platform usage (compute) is billed separately by Apify on top of the event fees; at the default 256 MB a small run typically uses about $0.001–$0.005, and standard-speed runs (always used for App Store reviews) wait for results and can use more. On the Apify Free plan you can use up to $0.25 of this Actor's results per calendar month (Apify pays developers nothing for Free-plan usage while our data costs are real). Runs that reach the allowance stop cleanly and keep everything collected so far; it resets on the 1st of the month. Any paid Apify plan has no limit.

**Why was I charged for more rows than I asked for, or for `minimum-top-up`?** Requests are collected in batches of at least 50 reviews per app, 30 search results per keyword and 50 chart positions, so a smaller **Max results** is raised to that (the run log warns at the start; 50 reviews = $0.0045). `minimum-top-up` is charged only when a request delivers less than its minimum (see [Pricing](#pricing)): typically an app not available in the chosen country, an app with few or no reviews (or few with your star filter), or a search or chart with few results. Only the difference is charged, in $0.0005 steps; `RUN_SUMMARY` shows `minimumTopUps`.

**Is it legal to scrape app reviews and store listings?** The Actor collects only publicly visible store data. Reviews can contain personal data, which is why reviewer names and avatars are off by default. If you turn them on, make sure you have a lawful basis (for example under GDPR or CCPA). This is not legal advice.

**How fresh is the data, and why are App Store reviews slower?** Every run reads the stores live at run time; `scrapedAt` shows when each row was collected. App Store reviews always use the standard processing speed and usually arrive within a few minutes; everything else runs in fast mode for small runs.

**How many reviews can I get?** Google Play: up to 100,000 per app and run. App Store: up to 500 per app, country and sort order; for more, run other countries (each storefront has its own reviews) or both sort orders.

**Why did I get fewer results than I asked for?** The app has fewer reviews in that country or language, the star filter removed some, the App Store 500-review cap applied, or your maximum cost per run was reached. The `RUN_SUMMARY` record tells you which.

**How do I control my budget?** Set **Maximum cost per run**. The Actor never charges more than that, and in review mode it splits the budget fairly between your apps.

**Can I monitor new reviews or rank changes?** Yes, see [Monitoring](#monitoring-new-reviews-and-rank-changes). There is no built-in "only new reviews" switch; you drop repeats on your side using the stable `id`.

**Can I mix Google Play and App Store apps in one run?** Yes. Each app is sent to its own store automatically (force one with a `google:` or `apple:` prefix). If one app fails, the others are still delivered and the failure is listed in `RUN_SUMMARY`.

**A field or feature is missing. Where do I get support?** Open an issue on the Actor's **Issues** tab and describe what you need.

### Limitations

- App Store: at most 500 reviews per app, country and sort order; the star-rating filter is Google Play only; App Store reviews have no helpful-vote count and no developer reply field.
- Requests are collected in batches: the minimum is 50 reviews per app, 30 search results per keyword and 50 positions per chart. App Store charts have up to 100 positions, Google Play charts up to 200.
- Some fields are store-specific (installs only on Google Play; subtitle and content rating only on the App Store) and are `null` or empty for the other store.
- Ranks reflect the storefront of the chosen country at the time of the run and can differ slightly from what a signed-in user sees on their device.
- The standard processing speed can take several minutes for large runs.
- Not included: in-app purchase lists, historical rank charts, and apps that are not available in the chosen country.

### Privacy and personal data

By default the Actor stores no reviewer names or profile pictures: `userName` and `userImageUrl` are `null`. They are filled only if you set `includeReviewerDetails: true`. Developer contact details in app details (website, support email, address) are business information that the developer publishes in the store. You are responsible for handling any personal data you choose to collect in line with applicable law.

# Actor input Schema

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

Reviews and app details use the <b>Apps</b> list. Search uses <b>Search keywords</b>. Top charts use the chart options in the advanced section (default: Top free, both stores).

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

Google Play or App Store links, Google Play package names (<code>org.telegram.messenger</code>) or App Store ids (<code>686449807</code> or <code>id686449807</code>). The store is detected automatically; prefix with <code>google:</code> or <code>apple:</code> to force it. In app details mode, an app that is not available in the chosen country costs its request minimum ($0.002 at fast speed, minimum-top-up).

## `keywords` (type: `array`):

Used when "What to get" is <b>Search results</b>: each keyword is searched in the store(s) chosen under Advanced → Store. You get the apps in the order the store ranks them. A keyword with few or no results pays the request minimum (minimum-top-up, $0.0005 steps).

## `maxResults` (type: `integer`):

Reviews: max reviews per app (App Store max 500 per app and country). Search: results per keyword (up to 200). Charts: positions per chart (up to 200; App Store charts have 100). <b>Minimums:</b> 50 reviews per app, 30 search results per keyword and 50 chart positions; a smaller number is raised to the minimum, and those rows are collected and charged (e.g. 10 reviews asked = up to 50 collected, about $0.0045 per app). The run log warns about this at the start. Each request also has a small minimum ($0.0005 steps, minimum-top-up) that its results normally cover; it is only charged when a request delivers few or no results (e.g. an app with few reviews).

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

Store storefront to read (all 249 countries). Reviews, rankings, charts and prices differ per country. Default: United States.

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

Language of the store listing and reviews. Google Play returns reviews written in this language; App Store reviews belong to the country storefront whatever language they are written in. Default: English.

## `reviewSort` (type: `string`):

Newest first is best for monitoring. "Most relevant" uses Google Play relevance and App Store helpfulness.

## `reviewRating` (type: `string`):

Filters Google Play reviews by star rating. The App Store does not support this filter. A filter that leaves few reviews can add a small minimum-top-up for the request (see Pricing).

## `includeReviewerDetails` (type: `boolean`):

Off by default: <code>userName</code> and <code>userImageUrl</code> are left empty (<code>null</code>). Turn on only if you have a lawful reason to process reviewers' personal data (e.g. under GDPR). Same price either way.

## `store` (type: `string`):

Which store(s) to search or read charts from. (For reviews and app details the store comes from each app link/id.)

## `charts` (type: `array`):

One or more charts. Charts that a store does not have are skipped for that store.

## `category` (type: `string`):

Optional. Limits charts to one category, e.g. <code>games</code>, <code>finance</code>, <code>health_and_fitness</code>. Similar categories are matched across stores (games → game, social → social_networking…). Leave empty for the overall chart.

## `ageRating` (type: `string`):

Google Play only. Shows the Family category chart for this age group.

## `speed` (type: `string`):

Result prices are the same in every mode (the rarely charged request minimum is lower at standard speed). App Store reviews always use the standard speed.

## Actor input object example

```json
{
  "mode": "reviews",
  "apps": [
    "https://play.google.com/store/apps/details?id=org.telegram.messenger",
    "https://apps.apple.com/us/app/telegram-messenger/id686449807"
  ],
  "keywords": [
    "vpn"
  ],
  "maxResults": 100,
  "country": "US",
  "language": "en",
  "reviewSort": "newest",
  "reviewRating": "any",
  "includeReviewerDetails": false,
  "store": "both",
  "charts": [
    "topFree"
  ],
  "ageRating": "all",
  "speed": "auto"
}
```

# Actor output Schema

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

No description

## `apps` (type: `string`):

No description

## `rankings` (type: `string`):

No description

## `results` (type: `string`):

No description

## `summary` (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 = {
    "mode": "reviews",
    "apps": [
        "https://play.google.com/store/apps/details?id=org.telegram.messenger",
        "https://apps.apple.com/us/app/telegram-messenger/id686449807"
    ],
    "keywords": [
        "vpn"
    ],
    "maxResults": 100,
    "country": "US",
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("cheapapi/app-store-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 = {
    "mode": "reviews",
    "apps": [
        "https://play.google.com/store/apps/details?id=org.telegram.messenger",
        "https://apps.apple.com/us/app/telegram-messenger/id686449807",
    ],
    "keywords": ["vpn"],
    "maxResults": 100,
    "country": "US",
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("cheapapi/app-store-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 '{
  "mode": "reviews",
  "apps": [
    "https://play.google.com/store/apps/details?id=org.telegram.messenger",
    "https://apps.apple.com/us/app/telegram-messenger/id686449807"
  ],
  "keywords": [
    "vpn"
  ],
  "maxResults": 100,
  "country": "US",
  "language": "en"
}' |
apify call cheapapi/app-store-google-play-scraper --silent --output-dataset

```

## MCP server setup

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