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

Apple App Store and Google Play in one Actor and one schema: app details, ratings, installs, reviews, search and top charts by country and category. No reviewer names or IDs; emails, phones and @handles in reviews and developer replies redacted. $1 per 1,000 apps, $0.10 per 1,000 reviews.

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

## Pricing

from $0.10 / 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

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

Get **app details, ratings, reviews, search results and top charts from the Apple App Store and Google Play in one run and one clean JSON schema**. Paste App Store IDs, Google Play package names or store URLs (the store is detected automatically), add search terms and charts, pick a country and language, and get one flat row per app and per review, with the same field names for both stores. Built for people and for AI agents: pay per row, no usage fees, no reviewer names or IDs, and e-mail addresses, phone numbers and @handles in review texts and developer replies are redacted.

| What you pay for | Price |
|---|---|
| `app`: one app row (details, search result or chart entry) | **$0.001** ($1 per 1,000 apps) |
| `review`: one review row | **$0.0001** ($0.10 per 1,000 reviews) |
| Actor start (standard Apify event, per run) | $0.00005 |

| Example | Rows | Cost |
|---|---|---|
| 1 app + 500 reviews | 1 app, 500 reviews | **$0.051** |
| 1 app, details only | 1 app | $0.001 |
| 5 competitors x 1,000 Google Play reviews | 5 apps, 5,000 reviews | $0.505 |
| Top 100 free apps on both stores | 200 apps | $0.20 |
| Keyword research: 10 search terms x 20 results x 2 stores | 400 apps | $0.40 |
| Our larger test run: 6 apps with 500 reviews each, a top-100 chart on both stores, 2 search terms on both stores | 282 apps, 3,000 reviews | $0.58 |

Apify platform costs are included. Apps that don't exist, invalid inputs and the run summary are free. Set **Maximum cost per run** (or `maxTotalChargeUsd` in the API) and the Actor stops cleanly at that amount; it never downloads reviews it can't charge for.

### What you get

- **Both stores, one schema.** `store` is `apple` or `google`; every other field means the same on both (null where a store doesn't have it).
- **App details**: title, developer, developer ID, page and website, category and genres, price and currency, free / paid, in-app purchase range and "contains ads" (Google Play), average rating, number of ratings, **ratings histogram (1-5 stars)** for listed apps on both stores (and for search and chart results with **Full details**), number of written reviews (Google Play), installs (Google Play, e.g. `10,000,000+`, plus the number), content rating, version, release and update dates (ISO 8601), size (App Store), minimum iOS / Android version, supported languages (App Store), full description and release notes as plain text, icon, screenshots and store URL.
- **Reviews**: review ID, stars, title (App Store), text, app version, date, helpful votes, the **developer's reply and its date** (Google Play), country, language and the sort order used. Sort by newest, most relevant / most helpful, or rating (Google Play). Google Play reviews are complete; **App Store reviews are best effort**, limited by Apple's public feed (at most 500 per country and sort order, and the feed sometimes answers empty pages: the Actor retries and warns you when reviews may be missing).
- **Search results** like a user typing in the store's search box, with `position` (App Store up to 200 per term, Google Play the ~30 apps on its first results page).
- **Top charts**: top free, top paid and top grossing (plus iPad charts on the App Store), overall or by category (games, productivity, finance, game-puzzle...), with `rank`. App Store chart entries are enriched with Apple's app data (everything but the ratings histogram, which **Full details** adds).
- **Any country and language** (`us`, `gb`, `de`, `in`, `jp`, `pt-br`...). App Store URLs keep their own country.
- **Each app once per run**: an app found by several inputs is returned and charged once (switch off for keyword rank tracking). **Each review is always returned and charged once per run**, even when the same app is found by several inputs.
- **No reviewer identities**: reviewer names, user IDs and profile pictures are never collected (not even read). In review texts **and developer replies**, e-mail addresses, phone numbers, order / ticket / account numbers and @handles become `[email]`, `[phone]`, `[number]` and `[handle]` (in replies also the developer's own support addresses and helplines; the app's own accounts such as `@SpotifyCares` stay). The name after a greeting ("Hi Sam," becomes "Hi,") is removed in both. Review texts also lose social-media profile links (`[link]`) and names after "my name is" or right before a removed e-mail or phone number (`[name]`). See the FAQ for what is not detected.
- Built for automation and AI agents: pay-per-event pricing without usage fees, limited permissions, a stable schema, a machine-readable `SUMMARY` record per run and clear per-input errors instead of failed runs.

### Use cases

- **ASO (App Store Optimization) and keyword research**: see which apps rank for a keyword on both stores, their ratings, installs and descriptions. Turn off "Return each app once per run" to track the position of every app for every term.
- **Competitor research**: compare your app with competitors on both platforms: rating, rating count, histogram, installs, price, in-app purchases, update frequency and release notes.
- **Review mining and sentiment analysis**: pull thousands of reviews per app, filter 1- and 2-star reviews, read developer replies, group by app version, feed them to an LLM to find bugs and feature requests.
- **Market and chart monitoring**: schedule a daily run of the top charts per country and category to spot rising apps and new entrants.
- **App portfolio and lead lists**: collect developer names, websites and categories for an app category or a set of keywords.
- **AI agents**: give an agent one tool for "What do users complain about in app X?", "Who ranks for 'habit tracker' in Germany?" or "What are the top grossing games in Japan?" with structured answers from both stores.

### How to use

1. **Apps**: one per line. `1232780281`, `id1232780281` or an App Store URL; `com.spotify.music` (package name) or a Google Play URL; `apple:com.spotify.client` to look up an App Store app by bundle ID.
2. **Search terms** and **Top charts** (optional): pick the stores to search, the charts and optionally chart categories.
3. Set **Country** and **Language**.
4. Turn on **Include reviews**, set **Max reviews per app** and the sort order. For more than 500 App Store reviews, add **Extra review countries**.
5. Run it and download the results as JSON, CSV or Excel, or read them via API. The dataset has the views **Apps**, **Charts & search**, **Reviews** and **All fields**. Apify views can't filter rows, so app and review rows appear in every view (with empty columns where a field doesn't apply); filter on `type` (`app` / `review`) in your export or code.

