# App Store Scraper — Reviews, Ratings & App Details (`tortuga/appstore-scraper`) Actor

Scrape Apple App Store reviews (full history, developer responses, any country), app details with rating histogram and version history, and keyword search results.

- **URL**: https://apify.com/tortuga/appstore-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** Developer tools, E-commerce, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 reviews

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 — Reviews, Ratings & App Details

Scrape **Apple App Store reviews** for any iPhone, iPad or Mac app, in any of the 175 App Store countries, **without the 500-review limit** of Apple's RSS feed. Every review comes with its rating, title, text, date, the reviewer's nickname, the **developer's response** (when there is one) and, for the newest 500 per country, the **app version** and helpful votes.

The same actor also collects **app details** (price, rating, **rating histogram**, version history, size, genres, in-app purchases, privacy labels, description, chart rank) and **App Store search results** for any keyword. No login, no API key, no browser.

### What can this App Store scraper do?

- **Reviews, full history**: newest first or most helpful first, 20 per request, as deep as the app's review history goes (tested to review number 200,000 of WhatsApp in the US store).
- **Developer responses**: the reply text, its date and ID, next to the review.
- **App version and votes**: `appVersion`, `helpfulVotes` and `totalVotes` for the first 500 reviews per country (Apple publishes these only for that window).
- **Many countries in one run**: list country codes, or enter `all` to cover all 175 storefronts. Each country has its own reviews.
- **Only new reviews**: set **Only reviews newer than** and the run stops at the first older review, so daily monitoring costs a few cents.
- **App details**: one row per app and country, with the 1-5 star histogram, the last 25 versions with release notes, size in bytes, age rating, in-app purchase prices, privacy labels, similar apps and more.
- **Keyword search**: up to 200 apps per keyword and country, in App Store order, for iPhone, iPad or Mac.

### How to scrape App Store reviews

1. Paste App Store links, numeric app IDs (`310633997`) or bundle IDs (`net.whatsapp.WhatsApp`) into **Apps**.
2. Choose the **Countries** (for example `us`, `gb`, `de`) or enter `all`.
3. Pick **Review order** (Most recent or Most helpful) and set **Max items** to cap the cost.
4. Click **Start**. Download the reviews as JSON, CSV or Excel from the **Dataset** tab, or read them through the API.

#### Input example: reviews

```json
{
  "mode": "reviews",
  "apps": ["https://apps.apple.com/us/app/whatsapp-messenger/id310633997", "1438388363"],
  "countries": ["us", "gb", "de"],
  "sort": "mostRecent",
  "maxReviewsPerCountry": 1000,
  "maxItems": 6000
}
```

#### Output example: review

```json
{
  "type": "review",
  "appId": "310633997",
  "appName": "WhatsApp Messenger",
  "country": "us",
  "reviewId": "14629143037",
  "rating": 5,
  "title": "Call",
  "text": "Good",
  "date": "2026-10-05T03:16:05Z",
  "isEdited": false,
  "userName": "Maxwell 35",
  "reviewerUrl": "https://itunes.apple.com/us/reviews/id1184979948",
  "appVersion": "26.38.74",
  "helpfulVotes": 0,
  "totalVotes": 0,
  "developerResponse": null,
  "developerResponseDate": null,
  "developerResponseId": null,
  "appUrl": "https://apps.apple.com/us/app/id310633997"
}
```

#### Review fields

| Field | Description |
|---|---|
| `appId`, `appName`, `country` | App Store ID, app name and the two-letter country of the storefront the review was written in |
| `reviewId` | Apple's review ID (unique, stable) |
| `rating` | Stars, 1-5 |
| `title`, `text` | Review headline and body |
| `date` | When the review was posted or last edited (UTC, ISO 8601) |
| `isEdited` | `true` if the reviewer edited the review |
| `userName` | The nickname the reviewer shows on the App Store |
| `reviewerUrl` | Apple's public link to the reviewer's reviews (first 500 per country) |
| `appVersion` | App version the review was written for (first 500 per country) |
| `helpfulVotes`, `totalVotes` | "Helpful" votes and total votes (first 500 per country) |
| `developerResponse`, `developerResponseDate`, `developerResponseId` | The developer's public reply, when it exists |
| `appUrl` | App Store page of the app in that country |

### How to get App Store app details

Set **Mode** to `details`. You get one row per app and country.

```json
{ "mode": "details", "apps": ["310633997", "425073498"], "countries": ["us", "jp"] }
```

