# App Store Scraper (Pay-Per-Event) (`prodiger/app-store-scraper`) Actor

Scrape Apple App Store, iTunes, Music, and Podcasts data — app details, ratings, and reviews. Pay-per-event pricing: $0.30 per 1000 apps + $0.10 per 1000 reviews. No monthly fee.

- **URL**: https://apify.com/prodiger/app-store-scraper.md
- **Developed by:** [Arnas](https://apify.com/prodiger) (community)
- **Categories:** E-commerce, Developer tools, AI
- **Features:** Limited permissions
- **Stats:** 12 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.15 / 1,000 app / track scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 Scraper (Pay-Per-Event)

**App Store Scraper** is a fast, HTTP-only crawler that extracts apps, movies, podcasts, music, and reviews from Apple's [App Store](https://www.apple.com/app-store/) and [iTunes Store](https://www.apple.com/itunes/). This is the **Pay-Per-Event** edition — there is no $25/month rental and no minimum spend. **You pay only for the apps and reviews you actually scrape.**

Run it on the Apify platform for scheduling, API access, dataset exports (JSON, CSV, Excel, HTML), proxy rotation, monitoring, and integration with Make, Zapier, and 5000+ other tools.

### What does App Store Scraper do?

Give it App Store URLs (e.g., `https://apps.apple.com/us/app/angry-birds-2/id880047117`), iTunes detail URLs, podcast or music URLs, or a search keyword — the actor hits Apple's **public** `itunes.apple.com/search`, `/lookup`, and `/rss/customerreviews/.../json` endpoints. No API key, no headless browser, no authentication.

It supports two operating modes:

- **Search** — search the App Store / iTunes catalog by keyword + country + media type.
- **Lookup** — fetch one or more specific items by numeric Apple ID.

The scraper extracts:

- **App / track metadata** — title, vendor (`artistName`), genre, average rating, rating count, price, currency, release date, version, file size, supported devices, languages, screenshots, artwork at multiple resolutions.
- **Reviews** (optional) — id, score (1–5), title, body, reviewer username, version, updated date.
- **Cross-media** — works for apps (software), movies, TV shows, podcasts, music albums and songs, music videos, audiobooks, short films, and ebooks.

#### Smarter review pagination — pull ~1000 reviews per app, not ~500

Apple's reviews RSS feed caps each sort order (`sortby`) at roughly 10 pages × 50 reviews = ~500 reviews. This actor runs **two passes** — `sortby=mostrecent` AND `sortby=mosthelpful` — and **de-duplicates by review id**. The result: ~700–1000 unique reviews per popular app, depending on overlap. You're charged once per deduped review, never twice for the same review.

### Why use App Store Scraper?

- **Competitive intelligence** — Track competitor app updates, version bumps, rating drift, and review themes.
- **Market research** — Aggregate app catalogs across categories and countries.
- **Sentiment analysis** — Feed reviews into your LLM or analytics pipeline.
- **ASO (App Store Optimization)** — Surface which keywords match which apps and how rankings shift.
- **Catalog enrichment** — Augment your dataset with Apple-side metadata (icons, descriptions, ratings).

### How to use App Store Scraper

1. **Click "Try for free"** on this actor's page.
2. **Paste one or more Apple URLs** in the Start URLs field, or enter a **Search term**. You can mix any of these in one run:
   - `https://apps.apple.com/us/app/angry-birds-2/id880047117`
   - `https://itunes.apple.com/us/movie/inception/id400763833`
   - `https://podcasts.apple.com/us/podcast/game-dev-unchained/id1043547750`
3. **Enable "Include reviews"** if you want each app's reviews attached. Tune **Max reviews per app** to control review spend.
4. **Set "Max apps per run"** to cap your overall cost.
5. **(Optional)** Set a country, media type filter, or a custom map function.
6. **Click "Save & Start"**. Results stream into the dataset as they're scraped.
7. **Export** as JSON, CSV, Excel, or HTML — or hit the [Dataset API](https://docs.apify.com/api/v2#/reference/datasets) directly.

### Input

| Parameter | Type | Required | Description |
|---|---|---|---|
| `startUrls` | Array | No\* | iTunes / App Store / podcasts / music / books detail URLs. |
| `term` | string | No\* | Search keyword (or numeric ID(s) when `mode=lookup`). |
| `country` | string | No | Two-letter Apple country code. Default `us`. |
| `mediaType` | string | No | One of `all`, `software`, `movie`, `podcast`, `music`, `musicVideo`, `audiobook`, `shortFilm`, `tvShow`, `ebook`. Default `all`. |
| `mode` | string | No | `search` or `lookup`. Default `search`. |
| `includeReviews` | boolean | No | Pull reviews for every scraped app. Default `false`. |
| `maxItems` | integer | No | Hard cap on emitted detail items. `0` = unlimited. Default `100`. |
| `maxReviewsPerApp` | integer | No | Cap reviews per app. `0` = unlimited (effectively ~1000). Default `500`. |
| `endPage` | integer | No | Max search-result pages per source. `0` = unlimited. |
| `customMapFunction` | string | No | JS function body that transforms each item before dataset push. |
| `proxyConfiguration` | object | No | Apify Proxy. Default: Apify Proxy datacenter with AUTO rotation. |

\* You must provide at least one of `startUrls` or `term`.

#### Input examples

| Use case | Input JSON |
|---|---|
| Scrape one app with all reviews | `{ "startUrls": [{ "url": "https://apps.apple.com/us/app/angry-birds-2/id880047117" }], "includeReviews": true, "maxReviewsPerApp": 1000 }` |
| Search "game dev" for podcasts in GB | `{ "term": "game dev", "mediaType": "podcast", "country": "gb", "maxItems": 50 }` |
| Lookup multiple ids at once | `{ "mode": "lookup", "term": "880047117,400763833,1043547750" }` |
| Stay on first 3 search pages only | `{ "term": "calculator", "mediaType": "software", "endPage": 3 }` |
| Custom transform | `{ "term": "todo", "customMapFunction": "return { id: item.trackId, name: item.trackName };" }` |

### Output

All results land in the run's default dataset, one row per app/track. If `includeReviews=true`, each row has a `reviews` array.

#### Sample output

```json
{
    "wrapperType": "software",
    "kind": "software",
    "trackId": 880047117,
    "trackName": "Angry Birds 2",
    "artistName": "Rovio Entertainment Oyj",
    "primaryGenreName": "Games",
    "averageUserRating": 4.5,
    "userRatingCount": 1234567,
    "trackPrice": 0,
    "currency": "USD",
    "trackViewUrl": "https://apps.apple.com/us/app/angry-birds-2/id880047117?uo=4",
    "artworkUrl100": "https://is5-ssl.mzstatic.com/.../100x100bb.jpg",
    "artworkUrl512": "https://is5-ssl.mzstatic.com/.../512x512bb.jpg",
    "releaseDate": "2015-07-27T07:00:00Z",
    "version": "3.18.1",
    "description": "Welcome to the world of Angry Birds 2...",
    "screenshotUrls": ["https://..."],
    "languageCodesISO2A": ["EN", "FR", "DE", "ES"],
    "reviews": [
        {
            "id": "9519350766",
            "userName": "ultrageoffe",
            "userUrl": "https://itunes.apple.com/us/reviews/id212958678",
            "version": "2.3",
            "score": 1,
            "title": "Ad nightmare",
            "text": "Ads every few seconds. Don't waste your time",
            "url": "https://itunes.apple.com/us/review?id=1660464064",
            "updated": "2026-04-12T19:34:21-07:00"
        }
    ],
    "scrapedAt": "2026-05-15T19:00:00.000Z"
}
```

You can download the dataset as JSON, JSONL, CSV, Excel, or HTML.

#### Data table

| Field | Type | Description |
|---|---|---|
| `wrapperType` | string | Apple wrapper (`software`, `track`, `collection`, etc.) |
| `kind` | string | Apple subkind (`software`, `feature-movie`, `podcast`, etc.) |
| `trackId` | integer | Apple numeric id |
| `trackName` | string | Display title |
| `artistName` | string | Developer / publisher / artist |
| `primaryGenreName` | string | Top genre |
| `genres` | string\[] | All assigned genres |
| `averageUserRating` | number | 1.0–5.0 |
| `userRatingCount` | integer | Total rating count |
| `trackPrice` | number | Price in local currency |
| `currency` | string | 3-letter ISO code |
| `releaseDate` | datetime | ISO 8601 |
| `version` | string | App version (software only) |
| `screenshotUrls` | string\[] | iPhone screenshots |
| `ipadScreenshotUrls` | string\[] | iPad screenshots |
| `languageCodesISO2A` | string\[] | Supported languages |
| `reviews` | object\[] | Reviews (when `includeReviews=true`) |
| `scrapedAt` | datetime | ISO 8601 when this row was emitted |

### Pricing / Cost estimation

This actor uses **Pay-Per-Event** pricing. There is no monthly rental. You pay only when records are emitted.

| Event | Fired when | Free tier rate |
|---|---|---|
| `app-scraped` | A detail item is written to the dataset | **$0.0003 / item** (i.e. $0.30 per 1000) |
| `review-scraped` | A review row is written into an app's `reviews` array | **$0.0001 / review** ($0.10 per 1000) |

Higher Apify subscription tiers receive lower per-event rates — see the **Pricing** tab on this actor's listing for the current rate table.

**Cost examples (Free tier):**

| Workload | Apps | Reviews | Cost |
|---|---|---|---|
| One app + 100 reviews | 1 | 100 | **$0.0103** |
| 100 apps, no reviews | 100 | 0 | **$0.030** |
| 500 apps, 200 reviews each | 500 | 100,000 | **$10.15** |
| 10,000 apps, no reviews | 10,000 | 0 | **$3.00** |

#### How does this compare to $25/month App Store actors?

The most popular App Store actor on Apify Store charges **$25/month + usage**. This actor charges **$0.30 per 1000 apps + $0.10 per 1000 reviews** with **no monthly fee**. For workloads under ~80,000 items per month, PPE is strictly cheaper — and for one-off jobs (a few hundred items) the cost is **pennies**, instead of $25.

You also get:

- **Streaming output** — results appear in your dataset within seconds, not after a long batch.
- **Up to ~1000 reviews per app** via dual-sortby pagination (vs ~500 in subscription competitors).
- **No headless browser** — pure HTTP, so runs are 10× faster and cheaper in compute.

### Tips and advanced options

- **Lookup mode is fastest.** If you have numeric IDs, set `mode=lookup` and pass them in `term` (comma-separated) or as the `id...` portion of URLs in `startUrls`. One HTTP request returns up to 100 items.
- **Country matters for reviews.** Reviews are country-specific. To see UK reviews vs. US reviews, run the actor twice with different `country` values.
- **`mediaType=all` is broad.** Set a specific media type (`software`, `podcast`, etc.) to narrow search hits and avoid surprises.
- **Use `endPage`** to cap search-result pagination for shallow scans.
- **`customMapFunction`** runs in the actor container. It receives the raw Apple item shape; return a transformed object. Errors fall back to the unmapped item.

### Errors and partial failures

Bad input (malformed URL, non-existent ID, network error on one app) does **not** abort the run. Each error is captured to a sidecar key-value store entry named `errors-YYYY-MM-DD.jsonl`. Check the **Key-value stores** tab on your run for this file.

### FAQ, disclaimers, and support

**Is this legal?** Scraping public App Store metadata is generally legal in most jurisdictions, but you are responsible for complying with Apple's Terms of Service, copyright on screenshots/icons/copy, and applicable data-protection laws (GDPR, CCPA). Don't scrape personal data, and don't use reviewer names in ways that violate the reviewer's rights.

**Why are some reviews missing?** Apple's reviews RSS feed has a hard cap around 10 pages per sort order. This actor runs two sort orders (mostrecent + mosthelpful) and de-duplicates to get past ~500 — but Apple ultimately decides what's available.

**Why are some fields null?** Apple returns different field sets for different `wrapperType` values. A podcast row won't have `screenshotUrls`; a song row won't have `supportedDevices`. This is normal.

**How do I report bugs?** Open an issue on this actor's **Issues** tab on the Apify Store.

# Changelog

This Actor's version history is a separate document: https://apify.com/prodiger/app-store-scraper/changelog.md

# Actor input Schema

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

Apple detail URLs to scrape. Accepts apps.apple.com, itunes.apple.com, podcasts.apple.com, music.apple.com, and books.apple.com URLs. Mix any combination. If left empty, you must provide a search term instead.

## `term` (type: `string`):

Keyword to search across the App Store / iTunes catalog. In lookup mode this should be a numeric ID instead. Either this or startUrls is required.

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

Two-letter country code for the storefront to query. Default: us.

## `mediaType` (type: `string`):

Limit results to a single media category. Default: all.

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

Search returns matches for the term. Lookup expects a numeric ID and returns the single matching record. Default: search.

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

If enabled, the actor fetches reviews for every scraped app. Reviews are de-duplicated across two sortby passes (mostrecent + mosthelpful) so you typically get up to ~1000 reviews per app instead of the default ~500 cap. Each review is charged separately.

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

Maximum number of detail records (apps, movies, podcasts, etc.) to emit per run. Set to 0 for unlimited. Default: 100.

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

Cap on reviews emitted per app when includeReviews=true. Apple paginates ~50 reviews per page and caps each sortby at ~10 pages (~500 reviews). This actor runs two sortby passes (mostrecent + mosthelpful) and de-duplicates, so values up to ~1000 are practical. Set to 0 for unlimited.

## `endPage` (type: `integer`):

Caps the number of search-result pages walked per startUrl / term. Apple's search endpoint returns up to 200 results per page. Set to 0 for unlimited. Default: 0.

## `customMapFunction` (type: `string`):

Optional JavaScript function body that transforms each scraped item before it is pushed to the dataset. Receives the item as argument `item` and must return the modified item. Errors are logged and the unmodified item is emitted. Example: `return { trackId: item.trackId, name: item.trackName };`

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

Apify Proxy configuration. Default: Apify Proxy with AUTO rotation (datacenter). Apple's iTunes endpoints rate-limit per IP, so proxy rotation is recommended for runs over a few hundred items.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://apps.apple.com/us/app/angry-birds-2/id880047117"
    },
    {
      "url": "https://podcasts.apple.com/us/podcast/game-dev-unchained/id1043547750"
    }
  ],
  "country": "us",
  "mediaType": "all",
  "mode": "search",
  "includeReviews": false,
  "maxItems": 100,
  "maxReviewsPerApp": 500,
  "endPage": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Scraped apps and (optionally) their reviews.

# 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 = {
    "startUrls": [
        {
            "url": "https://apps.apple.com/us/app/angry-birds-2/id880047117"
        },
        {
            "url": "https://podcasts.apple.com/us/podcast/game-dev-unchained/id1043547750"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("prodiger/app-store-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 = {
    "startUrls": [
        { "url": "https://apps.apple.com/us/app/angry-birds-2/id880047117" },
        { "url": "https://podcasts.apple.com/us/podcast/game-dev-unchained/id1043547750" },
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("prodiger/app-store-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 '{
  "startUrls": [
    {
      "url": "https://apps.apple.com/us/app/angry-birds-2/id880047117"
    },
    {
      "url": "https://podcasts.apple.com/us/podcast/game-dev-unchained/id1043547750"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call prodiger/app-store-scraper --silent --output-dataset

```

## MCP server setup

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