#### Input example

The prefilled input: Notion on Google Play with 20 reviews, plus "habit tracker" searched on both stores. Add an App Store ID such as `1232780281` (Notion) for App Store details and reviews:

```json
{
  "apps": ["notion.id"],
  "searchTerms": ["habit tracker"],
  "stores": ["apple", "google"],
  "country": "us",
  "language": "en",
  "maxResultsPerSearch": 5,
  "includeReviews": true,
  "maxReviewsPerApp": 20
}
```

More: `"charts": ["top-free", "top-grossing"], "chartCategories": ["games"], "maxAppsPerChart": 100`, `"reviewSort": "relevant"`, `"reviewCountries": ["gb", "ca", "au"]`, `"reviewsFor": "all"`, `"fullDetails": true`, `"dedupeApps": false`.

#### Output examples

From real runs on 18 Oct 2026 (long texts and lists shortened, empty fields left out). An **App Store app**:

```json
{
  "type": "app",
  "store": "apple",
  "appId": "1232780281",
  "bundleId": "notion.id",
  "title": "Notion: Notes, Tasks, AI",
  "developer": "Notion Labs, Incorporated",
  "developerId": "1232780280",
  "developerUrl": "https://apps.apple.com/us/developer/notion-labs-incorporated/id1232780280",
  "website": "https://www.notion.so",
  "category": "Productivity",
  "categoryId": "6007",
  "genres": ["Productivity", "Business"],
  "price": 0,
  "currency": "USD",
  "free": true,
  "priceText": "Free",
  "rating": 4.7752,
  "ratingCount": 90418,
  "histogram": {"1": 2381, "2": 884, "3": 1610, "4": 4930, "5": 80613},
  "contentRating": "4+",
  "version": "1.7.346",
  "released": "2017-09-14T19:07:31Z",
  "updated": "2026-10-13T04:16:20Z",
  "size": 485872640,
  "minOsVersion": "18.0",
  "languages": ["AR", "DA", "NL", "EN", "FI"],
  "description": "Notion is an AI-powered all-in-one workspace that helps individuals and teams write notes, manage tasks, organize projects, and collaborate in one place. ...",
  "releaseNotes": "Bug fixes and performance improvements.",
  "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/ba/fa/05/.../512x512bb.jpg",
  "url": "https://apps.apple.com/us/app/notion-notes-tasks-ai/id1232780281",
  "source": "app",
  "detailsComplete": true,
  "country": "us",
  "language": "en",
  "scrapedAt": "2026-10-17T21:38:59Z"
}
```

