# App Store Rankings Scraper: Top Charts by Country (`arman-bd/app-store-rankings-scraper`) Actor

Scrape Apple App Store top charts: rankings by category, country and chart type, with app metadata, publisher and price. Track rank movement over time.

- **URL**: https://apify.com/arman-bd/app-store-rankings-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** E-commerce, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 ranking scrapeds

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/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 Store Rankings Scraper: Top Charts by Country

![App Store Rankings — Top free, paid and grossing charts for every storefront and every category](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/app-store-rankings-scraper.jpg)

**App Store Rankings Scraper** pulls Apple's **top free**, **top paid** and **top grossing** charts for any storefront and any App Store category, rank, app name, publisher, price, currency, category, release date, icon and the full store description.

Every combination of country × chart type × category is fetched as its own chart, so one run can cover a dozen markets at once. **No proxy setup, no browser, no credentials to manage.**

### What you get

| Output field | Meaning |
|---|---|
| `country` | Storefront the chart came from, e.g. `us`, `gb` |
| `chartType` | `topfree`, `toppaid` or `topgrossing` |
| `categoryId` | Apple genre ID requested, or `null` for the overall chart |
| `rank` | Position in that chart, 1-based |
| `appId` | Apple's numeric app ID, your join key across runs |
| `appName` | App name as it appears in the store |
| `publisher` | Developer / seller name |
| `price`, `currency` | Numeric price and the storefront's currency (`0` + `USD` for free apps) |
| `category` | The app's own primary category, which can differ from the chart you pulled |
| `releaseDate` | First release date, ISO 8601 with the store's timezone offset |
| `iconUrl` | Highest-resolution icon Apple ships in the feed (100×100) |
| `summary` | Full store description text |
| `rights` | Copyright line |
| `scrapedAt` | Run timestamp, the field you diff on to measure rank movement |

A `RUN_SUMMARY` record in the key-value store holds per-run counts, per-chart counts, the filters applied and any chart that failed.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `countries` | array | `["us"]` | Two-letter storefront codes, lowercase. An unknown code returns HTTP 400 and is recorded as a failure without stopping the run. |
| `chartTypes` | array | `["topfree"]` | Any of `topfree`, `toppaid`, `topgrossing`. An unrecognised value fails validation up front. |
| `categoryIds` | array | `[]` | Apple genre IDs. Empty = the overall chart. |
| `limit` | integer | `100` | Chart depth. Capped at 200, though Apple currently truncates at ~100. |

**Which combinations make sense**

- The run fetches the **full cross product**: 4 countries × 3 chart types × 2 categories is 24 requests and up to 2,400 records. Size `limit` accordingly.
- `topgrossing` is the most useful signal for revenue tracking; `topfree` moves fastest day to day.
- `categoryIds` is where breakout detection lives, a new entrant at rank 40 in Games is far more informative than rank 400 overall.

Common category IDs: `6014` Games · `6015` Finance · `6016` Entertainment · `6017` Education · `6018` Books · `6020` Medical · `6023` Food & Drink · `6002` Utilities · `6005` Social Networking · `6008` Photo & Video · `6012` Lifestyle.

**Example input**

```json
{
 "countries": ["us", "gb"],
 "chartTypes": ["topfree", "toppaid"],
 "categoryIds": ["6014"],
 "limit": 25
}
```

### Output

A real record from the run above:

```json
{
 "country": "us",
 "chartType": "topfree",
 "categoryId": "6014",
 "rank": 1,
 "appId": "6748084174",
 "appName": "Smash Fest!",
 "publisher": "Flow Games Bilisim Yazilim ve Pazarlama Anonim Sirketi",
 "price": 0,
 "currency": "USD",
 "category": "Games",
 "releaseDate": "2025-12-16T00:00:00-07:00",
 "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/74/15/3a/74153a85-8436-259e-5297-63fbd718af84/AppIcon-0-0-1x_U007emarketing-0-8-0-85-220.png/100x100bb.png",
 "summary": "Welcome to Smash Fest! Get ready for the most explosive festival in town! …",
 "rights": "© Flow Games 2025",
 "scrapedAt": "2026-08-06T11:31:55.000Z"
}
```

Prices come back in the storefront's own currency, so the same app on the GB chart reads:

```json
{
 "country": "gb",
 "chartType": "toppaid",
 "rank": 1,
 "appId": "479516143",
 "appName": "Minecraft: Dream it, Build it!",
 "publisher": "Mojang",
 "price": 6.99,
 "currency": "GBP"
}
```

`RUN_SUMMARY`:

```json
{
 "chartsRequested": 6,
 "chartsFailed": 2,
 "failures": [
 { "chart": "zz/topfree/6014", "error": "no such chart, check country \"zz\" (HTTP 400)" },
 { "chart": "zz/toppaid/6014", "error": "no such chart, check country \"zz\" (HTTP 400)" }
 ],
 "appsSaved": 100,
 "appsPerChart": {
 "us/topfree/6014": 25,
 "us/toppaid/6014": 25,
 "gb/topfree/6014": 25,
 "gb/toppaid/6014": 25
 },
 "filters": {
 "countries": ["us", "gb", "zz"],
 "chartTypes": ["topfree", "toppaid"],
 "categoryIds": ["6014"],
 "limit": 25
 },
 "finishedAt": "2026-08-06T11:31:55.771Z"
}
```

### Use cases

**1. Track your app's rank daily.** Schedule a run in the categories and storefronts you care about, then filter the dataset to your `appId` and plot `rank` against `scrapedAt`.