| Field | Description |
|---|---|
| `name`, `subtitle`, `url`, `bundleId` | App identity |
| `developer`, `developerId`, `developerUrl`, `seller`, `sellerUrl` | Who publishes the app |
| `price`, `currency`, `priceFormatted`, `isFree` | Price in that country's currency |
| `hasInAppPurchases`, `inAppPurchases` | Top in-app purchases with name, price, subscription period |
| `rating`, `ratingCount`, `ratingHistogram` | Average stars, number of ratings, and ratings per star (`{"1": ..., "5": ...}`) |
| `ratingForCurrentVersion`, `ratingCountForCurrentVersion` | Same for the current version |
| `primaryGenre`, `genres`, `contentRating`, `contentAdvisories` | Category and age rating |
| `chartPosition`, `chartName`, `chartGenre` | Current top-chart rank, when the app is charting |
| `version`, `currentVersionReleaseDate`, `releaseNotes`, `versionHistory` | Current version and the last 25 versions with dates and notes |
| `releaseDate`, `sizeBytes`, `minimumOsVersion`, `requirements`, `deviceFamilies`, `languages` | Technical data |
| `description`, `editorialNote` | Store description and Apple's editorial note |
| `privacyLabels` | App privacy labels (data linked to you, not linked, tracking) |
| `websiteUrl`, `supportUrl`, `privacyPolicyUrl`, `copyright` | Links from the store page |
| `iconUrl`, `screenshots` | Image URLs |
| `similarAppIds` | "You might also like" app IDs |

Output excerpt:

```json
{
  "type": "app",
  "appId": "310633997",
  "country": "us",
  "name": "WhatsApp Messenger",
  "developer": "WhatsApp Inc.",
  "price": 0.0,
  "currency": "USD",
  "rating": 4.7,
  "ratingCount": 18723283,
  "ratingHistogram": { "1": 643496, "2": 254151, "3": 566911, "4": 1454223, "5": 15804502 },
  "primaryGenre": "Social Networking",
  "contentRating": "12+",
  "chartPosition": 2,
  "version": "26.39.74",
  "versionHistory": [
    { "version": "26.39.74", "releaseDate": "2026-10-06T09:33:53Z", "releaseNotes": "We update the app regularly..." }
  ],
  "releaseDate": "2009-05-04",
  "sizeBytes": 391110656,
  "inAppPurchases": [
    { "name": "No ads (1 account)", "price": 4.0, "priceFormatted": "$4.00", "isSubscription": true, "subscriptionPeriod": "P1M" }
  ]
}
```

### How to scrape App Store search results

Set **Mode** to `search` and add keywords. Each keyword returns up to 200 apps per country, ranked as in the App Store, with `searchTerm`, `position`, `appId`, `name`, `developer`, `price`, `rating`, `ratingCount`, `genres`, `version`, `releaseDate`, `sizeBytes`, `description`, `iconUrl`, `screenshots` and `url`. Feed the IDs back into `details` mode when you need histograms and version history.

```json
{ "mode": "search", "searchTerms": ["habit tracker"], "countries": ["us", "gb"], "searchPlatform": "iphone", "maxItems": 400 }
```

### Monitor changes on a schedule

