Scrape the Apple App Store as clean, structured JSON: search rankings, full app details, customer reviews, top charts and developer portfolios, for any storefront. It ships as a CLI and a Node library.
Powered by ScrapeUnblocker. Every page is fetched through the ScrapeUnblocker
getPageSourceAPI with the official Node.js SDK, so you don't run browsers, manage proxies or get rate-limited from your own IP. The parser reads the data model the App Store embeds in each page (plus its schema.org block) rather than the visual markup, so you don't have to maintain CSS selectors.
- 🔎 Search rankings: the apps the App Store ranks for a keyword, with their position, on iPhone, iPad or Mac. You can pass several keywords for ASO rank tracking.
- 📱 App details: exact rating count and a 1-5 star histogram, price and currency, every in-app purchase with its price, version, release notes and date, size, compatibility per device, languages, age rating, privacy labels, developer website and privacy policy, Editors' Choice, chart rank, screenshots, in-app events, "More by" and "You might also like".
- 💬 Reviews: Apple's public customer-review feed gives 50 reviews per page, up to 10 pages per storefront, sorted by most recent or most helpful. You can merge several storefronts (deduped by review ID) and filter by star rating or by date.
- 🏆 Top charts: the top free and top paid charts for any genre (42 genres,
including game sub-genres) on iPhone or iPad, up to 200 ranks.
--enrichfills in names, ratings and prices for ranks 26-200 with one extra request per 100 apps. - 👩💻 Developer pages: every app a developer publishes, grouped by platform section and deduped.
- 📦 Bulk lookup: metadata for many app IDs at once (100 per request), including the original release date and exact rating counts.
- 🌍 Works with any storefront (
--country gb,de,jp, ...) and optional page language (--lang en-GB). - 📤 Output as JSON, JSONL or CSV, to stdout or a file.
- 🔁 Retries with backoff on blocks, timeouts and incomplete pages. A real 404 is never retried.
- ✅ Offline unit tests with the SDK mocked, so they cost no API credit.
git clone https://github.com/ScrapeUnblocker/app-store-scraper.git
cd app-store-scraper
npm installYou need Node.js 18+ and an API key from ScrapeUnblocker:
cp .env.example .env # then edit it, or just export the key:
export SCRAPEUNBLOCKER_KEY="your_key_here"The scrapeunblocker SDK reads SCRAPEUNBLOCKER_KEY from the environment
automatically.
# Where do apps rank for these keywords? (one row per app per keyword)
node bin/cli.js search "budget planner" "expense tracker" --out ranks.csv
# Full details for one or more apps (ID or App Store URL), in the UK store
node bin/cli.js app 570060128 --country gb
node bin/cli.js app https://apps.apple.com/us/app/ankimobile-flashcards/id373493387
# 3 pages of the newest 1- and 2-star reviews from the US and UK stores
node bin/cli.js reviews 570060128 --pages 3 --countries us,gb --stars 1,2 --out reviews.csv
# Reviews posted since a date, most recent first
node bin/cli.js reviews 570060128 --pages 10 --since 2026-10-01
# Top 200 paid Finance apps, all ranks enriched
node bin/cli.js chart --genre finance --type paid --enrich --out finance-paid.csv
# Free and paid iPad puzzle-game charts
node bin/cli.js chart --genre games-puzzle --type both --platform ipad
# Everything a developer publishes
node bin/cli.js developer https://apps.apple.com/us/developer/duolingo/id570060151
# Bulk metadata for a list of app IDs
node bin/cli.js lookup 570060128 373493387 1459319842 --format jsonl
# List chart genres (no API call)
node bin/cli.js categoriesAfter npm link (or a global install) the same commands work as
app-store-scraper <command>. Run node bin/cli.js --help for every option.
| Command | What you get | Requests |
|---|---|---|
search <term...> |
The apps the App Store ranks for each term (about a dozen), with position |
1 per term |
app <id|url...> |
One detailed record per app | 1 per app |
reviews <id|url> |
Customer reviews, 50 per page, up to 10 pages per storefront | 1 per page |
chart |
Top free / paid chart, up to 200 ranks | 1 (+1 per 100 apps with --enrich) |
developer <id|url> |
A developer's apps across platforms | 1 |
lookup <id...> |
Bulk app metadata | 1 per 100 IDs |
categories |
Chart genre slugs and IDs | none |
Common options: --country (storefront, default us), --lang, --out,
--format json|jsonl|csv, --proxy-country (default US), --retries
(default 2), --verbose.
import { AppStoreScraper } from "./src/index.js";
const store = new AppStoreScraper({ country: "us" }); // reads SCRAPEUNBLOCKER_KEY
// Search rankings
const results = await store.search("habit tracker", { platform: "iphone" });
console.log(results.map((r) => `#${r.position} ${r.title}`));
// Full app details (null if the app is not in this storefront)
const app = await store.app("570060128", { country: "gb" });
console.log(app.rating, app.rating_count, app.rating_histogram);
// Newest low-star reviews from several storefronts
const reviews = await store.reviews("570060128", {
countries: ["us", "gb", "ca"],
sort: "mostrecent", // or "mosthelpful"
pages: 3,
stars: [1, 2],
});
// Top 200 free Productivity apps, ranks 26-200 enriched
const chart = await store.chart({ genre: "productivity", chart: "top-free", enrich: true });
// A developer's catalogue and bulk metadata
const dev = await store.developer("570060151");
const meta = await store.lookup(dev.apps.map((a) => a.app_id));The parsers are exported on their own too (parseApp, parseSearch,
parseCharts, parseDeveloper, parseReviewsFeed, parseLookup), so you
can run them on pages you have already saved.
app (trimmed):
{
"app_id": "373493387",
"bundle_id": "net.ichi2.anki",
"title": "AnkiMobile Flashcards",
"subtitle": "Smart & powerful flashcards",
"developer": "Anki Software, LLC",
"developer_id": "1895276950",
"seller": "Anki Software LLC",
"category": "Education",
"genres": ["Education", "Productivity"],
"is_free": false,
"price": 24.99,
"currency": "USD",
"price_text": "$24.99",
"rating": 4,
"rating_count": 2359,
"rating_histogram": { "1": 401, "2": 96, "3": 119, "4": 138, "5": 1605 },
"chart_rank": 1,
"chart_category": "Education",
"age_rating": "4+",
"size_text": "48 MB",
"size_mb": 48,
"compatibility": [
{ "device": "iPhone", "requirement": "Requires iOS 15.2 or later." },
{ "device": "iPad", "requirement": "Requires iPadOS 15.2 or later." }
],
"version": "25.09",
"version_date": "2025-09-08T15:11:50Z",
"privacy": [
{ "type": "DATA_LINKED_TO_YOU", "title": "Data Linked to You", "categories": ["Contact Info", "User Content"] },
{ "type": "DATA_NOT_LINKED_TO_YOU", "title": "Data Not Linked to You", "categories": ["Diagnostics"] }
],
"languages": ["English", "Arabic", "Belarusian", "Catalan", "..."],
"in_app_purchases": [],
"reviews": ["..."],
"similar_apps": ["..."]
}search (one row):
{
"term": "budget planner",
"position": 3,
"app_id": "942571931",
"bundle_id": "com.everydollar.EveryDollar",
"title": "EveryDollar: Budget Management",
"subtitle": "Track Income, Bills & Spending",
"developer": "The Lampo Group Incorporated",
"rating": 4.7,
"rating_count": 84000,
"rating_count_text": "84K",
"is_free": true,
"price": 0,
"price_text": null,
"has_in_app_purchases": true,
"age_rating": "4+",
"icon_url": "https://is1-ssl.mzstatic.com/image/thumb/.../1024x1024bb.png",
"url": "https://apps.apple.com/us/app/everydollar-budget-management/id942571931",
"has_event": false
}reviews (one row):
{
"review_id": "14664964125",
"app_id": "570060128",
"country": "us",
"rating": 1,
"title": "Do not waste your money",
"body": "1000 + day streak still not fluent or even basic. ...",
"author": "...",
"version": "7.144.0",
"date": "2026-10-14T09:56:47Z",
"vote_sum": 0,
"vote_count": 0
}chart --type both --enrich columns: chart, category, platform, country, rank, app_id, bundle_id, title, subtitle, developer, rating, rating_count, rating_count_text, is_free, price, price_text, has_in_app_purchases, age_rating, icon_url, url.
- Search returns the apps the App Store shows for a term, typically about
12. Editorial story cards are skipped, so
positioncounts apps only. The store answers every term, so a nonsense keyword still gets loosely related apps. - Rating counts on cards (search, charts, developer, similar apps) come
from the rounded display text ("84K") and are approximate.
appandlookupreturn the exact count. - Charts: the first 25 ranks carry full app cards. Ranks 26-200 are app IDs
until you pass
--enrich. Now and then a chart lists an app Apple no longer serves, and that rank keeps onlyrankandapp_id. - Reviews: the feed allows 10 pages (500 reviews) per storefront and sort
order. To get more coverage, collect several storefronts. The
reviewsarray inappoutput holds the featured reviews shown on the app page. - Localization: the App Store translates field labels per storefront. The
named fields
seller,languages,compatibilityandage_rating_notesare mapped from English labels. Use--lang en-GB(or similar) for non-English stores, or read the rawinformationarray, which is always included.
node examples/app-details.js 570060128 gb # app highlights as JSON
node examples/keyword-rank-tracker.js 1459319842 "budget" "budget planner" # ASO rank check
node examples/chart-to-csv.js finance top-paid # full 200-app chart -> CSV
node examples/review-monitor.js 570060128 # last week's 1-2 star reviewsapp-store-scraper/
├── bin/
│ └── cli.js # CLI (node:util parseArgs), one subcommand per page type
├── src/
│ ├── index.js # package entry point + exports
│ ├── scraper.js # AppStoreScraper: fetch, retry, paging, dedupe, enrichment
│ ├── parse.js # pure parsers: App Store pages + public JSON feeds -> records
│ ├── urls.js # URL builders, app ID parsing, chart genres
│ └── util.js # price/count/size parsing, artwork URLs, CSV, concurrency
├── examples/ # short runnable scripts
├── test/ # offline unit tests (SDK mocked, no API credit)
├── .github/workflows/ # CI: lint + test on Node 18, 20 and 22
└── package.json
npm install # install dependencies
npm test # offline unit tests (node --test)
npm run lint # eslintThe tests run against small fixtures that mirror real App Store pages and feeds. They cover every parser, the retry rules (blocks and incomplete pages are retried, 404s and billing errors are not), review paging and dedupe, chart enrichment, lookup batching and the CLI argument parser.
| Data | Source page | Fetched with |
|---|---|---|
| Search | apps.apple.com/<cc>/<platform>/search?term=... |
getPageSource |
| App details | apps.apple.com/<cc>/app/id<ID> |
getPageSource |
| Top charts | apps.apple.com/<cc>/<platform>/charts/<genre> |
getPageSource |
| Developer | apps.apple.com/<cc>/developer/id<ID> |
getPageSource |
| Reviews | Apple's public customer-reviews JSON feed | getPageSource |
| Lookup / enrichment | Apple's public lookup endpoint | getPageSource |
App Store pages are server-rendered and embed their full data model as JSON.
src/parse.js reads that JSON (and the schema.org SoftwareApplication
block) and flattens it into the records above.
MIT © 2026 ScrapeUnblocker
This is an unofficial, educational example. "App Store" and "Apple" are trademarks of Apple Inc. This project is not affiliated with or endorsed by Apple. Scrape responsibly and respect each site's Terms of Service.