```json
{
 "countries": ["us", "gb", "de", "jp", "br"],
 "chartTypes": ["topfree", "topgrossing"],
 "categoryIds": ["6014"],
 "limit": 100
}
```

**2. Competitive ASO monitoring.** Pull the paid and grossing charts for a category and watch which publishers gain positions week over week. `summary` gives you their live store copy for keyword analysis.

```json
{
 "countries": ["us"],
 "chartTypes": ["toppaid", "topgrossing"],
 "categoryIds": ["6015", "6017", "6023"],
 "limit": 100
}
```

**3. Spot breakout apps early.** Diff consecutive runs on `appId`: an app that appears in a category chart for the first time, or jumps more than ~20 places, is a breakout candidate. `releaseDate` separates new apps from re-surging old ones.

```json
{
 "countries": ["us"],
 "chartTypes": ["topfree"],
 "categoryIds": ["6014", "6005", "6008"],
 "limit": 100
}
```

### Limits and behaviour

- **Charts are already ranked.** `rank` is the app's position in the feed as Apple ordered it, the Actor does no sorting or scoring of its own.
- **Apple currently truncates at ~100 entries per chart.** The feed URL accepts `limit=200` and the Actor caps there, but every chart tested returns 100 (99 for `toppaid`). A request for 200 that yields 100 is Apple's behaviour, not a bug here.
- **The cross product is fetched in full.** Requests are sequential, one per chart, and each takes a few hundred milliseconds.
- **`category` is the app's own category, not the chart's.** An app pulled from the Games chart can carry a different primary category; use `categoryId` for the chart you asked for.
- **A failing chart never aborts the run.** An unknown storefront returns HTTP 400, is recorded in `RUN_SUMMARY.failures`, and the next chart proceeds. The Actor only errors out if *every* chart fails.
- **Transient errors are retried.** 429 and 5xx get three attempts with linear backoff. HTTP 400 fails fast, because retrying an invalid storefront never helps.
- **Single-app feeds are handled.** With `limit=1` Apple returns `entry` as a bare object instead of an array; the Actor normalises that.
- **Public data only.** No authentication, no personal data, no access-control bypass.

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need an Apple developer account?** No. You supply no credentials.

**What happens if a storefront is unavailable?** It is reported in `RUN_SUMMARY.failures` and the run continues with the next chart. The run only fails outright if every chart failed.

**Can I schedule it?** Yes, daily scheduled runs are the intended pattern. Rank data is only useful as a time series.

**Why does `toppaid` return 99 rows instead of 100?** Apple's paid feed itself serves 99 entries. The Actor reports exactly what Apple returned.

**How do I measure rank movement?** Run on a schedule and diff consecutive datasets on `appId` within the same `country` + `chartType` + `categoryId`, comparing `rank` across `scrapedAt`.

**Does it include ratings or review counts?** No, the RSS chart feeds do not carry them. Pair this with an App Store detail scraper if you need ratings.

**Can I get iPad or Mac charts?** Not currently. The Actor exposes the three iPhone chart types the plan specifies.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~app-store-rankings-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "countries": ["us", "gb"],
 "chartTypes": ["topfree", "toppaid"],
 "categoryIds": ["6014"],
 "limit": 25
 }'
```

### JavaScript example

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/app-store-rankings-scraper').call({
 countries: ['us'],
 chartTypes: ['topgrossing'],
 limit: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const app of items) console.log(`#${app.rank} ${app.appName}, ${app.publisher}`);
```

# Actor input Schema

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

Two-letter storefront codes, lowercase. Each is a separate Apple storefront with its own chart. 'us', 'gb', 'de', 'jp', 'br'. An unknown code returns HTTP 400 and is recorded as a failure without stopping the run.

## `chartTypes` (type: `array`):

Which charts to pull. 'topfree' is the free apps chart, 'toppaid' the paid chart, 'topgrossing' the revenue chart.

## `categoryIds` (type: `array`):

Apple genre IDs to restrict each chart to. Leave empty for the overall chart. Common IDs: 6014 Games, 6015 Finance, 6016 Entertainment, 6017 Education, 6018 Books, 6020 Medical, 6023 Food & Drink, 6002 Utilities, 6005 Social Networking, 6008 Photo & Video, 6012 Lifestyle.

## `limit` (type: `integer`):

How many ranked apps to pull per chart. 200 is the deepest URL Apple accepts and higher values are capped there, but Apple is currently truncating every chart at around 100 entries. so asking for 200 legitimately returns 100.

## Actor input object example

```json
{
  "countries": [
    "us",
    "gb",
    "de",
    "jp"
  ],
  "chartTypes": [
    "topfree",
    "toppaid",
    "topgrossing"
  ],
  "categoryIds": [
    "6014",
    "6015"
  ],
  "limit": 100
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# 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 = {
    "countries": [
        "us",
        "gb"
    ],
    "chartTypes": [
        "topfree",
        "toppaid"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/app-store-rankings-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 = {
    "countries": [
        "us",
        "gb",
    ],
    "chartTypes": [
        "topfree",
        "toppaid",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/app-store-rankings-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 '{
  "countries": [
    "us",
    "gb"
  ],
  "chartTypes": [
    "topfree",
    "toppaid"
  ]
}' |
apify call arman-bd/app-store-rankings-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/1gbk2dIH36IHgB4BH/builds/BX3qmWeaPP8wH3OHJ/openapi.json