Turn on **Only new or changed items** and save the input as a task with a Schedule (Apify Console: **Saved tasks** -> **Schedule**, for example daily). Each run remembers what earlier runs of the same input returned and outputs only what is new: reviews not seen before in `reviews` mode (read newest first, one page at a time, stopping where the previous run's reviews begin), apps whose price, version or star rating changed in `details` mode, and apps that are new in the results or changed price or version in `search` mode. Unchanged items are skipped before they are saved, so they are not charged. **Only reviews newer than** also takes a period such as `2 days` (counted back from each run), which bounds how far back the first run reads. Monitored items carry `changeType` (`new` or `changed`) and, for changed apps, `previousValues` with the old values of the fields that changed (e.g. `{"version": "2.24"}`); use **Monitoring key** to keep several schedules on the same input apart.

### How many App Store reviews can I get per app?

All the written reviews Apple keeps for the app in each country. Apple's RSS feed, which most review scrapers use, stops at 500 reviews per country; this actor reads the same review service the App Store website uses, 20 reviews per request, with no fixed limit. Note that most ratings are stars without text: an app with 18 million ratings has far fewer written reviews. For more reviews, add more countries.

Two details apply only to the first 500 reviews per country, because Apple publishes them only in the RSS feed: `appVersion` and the vote counts. Older reviews keep those fields empty. Turn off **Add app version and helpful votes** to skip the extra requests.

### Can I get the developer's replies to reviews?

Yes. `developerResponse` holds the reply text, with `developerResponseDate` and `developerResponseId`. Reviews without a reply have `null`.

### Does it work without login or an API key?

Yes. It reads public App Store data that anyone can see without an Apple ID. The default Apify datacenter proxy is enough; the actor spreads requests across IPs because Apple limits requests per IP.

### How much does it cost to scrape App Store reviews?

You pay per result: **$1.20 per 1,000 reviews**, **$3.00 per 1,000 app detail rows**, **$1.00 per 1,000 search results**, plus Apify platform usage, which was under $0.01 per 1,000 reviews in our tests (3,000 reviews: $0.018). There is no start fee. **Max items** caps every run, and **Only reviews newer than** lets you pay only for new reviews when you monitor an app.

### Integrations and API

Run it from Zapier, Make, n8n or Google Sheets, or call it from Python or Node with the Apify client. Schedule it daily with **Only reviews newer than** to track new reviews and developer replies. See the **API** tab for ready-made snippets.

### Is it legal to scrape the App Store?

This actor collects only data that the App Store shows publicly to every visitor without an account: app pages, search results, and reviews with the nickname each reviewer chose to publish. It does not log in and does not collect anything private. You are responsible for how you use the data, including data-protection law (for example GDPR) when you process reviewer nicknames, and for respecting Apple's terms.

### Support

Found a bug or need a field? Open an issue in the **Issues** tab; it is usually answered within a day.

# Actor input Schema

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

What to scrape. `reviews`: user reviews of each app in **Apps**, in each country. `details`: one app item per app and country (price, rating histogram, version history, size, privacy labels and more). `search`: App Store search results for each keyword in **Search terms**.

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

App Store URLs, numeric app IDs or bundle IDs, one per line: `https://apps.apple.com/us/app/whatsapp-messenger/id310633997`, `310633997`, `net.whatsapp.WhatsApp`. Used by the `reviews` and `details` modes.

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

Keywords for the `search` mode, one per line, e.g. `habit tracker`. Apple returns up to 200 apps per keyword and country, in App Store search order.

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

Two-letter App Store country codes, e.g. `us`, `gb`, `de`, `jp`, `br`. Each country has its own reviews, prices and search results. Enter `all` for all 175 storefronts. Leave empty to use the country in each app URL (or `us`).

## `sort` (type: `string`):

`mostRecent`: newest first (default). `mostHelpful`: Apple's most-helpful order. Both reach the full review history, not only the 500 that the RSS feed shows.

## `maxReviewsPerCountry` (type: `integer`):

Cap on reviews collected for each app in each country (`reviews` mode). Leave empty to use only **Max items**.

## `reviewsNewerThan` (type: `string`):

A date (YYYY-MM-DD) or a period such as '2 days' or '3 weeks' (counted back from the start of each run, so a schedule always gets the latest reviews). With **Review order** = Most recent, the scraper stops at the first older review, so you pay only for new ones. This is the date input for daily or weekly monitoring; combine it with **Only new or changed items**.

## `includeAppVersion` (type: `boolean`):

Adds `appVersion`, `helpfulVotes`, `totalVotes` and `reviewerUrl` to reviews from Apple's reviews RSS feed (one extra request per 50 reviews). Apple publishes these only for the first 500 reviews per country in the chosen order; older reviews keep them empty.

## `searchPlatform` (type: `string`):

Which store to search in `search` mode: iPhone apps (default), iPad apps or Mac apps.

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

Overall cap on dataset items for the run (reviews, apps or search results). Keeps cost predictable.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this input returned and output only new items: in `reviews` mode only reviews not output before (read newest first, one page at a time, stopping where the previous run's reviews begin; forces **Review order** = Most recent); in `details` mode apps whose price, version or star rating changed per country; in `search` mode apps that are new in the results or whose price or version changed. Use with a Schedule; unchanged items are not charged.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input. Only used with **Only new or changed items**.

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

Apify datacenter proxy (default) is enough: Apple's endpoints are public. Requests rotate IPs to spread Apple's per-IP rate limit.

## Actor input object example

```json
{
  "mode": "reviews",
  "apps": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
  ],
  "searchTerms": [
    "habit tracker"
  ],
  "countries": [
    "us"
  ],
  "sort": "mostRecent",
  "includeAppVersion": true,
  "searchPlatform": "iphone",
  "maxItems": 100,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "apps": [
        "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
    ],
    "searchTerms": [
        "habit tracker"
    ],
    "countries": [
        "us"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/appstore-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": ["https://apps.apple.com/us/app/whatsapp-messenger/id310633997"],
    "searchTerms": ["habit tracker"],
    "countries": ["us"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/appstore-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": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
  ],
  "searchTerms": [
    "habit tracker"
  ],
  "countries": [
    "us"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call tortuga/appstore-scraper --silent --output-dataset

```

## MCP server setup

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