# App Store Top Charts Scraper · Ratings + Rank Changes (`thequietstack/app-store-charts`) Actor

Apple App Store top charts (free, paid, grossing) for any country and category, plus keyword search. Every row carries rating, rating count, bundle ID and release date, and scheduled runs show rank changes and new entries. Official Apple feeds, no login.

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

## Pricing

from $1.00 / 1,000 app 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/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 Top Charts Scraper · Ratings + Rank Changes

Scrape Apple App Store **top charts (top free, top paid, top grossing)** for any country and any category, iPhone or iPad, plus **App Store keyword search**. Every chart position becomes one flat row that already has the rating, rating count, bundle ID, price and release date. When you schedule it, it also shows **how each app moved since the last run**. Uses Apple's own public feeds and APIs. No login, no API key.

Part of an app data family with the same flat output:

- [Google Play Top Charts Scraper](https://apify.com/thequietstack/google-play-charts), the Android twin (top 200, with installs)
- [App Store Reviews Scraper](https://apify.com/thequietstack/app-store-reviews), reviews from all ~175 storefronts in one run
- [Google Play Reviews Scraper](https://apify.com/thequietstack/google-play-reviews), reviews in all 49 languages, with developer replies

### Why this scraper

- **Ratings are in every chart row.** Apple's chart feed has no rating and no rating count. This Actor looks up all 100 apps of a chart in **one** call to Apple's official Lookup API, so every row has `rating`, `ratingCount`, `bundleId`, `releaseDate` and `lastUpdated`. You don't need a second actor or a second run.
- **Rank changes built in.** With `trackRankChanges` (on by default), each run saves a small snapshot of each chart in a named key-value store in **your** account. The next run fills `previousRank`, `rankChange` (+3 = moved up three places) and `isNewInChart` on every row, and the run summary lists the apps that **dropped out**. Schedule it daily and you get a rank tracker without a spreadsheet.
- **Any country × chart × category in one run.** For example, top grossing in Games/Puzzle for US, UK, DE and JP = 4 charts, one dataset. You can give categories by name (`games/puzzle`, `health & fitness`) or by Apple genre ID (`7012`). Unknown categories are rejected up front. (Apple would otherwise silently return the overall chart.)
- **Checked against the App Store.** On 24 Sep 2026 the top free chart matched apps.apple.com one for one (US: 15 of 15 positions, UK: 25 of 25, UK Games/Puzzle: 25 of 25), and rating and rating count matched the app pages exactly for 3 of 3 sampled apps.
- **Keyword search with the same fields.** `searchTerms` returns the App Store search results in order (up to 200 per keyword and country). Use it to see who ranks for a keyword and how many ratings they have.
- **No silent empty results.** Every feed response is checked. If Apple answers in a shape the Actor does not recognise, the summary says `FORMAT_WARNING`, and a run that writes nothing **fails visibly**.

### Input example

```json
{
    "charts": ["top-free", "top-grossing"],
    "categories": ["all", "games/puzzle", "productivity"],
    "countries": ["us", "gb", "de"],
    "device": "iphone",
    "appsPerChart": 100,
    "searchTerms": ["habit tracker"],
    "resultsPerSearchTerm": 50,
    "maxResults": 5000
}
```

That's 2 charts × 3 categories × 3 countries = 18 charts (up to 1,800 rows), plus 3 searches.

### Output example

```json
{
    "rank": 1,
    "chart": "top-free",
    "chartCategory": "Games / Puzzle",
    "country": "us",
    "device": "iphone",
    "appId": "6761760135",
    "bundleId": "com.oakever.meowdoku",
    "name": "Meowdoku!",
    "developer": "Oakever Games",
    "category": "Games",
    "genres": ["Games", "Casual", "Puzzle"],
    "price": 0,
    "currency": "USD",
    "rating": 4.80949,
    "ratingCount": 240470,
    "releaseDate": "2026-05-19T07:00:00.000Z",
    "lastUpdated": "2026-09-20T10:34:12.000Z",
    "version": "1.18.0",
    "url": "https://apps.apple.com/us/app/meowdoku/id6761760135",
    "previousRank": 1,
    "rankChange": 0,
    "isNewInChart": false,
    "previousSnapshotAt": "2026-09-23T23:24:37.997Z",
    "scrapedAt": "2026-09-23T23:27:04.715Z"
}
```

| Field | Meaning |
|---|---|
| `rank` | Position in the chart (1 = top). For search rows, the position in the search results |
| `chart` | `top-free`, `top-paid`, `top-grossing` or `search` |
| `chartCategory` | The category of the chart (`All`, `Games / Puzzle` ...) |
| `category`, `genres` | The app's own primary category and all its categories |
| `rating`, `ratingCount` | Average stars and number of ratings **in that country's storefront**, as Apple's Lookup API reports them |
| `releaseDate` / `lastUpdated` | First release / latest version date, ISO 8601 UTC |
| `previousRank`, `rankChange`, `isNewInChart` | Compared with your previous run of the same chart. Empty on the first run, or when the app sits deeper in the chart than your last snapshot went |

A run summary (`SUMMARY` in the key-value store) lists every chart with the source URL, rows written, apps that dropped out since the last snapshot, and any chart or search that failed.

### Honest limits

- **Apple publishes the top 100 per chart.** The feeds are capped at 100 (tested with 150 and 200). No positions 101–200 from this source.
- **Apple's overall charts include games.** The App Store app shows apps and games in separate charts, so the overall *paid* chart here has games in between (UK top paid on 24 Sep 2026: Minecraft at #4, which the website's apps-only list leaves out). Filter on `category` if you want apps only, or ask for a category chart.
- **Top grossing is no longer shown on apps.apple.com**, only in Apple's feed. It can't be checked against a web page; it comes straight from Apple's official feed.
- Top charts are for iPhone and iPad apps. Mac App Store charts are not included (Apple's paid and grossing Mac feeds returned empty in our test).
- `ratingCount` is per storefront. The same app has a different count in the US and in Germany.
- Search results come from Apple's official Search API. The order is close to the App Store app, but Apple does not promise it is identical.
- The rank snapshot store is kept in your account (a few KB per chart). Delete it in Storage → Key-value stores, or set `trackRankChanges` to false.

### Pricing (pay per event)

- Actor start: small flat fee per run
- Per row: charged only for rows written to the dataset (one row = one app in one chart or search result). Failed charts and skipped categories cost nothing, and `maxResults` caps the bill.

# Actor input Schema

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

Which top charts to scrape. Leave empty to run keyword search only.

## `categories` (type: `array`):

"all" for the overall chart, or category names / IDs: games, games/puzzle, productivity, health & fitness, finance, 6014 ... One chart per category.

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

Two-letter country codes (us, gb, de, jp ...). One chart per country.

## `device` (type: `string`):

iPhone or iPad charts.

## `appsPerChart` (type: `integer`):

Apple publishes the top 100 of every chart.

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

Optional. Search results in App Store order, one search per keyword and country, with the same fields as the chart rows.

## `resultsPerSearchTerm` (type: `integer`):

Apple's search API returns up to 200.

## `trackRankChanges` (type: `boolean`):

Saves a small snapshot of each chart in a named key-value store in your account. The next run fills previousRank, rankChange and isNewInChart. Schedule the Actor daily to get a rank history.

## `historyStoreName` (type: `string`):

Named key-value store for the rank snapshots. Use different names to keep separate histories.

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

The run stops cleanly at this number. You are never charged for more rows than this.

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

How many charts are fetched at the same time.

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

Requests go direct first. Only retries (after HTTP 403/429/5xx) use the proxy, on a fresh IP each time.

## Actor input object example

```json
{
  "charts": [
    "top-free",
    "top-grossing"
  ],
  "categories": [
    "all"
  ],
  "countries": [
    "us"
  ],
  "device": "iphone",
  "appsPerChart": 100,
  "searchTerms": [
    "habit tracker",
    "ai photo editor"
  ],
  "resultsPerSearchTerm": 50,
  "trackRankChanges": true,
  "historyStoreName": "app-store-charts-history",
  "maxResults": 5000,
  "concurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One flat row per app per chart: rank, rank change, app ID, bundle ID, name, developer, category, price, rating, rating count, release date, URL.

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

Rows per chart and search, apps that dropped out since the last run, failed charts, data source check.

# 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 = {
    "charts": [
        "top-free",
        "top-grossing"
    ],
    "categories": [
        "all"
    ],
    "countries": [
        "us"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("thequietstack/app-store-charts").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 = {
    "charts": [
        "top-free",
        "top-grossing",
    ],
    "categories": ["all"],
    "countries": ["us"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("thequietstack/app-store-charts").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 '{
  "charts": [
    "top-free",
    "top-grossing"
  ],
  "categories": [
    "all"
  ],
  "countries": [
    "us"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call thequietstack/app-store-charts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thequietstack/app-store-charts"
        }
    }
}
```

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/ZUuXADgUfPq21PbX0/builds/v1MXBo8DSQhTDyNhY/openapi.json
