# App Stores Scraper - Apple App Store & Google Play ASO (`crawloop/app-stores-scraper`) Actor

Scrape Apple App Store and Google Play in one run: app details, reviews, keyword search, top charts, ASO keyword ranks, developer catalogs, and monitors. Cross-store app pairs, multi-country. Fast HTTP — no browser.

- **URL**: https://apify.com/crawloop/app-stores-scraper.md
- **Developed by:** [Andrej Kiva](https://apify.com/crawloop) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.25 / 1,000 app / listing / rank rows

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## App Stores Scraper — Apple App Store & Google Play ASO

> Unofficial tool for publicly accessible Apple App Store / iTunes and Google Play Store data. Apple, App Store, Google Play, and related trademarks belong to their respective owners. Not affiliated with, sponsored by, or endorsed by Apple Inc. or Google LLC. Provided for informational use only; users must comply with applicable terms and laws.

**App Stores Scraper** ◄── you are here

| Actor | Platform |
| :--- | :--- |
| **App Stores Scraper** ◄── you are here | Apple App Store + Google Play (combo) |
| [Apple App Store Scraper](https://apify.com/crawloop/apple-app-store-scraper) | iOS / App Store only |
| [Google Play Store Scraper](https://apify.com/crawloop/google-play-scraper) | Android / Google Play only |

**App Store and Google Play scraper** for Apify — one Actor for **cross-platform ASO**: **app metadata**, **customer reviews**, **keyword search**, **top charts**, **ASO keyword ranks**, **developer catalogs**, and **listing monitors**. Scrape **Apple App Store and Google Play in a single run**, or either store alone. Map the same product with **app pairs** (`pairKey` on every row). Fast HTTP — **no browser**.

Use it for dual-store competitor research, ASO rank tracking, review mining, release / price / rating monitors, and agency reporting when you need iOS + Android without two Actor jobs. For deepest single-store options, use the sibling Actors above.

### When to use this Actor

- Track the **same app on iOS + Android** (details, ranks, reviews) with `appPairs`
- **ASO keyword ranks** across both stores and countries / locales
- Competitor cards: version, price, ratings, App Privacy / Data safety
- Top charts + keyword discovery for App Store and Google Play in one job
- Review intelligence from both marketplaces
- Scheduled **monitor** for version / price / rating / listing deltas
- One Actor ID in automations instead of separate Apple and Play pipelines

### Modes

| Mode | Apple App Store | Google Play |
| :--- | :--- | :--- |
| `app_details` | iTunes lookup (+ optional HTML enrich) | Full metadata (+ similar / permissions / data safety) |
| `reviews` | Customer reviews RSS (~500/country) | Paginated reviews (sort / stars / after-date) |
| `search` | Keyword search | Keyword search |
| `top_charts` | Top Free / Paid / Grossing / New | Top free / paid / grossing (+ category) |
| `search_and_charts` | Search + charts | Search + charts |
| `developer_apps` | Artist / developer catalog | Developer page listings |
| `keyword_ranks` | SERP position + KV history + RANK\_CHANGE | SERP position + KV history |
| `monitor` | Version / price / rating / privacy / IAP deltas | Version / price / rating / listing + review watch + webhook |

### Key features

- **Both stores, one run** — `stores: ["apple","google"]` (default) or either alone
- **App pairs for ASO** — `appPairs: [{ name, appleId, googleId }]` stamps `pairKey` for joins
- **ASO keyword ranks** — track App Store IDs and Play packages across keywords × countries
- **Fair `maxItems` split** — when both stores run, the global cap is split evenly (`balanceStoreLimits`); optional `maxItemsPerStore`
- **Parallel pipelines** — `runStoresInParallel` (default true)
- **Apple HTML enrich** — privacy nutrition labels, IAP, rating histogram
- **Play extras** — similar apps, permissions, Data safety, monitor webhooks
- **Typed rows** — every item has `store` plus `itemType` / native fields
- **Monitor state** — separate KV namespaces per store under `monitorStoreName`

### Input parameters

| Parameter | Description |
|-----------|-------------|
| `mode` | See modes table |
| `stores` | `apple`, `google`, or both (default both) |
| `appPairs` | Cross-store ASO pairs (`name` / `appleId` / `googleId`) |
| `startUrls` | App Store or Play URLs (store auto-detected) |
| `appleAppIds` / `googleAppIds` | Explicit track IDs / package names |
| `searchTerms` / `searchTerm` | Keywords for search and keyword ranks |
| `trackAppleAppIds` / `trackGoogleAppIds` | Apps to locate in SERP (`keyword_ranks`) |
| `country` / `countries` / `useAllCountries` | Storefronts (Apple majors preset supported) |
| `language` / `languages` | Play `hl` (+ multi-locale matrix with countries) |
| `appleChartType` / `googleChartType` / `chartLimit` | Top charts |
| `appleGenreId` / `googleCategory` | Category / genre filters |
| `enrichFromHtml` / `enrichChartDetails` | Apple HTML / chart enrich |
| `includeSimilarApps` / `includePermissions` / `includeDataSafety` | Play detail extras |
| `enrichListings` / `enrichDeveloperApps` / `maxSearchPages` | Play listing enrich |
| `maxReviewsPerApp` / `appleReviewSort` / `googleReviewSort` | Reviews |
| `reviewScoreFilter` / `reviewAfterDate` | Play review filters |
| `maxItems` | Global row cap (auto-split across stores when both run) |
| `maxItemsPerStore` | Optional hard cap per store |
| `balanceStoreLimits` | Even split of `maxItems` (default true) |
| `monitorStoreName` / `monitorBaselineOnly` / `resetMonitorState` | Monitor / rank KV |
| `webhookUrl` / `notifyOn*` | Play monitor alerts |
| `concurrency` / `maxRequestsPerSecond` | Parallelism and Apple RPS |
| `runStoresInParallel` | Scrape Apple and Google concurrently |
| `proxyConfiguration` | Residential recommended for Google Play |

#### Example — dual-store app details (ASO competitor pack)

```json
{
  "mode": "app_details",
  "stores": ["apple", "google"],
  "appPairs": [
    {
      "name": "WhatsApp",
      "appleId": "310633997",
      "googleId": "com.whatsapp"
    }
  ],
  "countries": ["us", "de"],
  "enrichFromHtml": true,
  "includeDataSafety": true,
  "includeSimilarApps": true,
  "maxItems": 50
}
```

#### Example — ASO keyword ranks (both stores)

```json
{
  "mode": "keyword_ranks",
  "stores": ["apple", "google"],
  "searchTerms": ["vpn", "secure vpn", "proxy"],
  "appPairs": [
    {
      "name": "CompetitorA",
      "appleId": "123456789",
      "googleId": "com.example.vpn"
    }
  ],
  "countries": ["us", "gb", "de"],
  "languages": ["en"],
  "monitorStoreName": "my-aso-ranks",
  "maxItems": 200
}
```

#### Example — reviews from App Store + Google Play

```json
{
  "mode": "reviews",
  "stores": ["apple", "google"],
  "appPairs": [
    {
      "name": "WhatsApp",
      "appleId": "310633997",
      "googleId": "com.whatsapp"
    }
  ],
  "maxReviewsPerApp": 100,
  "countries": ["us"],
  "appleReviewSort": "mostRecent",
  "googleReviewSort": "NEWEST",
  "maxItems": 400
}
```

#### Example — top charts both stores

```json
{
  "mode": "top_charts",
  "stores": ["apple", "google"],
  "countries": ["us"],
  "languages": ["en"],
  "appleChartType": "top_free",
  "appleGenreId": 6014,
  "googleChartType": "topselling_free",
  "googleCategory": "GAME",
  "chartLimit": 100,
  "maxItems": 200
}
```

#### Example — monitor (baseline then schedule)

```json
{
  "mode": "monitor",
  "stores": ["apple", "google"],
  "appPairs": [
    {
      "name": "Spotify",
      "appleId": "324684580",
      "googleId": "com.spotify.music"
    }
  ],
  "countries": ["us"],
  "monitorStoreName": "app-stores-aso-monitor",
  "monitorBaselineOnly": true,
  "enrichFromHtml": true,
  "maxItems": 100
}
```

After the baseline run, set `monitorBaselineOnly` to `false` (or omit it) so later runs push only change events.

### Output

| Field | Description |
|-------|-------------|
| `store` | `apple` or `google` |
| `itemType` / `recordType` | `app`, `review`, `keyword_rank`, `chart_entry`, `search_result`, `monitor_event`, … |
| `pairKey` | Cross-store pair name when provided in `appPairs` |
| `appId` | Apple track ID or Google package name |
| `title` / `developerName` | Listing title and publisher |
| `score` / `rating` | Average rating (mirrored for cross-store tables) |
| `price` / `currency` / `version` | Commercial / version metadata |
| `keyword` / `rank` / `found` | ASO SERP / chart position |
| `country` / `language` | Storefront / Play locale |
| `eventType` / `changeType` | Monitor deltas |
| `url` | Store listing URL when present |
| `scrapedAt` | UTC collection time |

### Limitations

- Apple reviews RSS is a **recent window** (~500 per country per sort), not full history.
- Apple keyword ranks / search stop at the **top 200** per term per country.
- Google Play developer pages and chart RPCs can return **fewer apps than your store quota** — total rows may be below `maxItems`.
- With both stores and `balanceStoreLimits`, `maxItems` is **split evenly**; one store cannot consume the whole cap.
- Play version may be missing (`Varies with device`); monitor prefers update timestamps when present.
- Residential proxy is recommended for non-US Play locales and larger review volumes.

### Workflow tips

1. Validate IDs with `app_details` + `appPairs` (check `pairKey` in the dataset).
2. Seed ASO with `keyword_ranks` + a dedicated `monitorStoreName`, then schedule daily.
3. Run `monitor` once with `monitorBaselineOnly: true`, then schedule deltas.
4. Cap cost with `maxItems` while testing (auto-splits across stores; or set `maxItemsPerStore`).
5. Prefer Residential proxy for Google Play; Apple iTunes JSON often works without proxy.

### Related Actors

| Actor | What it covers |
| :--- | :--- |
| **App Stores Scraper** ◄── you are here | Apple App Store + Google Play combo ASO suite |
| [Apple App Store Scraper](https://apify.com/crawloop/apple-app-store-scraper) | iOS apps, games, reviews, ASO ranks, charts, monitor |
| [Google Play Store Scraper](https://apify.com/crawloop/google-play-scraper) | Android apps, reviews, ASO ranks, monitor + webhooks |

# Actor input Schema

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

ASO / intelligence mode. Runs on every selected store that supports the inputs you provide.

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

Which stores to scrape this run. Empty = both (apple + google).

## `startUrls` (type: `array`):

App Store or Google Play URLs (apps, developers, search). Store is auto-detected from the host.

## `appPairs` (type: `array`):

Map the same product on both stores. Each object: name/pairKey, appleId (numeric), googleId (package). Rows get pairKey stamped.

## `appleAppIds` (type: `array`):

Numeric App Store track IDs (e.g. 310633997).

## `googleAppIds` (type: `array`):

Android package names (e.g. com.whatsapp).

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

Mixed IDs: numeric → Apple, package-like → Google. Prefer appleAppIds / googleAppIds / appPairs when possible.

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

Keywords for search, search\_and\_charts, and keyword\_ranks.

## `searchTerm` (type: `string`):

Optional single keyword merged into searchTerms.

## `trackAppleAppIds` (type: `array`):

Apple apps to locate in SERP for keyword\_ranks. Defaults to appleAppIds / pairs.

## `trackGoogleAppIds` (type: `array`):

Play packages to locate in SERP for keyword\_ranks. Defaults to googleAppIds / pairs.

## `appleDeveloperIds` (type: `array`):

Numeric artist / developer IDs for Apple developer\_apps.

## `googleDeveloperId` (type: `string`):

Play developer page id (e.g. Spotify AB).

## `googleDeveloperUrl` (type: `string`):

Full Play developer URL (overrides googleDeveloperId).

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

Primary storefront / gl (ISO-2).

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

Multi-country storefronts. Use "all" for Apple major-storefronts preset. For Play, combined with languages.

## `useAllCountries` (type: `boolean`):

Apple only: scrape the built-in major storefronts list (~44).

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

Primary Google Play language.

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

Optional Play hl codes × countries (cartesian, capped).

## `appleChartType` (type: `string`):

App Store top charts feed.

## `googleChartType` (type: `string`):

Play top chart collection.

## `appleGenreId` (type: `integer`):

Optional App Store genre for charts (e.g. 6014 Games).

## `googleCategory` (type: `string`):

Play category id (e.g. GAME\_ACTION, MUSIC\_AND\_AUDIO).

## `chartLimit` (type: `integer`):

Max chart positions (Apple up to 200).

## `enrichFromHtml` (type: `boolean`):

Fetch apps.apple.com for subtitle, privacy labels, IAP, rating histogram.

## `enrichChartDetails` (type: `boolean`):

Re-lookup each Apple chart app for full metadata.

## `includeSimilarApps` (type: `boolean`):

In app\_details, emit similar\_app rows.

## `similarLimit` (type: `integer`):

Max similar\_app rows per source app.

## `includePermissions` (type: `boolean`):

In app\_details, emit permission rows.

## `includeDataSafety` (type: `boolean`):

In app\_details, emit data\_safety row.

## `enrichListings` (type: `boolean`):

Fetch full details for each Play search/chart hit.

## `enrichDeveloperApps` (type: `boolean`):

Fetch full details for each Play developer listing.

## `maxSearchPages` (type: `integer`):

Paginate Play search (1–10).

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

Per app (and per Apple country). Apple RSS hard-caps ~500.

## `appleReviewSort` (type: `string`):

App Store RSS review sort order.

## `googleReviewSort` (type: `string`):

Google Play review sort order.

## `reviewScoreFilter` (type: `integer`):

0 = all; 1–5 = only that star rating.

## `reviewAfterDate` (type: `string`):

ISO date/time. Skip older reviews when sorting NEWEST.

## `rankHistoryLimit` (type: `integer`):

Past keyword-rank snapshots to keep per app×keyword×country.

## `emitRankChanges` (type: `boolean`):

Apple keyword\_ranks: also push RANK\_CHANGE monitor events.

## `monitorStoreName` (type: `string`):

Named Key-Value Store prefix. Apple and Google use separate keys inside.

## `monitorBaselineOnly` (type: `boolean`):

Seed fingerprints / ranks without emitting deltas.

## `resetMonitorState` (type: `boolean`):

Clear stored fingerprints / rank history before this run.

## `monitorFetchReviews` (type: `boolean`):

In monitor mode on Google, also pull newest reviews.

## `webhookUrl` (type: `string`):

Optional Slack/Discord/custom HTTPS webhook for Play monitor events.

## `notifyOnNewVersion` (type: `boolean`):

Fire Play webhook when version or update timestamp changes.

## `notifyOnRatingDrop` (type: `boolean`):

Fire Play webhook when score drops by ratingDropThreshold or more.

## `notifyOnPriceChange` (type: `boolean`):

Fire Play webhook when price or currency changes.

## `notifyOnNegativeReviews` (type: `boolean`):

Fire Play webhook for new reviews with score <= 2.

## `notifyOnListingChange` (type: `boolean`):

Fire Play webhook when title, summary, or icon changes.

## `ratingDropThreshold` (type: `number`):

Minimum Play score drop to emit rating\_drop.

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

Hard cap on dataset rows for the whole run (0 = unlimited). When both stores run and balanceStoreLimits is on, this total is split evenly (e.g. 100 → 50 Apple + 50 Google).

## `maxItemsPerStore` (type: `integer`):

Optional per-store cap. When set, each selected store may push up to this many rows (global maxItems still applies). 0 = auto-split maxItems across stores.

## `balanceStoreLimits` (type: `boolean`):

When both Apple and Google run and maxItemsPerStore is 0, split maxItems evenly so one store cannot consume the whole quota.

## `concurrency` (type: `integer`):

Parallel workers per store pipeline.

## `maxRequestsPerSecond` (type: `number`):

Client-side rate limit for iTunes / RSS calls.

## `runStoresInParallel` (type: `boolean`):

When both stores selected, scrape Apple and Google concurrently.

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

Apify Proxy recommended for Google Play (defaults to RESIDENTIAL when enabled). Usually optional for Apple iTunes JSON.

## Actor input object example

```json
{
  "mode": "app_details",
  "stores": [
    "apple",
    "google"
  ],
  "startUrls": [
    {
      "url": "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
    },
    {
      "url": "https://play.google.com/store/apps/details?id=com.whatsapp"
    }
  ],
  "appPairs": [
    {
      "name": "WhatsApp",
      "appleId": "310633997",
      "googleId": "com.whatsapp"
    }
  ],
  "appleAppIds": [],
  "googleAppIds": [],
  "appIds": [],
  "searchTerms": [
    "vpn",
    "photo editor"
  ],
  "searchTerm": "",
  "trackAppleAppIds": [],
  "trackGoogleAppIds": [],
  "appleDeveloperIds": [],
  "googleDeveloperId": "",
  "googleDeveloperUrl": "",
  "country": "us",
  "countries": [],
  "useAllCountries": false,
  "language": "en",
  "languages": [],
  "appleChartType": "top_free",
  "googleChartType": "topselling_free",
  "googleCategory": "",
  "chartLimit": 100,
  "enrichFromHtml": false,
  "enrichChartDetails": false,
  "includeSimilarApps": false,
  "similarLimit": 40,
  "includePermissions": false,
  "includeDataSafety": false,
  "enrichListings": false,
  "enrichDeveloperApps": false,
  "maxSearchPages": 3,
  "maxReviewsPerApp": 100,
  "appleReviewSort": "mostRecent",
  "googleReviewSort": "NEWEST",
  "reviewScoreFilter": 0,
  "reviewAfterDate": "",
  "rankHistoryLimit": 30,
  "emitRankChanges": true,
  "monitorStoreName": "app-stores-aso-state",
  "monitorBaselineOnly": false,
  "resetMonitorState": false,
  "monitorFetchReviews": true,
  "webhookUrl": "",
  "notifyOnNewVersion": true,
  "notifyOnRatingDrop": true,
  "notifyOnPriceChange": true,
  "notifyOnNegativeReviews": true,
  "notifyOnListingChange": true,
  "ratingDropThreshold": 0.2,
  "maxItems": 0,
  "maxItemsPerStore": 0,
  "balanceStoreLimits": true,
  "concurrency": 6,
  "maxRequestsPerSecond": 8,
  "runStoresInParallel": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Default dataset items from one or both app stores.

# 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 = {
    "stores": [
        "apple",
        "google"
    ],
    "startUrls": [
        {
            "url": "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
        },
        {
            "url": "https://play.google.com/store/apps/details?id=com.whatsapp"
        }
    ],
    "appPairs": [
        {
            "name": "WhatsApp",
            "appleId": "310633997",
            "googleId": "com.whatsapp"
        }
    ],
    "searchTerms": [
        "vpn",
        "photo editor"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawloop/app-stores-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 = {
    "stores": [
        "apple",
        "google",
    ],
    "startUrls": [
        { "url": "https://apps.apple.com/us/app/whatsapp-messenger/id310633997" },
        { "url": "https://play.google.com/store/apps/details?id=com.whatsapp" },
    ],
    "appPairs": [{
            "name": "WhatsApp",
            "appleId": "310633997",
            "googleId": "com.whatsapp",
        }],
    "searchTerms": [
        "vpn",
        "photo editor",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("crawloop/app-stores-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "stores": [
    "apple",
    "google"
  ],
  "startUrls": [
    {
      "url": "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
    },
    {
      "url": "https://play.google.com/store/apps/details?id=com.whatsapp"
    }
  ],
  "appPairs": [
    {
      "name": "WhatsApp",
      "appleId": "310633997",
      "googleId": "com.whatsapp"
    }
  ],
  "searchTerms": [
    "vpn",
    "photo editor"
  ]
}' |
apify call crawloop/app-stores-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=crawloop/app-stores-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/ztyPcl4lQI033xfZI/builds/OSjZFcdbXxXAoLGUR/openapi.json