The same app on **Google Play** (some fields left out; Google Play fills fields the App Store doesn't have, and the other way round):

```json
{
  "type": "app",
  "store": "google",
  "appId": "notion.id",
  "bundleId": "notion.id",
  "title": "Notion: AI Workspace",
  "summary": "Projects, Docs, Meetings, Agents. All in Notion.",
  "developer": "Notion Labs, Inc.",
  "developerUrl": "https://play.google.com/store/apps/developer?id=Notion+Labs,+Inc.",
  "website": "http://www.notion.so/mobile",
  "privacyPolicyUrl": "https://www.notion.so/notion/Terms-and-Privacy-28ffdd083dc3473e9c2da6ec011b58ac",
  "category": "Productivity",
  "categoryId": "PRODUCTIVITY",
  "price": 0,
  "currency": "USD",
  "free": true,
  "inAppPurchases": true,
  "inAppPurchaseRange": "$11.99 - $239.99 per item",
  "containsAds": false,
  "rating": 4.6125,
  "ratingCount": 399874,
  "reviewCount": 4940,
  "histogram": {"1": 18163, "2": 5661, "3": 12800, "4": 39627, "5": 323591},
  "installs": "10,000,000+",
  "minInstalls": 10000000,
  "contentRating": "Everyone",
  "version": "0.6.4222",
  "released": "2018-06-05T17:34:21Z",
  "updated": "2026-10-14T01:49:26Z",
  "minOsVersion": "8.0",
  "url": "https://play.google.com/store/apps/details?id=notion.id&hl=en&gl=us",
  "source": "app",
  "detailsComplete": true,
  "country": "us",
  "language": "en"
}
```

A **Google Play review** with the developer's reply (sort: most relevant) and an **App Store review** (sort: most helpful):

```json
{
  "type": "review",
  "store": "google",
  "appId": "com.spotify.music",
  "appTitle": "Spotify: Music and Podcasts",
  "reviewId": "31dd5f9b-bd5f-4e2f-9465-71f707e14285",
  "rating": 2,
  "title": null,
  "text": "I don't know what you guys did in the last update, but now when I listen to any podcast, it plays the list opposite. ...",
  "appVersion": "9.1.82.2160",
  "date": "2026-09-18T06:24:22Z",
  "thumbsUp": 315,
  "voteCount": null,
  "developerReply": "Hi! For more information, visit support.spotify.com and search for \"Follow artists, podcasts, and shows.\" ...",
  "developerReplyDate": "2026-09-18T15:50:44Z",
  "sort": "relevant",
  "country": "us",
  "language": "en",
  "scrapedAt": "2026-10-17T21:40:07Z"
}
```

```json
{
  "type": "review",
  "store": "apple",
  "appId": "324684580",
  "appTitle": "Spotify: Music and Podcasts",
  "reviewId": "11994640656",
  "rating": 5,
  "title": "Favorite app ever",
  "text": "I personally love this app. I have had it for over 4 years, and it’s been an app I use daily. ...",
  "appVersion": "8.9.94",
  "date": "2024-11-26T16:10:59Z",
  "thumbsUp": 62,
  "voteCount": 96,
  "developerReply": null,
  "developerReplyDate": null,
  "sort": "relevant",
  "country": "us",
  "language": "en",
  "scrapedAt": "2026-10-17T21:40:07Z"
}
```

Search and chart rows are app rows with their context: `"source": "search", "searchTerm": "habit tracker", "position": 1` or `"source": "chart", "chart": "top-free", "chartCategory": null, "rank": 1`. Google Play search and chart rows carry the fields shown on the result card (title, developer, rating, installs, price, category, content rating, description, icon, screenshots) and `"detailsComplete": false`; App Store search and chart rows have every field except the ratings histogram (`"detailsComplete": false`). Turn on **Full details for search and chart results** to open each result's store page for the missing fields (both stores).

#### Output fields

| Field | Description |
|---|---|
| `type` | `app` or `review` |
| `store` | `apple` (App Store) or `google` (Google Play) |
| `appId`, `bundleId` | App Store numeric ID or Google Play package name; iOS bundle ID / Android package |
| `title`, `summary` | App name (review rows: review title, App Store only); short description (Google Play) |
| `developer`, `developerId`, `developerUrl`, `website`, `privacyPolicyUrl` | Developer name, ID and store page, developer website, privacy policy (Google Play) |
| `category`, `categoryId`, `genres` | Primary category, its ID (`6007` / `PRODUCTIVITY`), all genres |
| `price`, `currency`, `free`, `priceText` | Price in the store's currency, ISO code, free flag, price as shown |
| `inAppPurchases`, `inAppPurchaseRange`, `containsAds` | Google Play app pages |
| `rating`, `ratingCount`, `reviewCount`, `histogram` | Average rating, number of ratings, number of written reviews (Google Play), ratings per star |
| `installs`, `minInstalls` | Google Play install bucket (`10,000,000+`) and its number |
| `contentRating`, `version`, `released`, `updated`, `size`, `minOsVersion`, `languages` | Age rating, version, dates (ISO 8601 UTC), download size in bytes (App Store), minimum OS, languages (App Store) |
| `description`, `releaseNotes`, `iconUrl`, `screenshots`, `url` | Plain-text texts, images and the store page |
| `source`, `chart`, `chartCategory`, `rank`, `searchTerm`, `position` | How the app was found: `app` (listed), `search` or `chart`, with rank / position |
| `detailsComplete` | False when some fields were not read: Google Play search / chart cards, App Store search / chart rows without the histogram (see above) |
| `appTitle`, `reviewId`, `rating`, `title`, `text`, `appVersion`, `date` | Review rows: app name, review ID, stars (1-5), title, text (e-mails, phone numbers, order numbers, @handles, profile links and self-introduced names redacted), version, date |
| `thumbsUp`, `voteCount` | Helpful votes (Google Play thumbs up / App Store vote sum), total votes (App Store) |
| `developerReply`, `developerReplyDate` | Google Play developer reply and its date. Redacted like review texts: e-mails, phone numbers, order numbers and @handles (also the developer's own support contacts) and the reviewer's name in the greeting |
| `sort`, `country`, `language`, `scrapedAt` | Review sort order used; store country (Google Play reviews depend on the language, not the country), language, run time |

### Use it from the API, an app or an AI agent

Run synchronously and get the rows in one call (keep your API token in the `APIFY_TOKEN` environment variable):

```bash
curl -X POST "https://api.apify.com/v2/acts/fairfetch~app-store-google-play-scraper/run-sync-get-dataset-items?maxTotalChargeUsd=0.50" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"apps": ["com.spotify.music", "324684580"], "includeReviews": true, "maxReviewsPerApp": 200, "reviewSort": "newest"}'
```

Python (`pip install apify-client`):

```python
import os
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("fairfetch/app-store-google-play-scraper").call(
    run_input={"searchTerms": ["habit tracker"], "country": "de", "language": "de", "maxResultsPerSearch": 20},
    max_total_charge_usd=Decimal("0.20"),
)
for row in client.dataset(run.default_dataset_id).iterate_items():
    print(row["store"], row["position"], row["title"], row["rating"], row["installs"])
```

The Actor runs with limited permissions and pay-per-event pricing without usage fees, so AI agents can call it through the Apify MCP server and pay per run. Each run also writes a `SUMMARY` record (per-input counts, errors, warnings, notes and request statistics) to its key-value store.

### Performance and limits

- From our test runs on Apify (256 MB): the prefilled input (11 apps, 20 reviews) takes a few seconds; 282 apps and 3,000 reviews about 27 seconds; 5,000 reviews of one Google Play app about 50 seconds; a top-200 Google Play chart with full details about 3 minutes (one app page per app). Peak memory stayed around 100 MB. Listed App Store apps are looked up 100 at a time, plus one product-page request per app for the ratings histogram, one at a time: about 1.2 seconds per app, so 1,000 listed App Store apps take about 20 minutes.
- No proxy needed: both stores answered Apify's servers directly in all tests. Requests are polite: at most two requests at a time per store host, a few per second at most, with retries and exponential backoff on errors and throttling (HTTP 429 / 403 / 5xx). If you add a proxy, it is only used for a store that blocks direct requests.
- **App Store reviews are best effort: at most 500 per country and sort order.** Apple's public review feed has 10 pages of 50. To get more, add **Extra review countries** (each adds up to 500) or run again with the other sort order. Apple's feed is also flaky at times and answers empty pages: the Actor re-reads such pages up to 5 times (bypassing Apple's cache) when the app has 50 or more ratings in that country (for extra review countries, one lookup per country gets Apple's rating count there), and if they stay empty the status message and `SUMMARY` (`appStoreReviewsIncomplete`) say that App Store reviews may be incomplete and for which countries; run again later. Where the app has few or no ratings, an empty feed is read once and the `SUMMARY` notes say so (e.g. `Apple has no reviews of ... in "de" (0 ratings there)`), without a warning. When two inputs need the same app's reviews in the same App Store country, the second continues where the first stopped; reviews already returned count toward its maximum. Google Play has no such limit (pages of 200 with continuation tokens).
- **Google Play reviews depend on the language, not the country**: the same app in two countries with the same language has the same reviews, so they are returned (and charged) once per run.
- App Store charts list up to 100 apps; Google Play charts up to 200. Google Play search returns the apps of its first results page (about 30); the App Store search API up to 200.
- "Rating" review sort exists on Google Play only (App Store reviews then use "newest").
- Google Play doesn't publish app size; the App Store doesn't publish installs.

### FAQ

**Do I need an Apple or Google account, a developer account or an API key?**
No. The Actor reads public store data: Apple's official iTunes Search, Lookup and RSS feeds, and the public Google Play website.

**How does it know which store an app is from?**
Numbers (`1232780281`, `id1232780281`) and apps.apple.com URLs are App Store apps; package names (`com.spotify.music`) and play.google.com URLs are Google Play apps. For an App Store bundle ID write `apple:com.spotify.client`.

**Why did I get fewer App Store reviews than I asked for?**
Apple's feed stops at 500 reviews per country and sort order; add review countries (e.g. `gb`, `ca`, `au`) or use the other sort order. Apple's feed also answers empty pages at times. The Actor retries them; when they stay empty, the status message says "App Store reviews may be incomplete" and the `SUMMARY` lists the apps. Run those apps again later.

**Can I get reviews for every app in a chart or search?**
Yes: set **Reviews for** to "Every app returned". Use **Max reviews per app** and the maximum cost per run to keep the total in check.

**Why are some fields null on search and chart rows?**
Google Play result cards don't carry rating count, histogram, version or dates, and App Store search and chart rows have no ratings histogram. Turn on **Full details for search and chart results**; it opens each result's store page (one extra request per app, same price per row).

**What does "Return each app once per run" do?**
An app found by several inputs (listed, in a search and in a chart) is returned and charged once, with the context of the first input that found it. Turn it off for keyword rank tracking to get one row per search term and chart position.

**What happens if an app ID is wrong?**
The other inputs still run. The wrong one is listed in the status message and in the `SUMMARY` record with the reason (`not_found`, `invalid_input`...) and isn't charged. If every input is wrong, the run still ends SUCCEEDED with a status message that says so. The run fails only when the stores can't be reached (every input blocked, network errors or a changed page format).

**Does it collect personal data?**
Reviewer names, user IDs and profile pictures are never collected. Review rows contain the review itself (stars, title, text, date, version, votes, developer reply). Because people type contact details into reviews, and replies can contain a support agent's direct contact, review texts **and developer replies** are redacted before they are stored: e-mail addresses (also broken ones like `name@gmail`) become `[email]`, phone numbers (8-15 digits in international or national formats) `[phone]`, order, ticket, transaction and account numbers written after such a word (also as `Order #1234567890`) `[number]`, and @handles `[handle]`. In replies this also removes the developer's own support addresses and helpline numbers; the app's own accounts (e.g. `@SpotifyCares`, `@SpotifyUK`) and website links stay. Dates and times (`12/10/2024 10:30 am`), years, versions (`10.12.123.1234`), error codes, levels, prices, counts (`12 345 678 views`) and round numbers are left alone. The name after a greeting at the start ("Hi Sam," becomes "Hi,") is removed in reviews and replies. Review texts also lose social-media profile links (`[link]`) and names after "my name is" or written right before a removed e-mail or phone number (`[name]`). Other personal details a reviewer writes in their own words (for example a name in the middle of a sentence) can't be detected reliably and are returned as written. Developer names and websites are the publishers' public business information shown on the store pages.

**How fresh is the data?**
It is read live from the stores in every run. Ratings and review counts are what the stores show at that moment (Google Play's totals can lag its histogram by a few minutes).

### Legal

This Actor reads only public information that the App Store and Google Play show to every visitor, through Apple's public APIs and feeds and the public Google Play website. It does not log in, bypass access controls or collect reviewer names, IDs or profile pictures, and it redacts contact details found in review texts and developer replies. It is not affiliated with, endorsed or sponsored by Apple or Google; "App Store" and "Google Play" are trademarks of their owners and are used here only to describe where the data comes from. You are responsible for how you use the data and for complying with the stores' terms and the laws that apply to you.

# Actor input Schema

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

One app per line. The store is detected automatically:

- App Store ID: 1232780281 or id1232780281
- App Store URL: https://apps.apple.com/us/app/notion-notes-tasks-ai/id1232780281 (the country in the URL is used for that app)
- Google Play package name: notion.id or com.spotify.music
- Google Play URL: https://play.google.com/store/apps/details?id=com.spotify.music
- App Store bundle ID: apple:com.spotify.client
  Every app returns one full app row; turn on reviews below to add its reviews.

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

Search each selected store for these keywords, like a user typing in the store's search box. Every result is one app row with its position. The App Store returns up to 200 results per term, Google Play the apps on its first results page (about 30).

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

Top charts to read from each selected store, in rank order. iPad charts exist on the App Store only.

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

Read the charts of these categories instead of the overall chart (used together with "Top charts" only). One name works for both stores: games, productivity, finance, health-fitness, education, entertainment, social, shopping, music, photo-video, travel, business, lifestyle, utilities, news, food-drink, medical, sports, weather, navigation, books, reference, graphics-design, game-puzzle, game-action, game-casual, game-strategy (and the other game-\* genres). Apple genre IDs (6014) and Google Play category codes (GAME_PUZZLE) work too.

## `stores` (type: `array`):

Which stores to search and read charts from. Apps listed above are always read from their own store.

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

Two-letter country code of the store front, e.g. us, gb, de, in, jp. Sets prices, currency, charts, search results and the App Store reviews you get. Google Play reviews depend on the language, not the country.

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

Language of descriptions and Google Play reviews, e.g. en, de, fr, ja, pt-br. Google Play returns the same reviews for every country with the same language. App Store reviews are always in the language their writers used.

## `maxResultsPerSearch` (type: `integer`):

Maximum apps returned per search term and store (App Store: up to 200; Google Play shows about 30).

## `maxAppsPerChart` (type: `integer`):

Maximum apps returned per chart (App Store charts list up to 100 apps, Google Play up to 200).

## `fullDetails` (type: `boolean`):

Search and chart results carry the main fields. Turn this on to open every result's store page for the rest: on Google Play the rating count, ratings histogram, version, dates, release notes, in-app purchases and ads; on the App Store the ratings histogram. Slower: one extra request per app, same price per row. Apps listed under "Apps" always get all fields.

## `dedupeApps` (type: `boolean`):

An app found by several inputs (listed, in a search and in a chart) is returned and charged once, with the context of the first input. Turn off for keyword rank tracking, to get one row per search term and chart position. Reviews are always returned and charged once per run.

## `includeReviews` (type: `boolean`):

Add review rows for the apps listed under "Apps" (or for every app returned, see below). Reviewer names, IDs and avatars are never collected. In review texts and developer replies, email addresses, phone numbers, order / account numbers and @handles are replaced by placeholders such as \[email] (in replies also the developer's own support addresses and helplines; the app's own accounts stay), and the name after a greeting is removed. Review texts also lose social-media profile links and names after "my name is".

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

Maximum reviews per app. Google Play has no fixed limit. The App Store feed gives at most 500 reviews per country and sort order, so add review countries below to get more. App Store reviews are best effort: Apple's feed sometimes answers empty pages; the Actor retries and warns in the status message when reviews may be missing.

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

"Newest" for monitoring, "Most relevant" (Google Play) / "Most helpful" (App Store) for the reviews people see first, "Rating" (Google Play only; the App Store uses newest).

## `reviewCountries` (type: `array`):

More App Store countries to read reviews from after the main country, until "Max reviews per app" is reached, e.g. gb, ca, au. Each country adds up to 500 reviews. Before reading an extra country, Apple's rating count there is looked up (one request per country): an empty feed where the app has 50+ ratings is retried and reported as a warning; where it has few or none, the SUMMARY notes say there are no reviews.

## `reviewsFor` (type: `string`):

"Listed apps" = only apps under "Apps". "Every app" also adds reviews for each search and chart result (can be many reviews).

## `maxConcurrency` (type: `integer`):

How many apps, searches and charts are processed at the same time. Requests stay polite: at most two at a time per store host, a few per second at most.

## `requestTimeoutSecs` (type: `integer`):

Timeout for a single HTTP request.

## `maxRetries` (type: `integer`):

Retries for network errors, HTTP 5xx and throttling (429 / 403), with exponential backoff.

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

Not needed normally: both stores answer Apify's servers directly. If you add a proxy, requests still go out directly first, and a store that blocks or throttles them is switched to this proxy for the rest of the run.

## Actor input object example

```json
{
  "apps": [
    "1232780281",
    "notion.id",
    "https://apps.apple.com/gb/app/spotify-music-and-podcasts/id324684580",
    "https://play.google.com/store/apps/details?id=com.spotify.music"
  ],
  "searchTerms": [
    "habit tracker",
    "budget app"
  ],
  "charts": [
    "top-free",
    "top-grossing"
  ],
  "chartCategories": [
    "games",
    "productivity"
  ],
  "stores": [
    "apple",
    "google"
  ],
  "country": "us",
  "language": "en",
  "maxResultsPerSearch": 5,
  "maxAppsPerChart": 100,
  "fullDetails": false,
  "dedupeApps": true,
  "includeReviews": true,
  "maxReviewsPerApp": 20,
  "reviewSort": "newest",
  "reviewCountries": [
    "gb",
    "ca",
    "au"
  ],
  "reviewsFor": "apps",
  "maxConcurrency": 4,
  "requestTimeoutSecs": 30,
  "maxRetries": 4
}
```

# Actor output Schema

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

All rows as JSON: app rows (type "app") and review rows (type "review") with the same field names for both stores.

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

The "Apps" view: store, title, developer, category, rating, ratings, installs, price, version. Review rows are included with empty columns; keep rows with type "app".

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

The "Reviews" view: app, stars, title, text, date, app version, helpful votes, developer reply. App rows are included with empty columns; keep rows with type "review".

## `summary` (type: `string`):

Per-input counts (apps, reviews, duplicates), errors, warnings (e.g. App Store reviews that may be incomplete), notes and request statistics.

# 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": [
        "notion.id"
    ],
    "searchTerms": [
        "habit tracker"
    ],
    "stores": [
        "apple",
        "google"
    ],
    "country": "us",
    "language": "en",
    "maxResultsPerSearch": 5,
    "includeReviews": true,
    "maxReviewsPerApp": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("fairfetch/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 = {
    "apps": ["notion.id"],
    "searchTerms": ["habit tracker"],
    "stores": [
        "apple",
        "google",
    ],
    "country": "us",
    "language": "en",
    "maxResultsPerSearch": 5,
    "includeReviews": True,
    "maxReviewsPerApp": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("fairfetch/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 '{
  "apps": [
    "notion.id"
  ],
  "searchTerms": [
    "habit tracker"
  ],
  "stores": [
    "apple",
    "google"
  ],
  "country": "us",
  "language": "en",
  "maxResultsPerSearch": 5,
  "includeReviews": true,
  "maxReviewsPerApp": 20
}' |
apify call fairfetch/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,fairfetch/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/ne3TDIHQQHI9x93bV/builds/sbWZwU2rP5U5u4Uab/openapi.json
