If you are trying to scrape the Apple App Store for ASO research, keyword tracking, or competitor monitoring, here is the honest version most posts skip: this one is genuinely easier than most scraping targets, because Apple exposes a public search API and public chart feeds that need no login and no proxy. The real cost isn't getting the data out - it's normalizing three different response shapes (search, lookup, charts) into one schema, handling Apple's undocumented rate limits, and building the scheduling and history layer on top so a snapshot becomes a trend. This post covers both. Whether you searched "app store scraper", "aso tool" or "app store keyword api" to get here, the tradeoffs below apply either way.
Why this is easier than most scrapers, and where it still bites you
Apple's iTunes Search API (itunes.apple.com/search) and the App Store's public RSS chart feeds are unauthenticated, documented (loosely), and return JSON directly - no headless browser, no residential proxy, no client-side rendering to fight. For search-by-keyword and lookup-by-ID, a plain requests.get() gets you real data on the first try. That's the good news, and it's worth saying plainly instead of manufacturing a horror story that isn't there.
Where it still costs you real engineering time:
- Undocumented rate limits. Apple does not publish a request-per-minute ceiling for the Search API. Hit it too hard and you start getting empty results or throttled responses with no clear error explaining why - you find the limit by tripping it.
-
Three different response shapes. Search and lookup return the same iTunes JSON schema, but the RSS chart feeds (
topfreeapplications,topgrossingapplications, etc.) come back in a different structure entirely, so "one App Store scraper" is really three code paths you have to keep in sync. - No native ranking history. Every response is a snapshot. Trend detection (a rating that moved, a competitor climbing the charts) only exists if you build the storage and diffing layer yourself - the API gives you no memory.
-
Country and entity matrix. Multiply keywords by storefronts (
us,gb,br,de, ...) and entity types (software,iPadSoftware,macSoftware, ...) and you are running and merging a lot of small requests, not one big one.
Approach 1: DIY with Python
Attempt 1: keyword search and ID lookup (this actually works)
import requests
def search_apps(term: str, country: str = "us", limit: int = 50):
r = requests.get(
"https://itunes.apple.com/search",
params={"term": term, "country": country, "entity": "software", "limit": limit},
timeout=10,
)
return r.json()["results"]
def lookup_apps(app_ids: list[str], country: str = "us"):
r = requests.get(
"https://itunes.apple.com/lookup",
params={"id": ",".join(app_ids), "country": country},
timeout=10,
)
return r.json()["results"]
No proxy, no browser, no anti-bot fight. Run this and you get real ratings, prices and metadata back in one request. The catch shows up once you scale it: run this in a tight loop across many keywords and countries and you start hitting undocumented throttling, with nothing in the response body explaining that you've been rate-limited versus that zero apps genuinely matched.
Attempt 2: Top Charts (a different shape entirely)
def get_top_charts(feed: str = "topfreeapplications", country: str = "us", limit: int = 100):
url = f"https://itunes.apple.com/{country}/rss/{feed}/limit={limit}/json"
r = requests.get(url, timeout=10)
entries = r.json()["feed"]["entry"]
return [
{
"rank": i + 1,
"id": e["id"]["attributes"]["im:id"],
"name": e["im:name"]["label"],
"developer": e["im:artist"]["label"],
}
for i, e in enumerate(entries)
]
This is a completely different response shape from search/lookup - im:-prefixed field names, a different nesting level, no rating or price data at all (charts feeds give you ranking, not metadata; you have to lookup_apps() afterward if you want ratings for the ranked IDs). Two API surfaces, one mental model you have to build to keep them consistent.
The real cost here isn't fetching data, it's normalizing and remembering it
Because the raw fetch is genuinely free and reliable, DIY is a fair choice for a single check. The cost shows up when you need one consistent schema across modes, historical trend data, and scheduled runs.
| DIY (Python, direct API calls) | Managed scraper (API) | |
|---|---|---|
| Upfront cost | Free (your time) | Pay per result returned |
| Fetching data | Genuinely easy - public JSON endpoints | Same endpoints, wrapped for you |
| Rate limit handling | You find the ceiling by tripping it | Handled on the provider's side |
| Search / lookup / charts schema | Three shapes you normalize yourself | One consistent output schema |
| Country x entity matrix | You loop and merge manually | One input, same schema every time |
| History / scheduling | You build storage + diffing | Native scheduler, append to a dataset |
Neither column is objectively "right." A one-off keyword check is genuinely fine to write yourself with the snippets above - that's the point of showing they work. Recurring, cross-country, cross-mode monitoring is where the normalization and scheduling work adds up.
Approach 2: a ready-made App Store ASO scraper (API)
This is the part where I show you the shortcut. App Store Scraper & ASO Tool is an Apify actor that wraps search, lookup and charts behind one input and one output schema, with rate limiting, country handling and dataset history already built in.
Input
Only the fields relevant to your chosen mode are required.
| Field | Description | Example |
|---|---|---|
mode |
search, lookup, or charts
|
search |
term |
Keyword to search for (search mode) | fitness tracker |
appIds |
One or more App Store IDs (lookup mode) | ["310633997"] |
feed |
Chart to fetch (charts mode) | topfreeapplications |
entity |
App type (search mode) | software |
country |
Two-letter storefront code |
us, gb, br
|
maxResults |
Maximum results to return (up to 200) | 50 |
{
"mode": "search",
"term": "fitness tracker",
"entity": "software",
"country": "us",
"maxResults": 50
}
{
"mode": "charts",
"feed": "topfreeapplications",
"country": "gb",
"maxResults": 100
}
Output
The same shape whether the record came from search, lookup, or charts:
{
"appId": 389801252,
"name": "Instagram",
"developer": "Instagram, Inc.",
"price": 0.0,
"currency": "USD",
"rating": 4.7,
"ratingCount": 28000000,
"genre": "Photo & Video",
"version": "350.1",
"releaseDate": "2010-10-06T19:12:14Z",
"updatedDate": "2026-06-10T08:31:00Z",
"contentRating": "12+",
"url": "https://apps.apple.com/us/app/id389801252",
"icon": "https://is1-ssl.mzstatic.com/image/.../512x512bb.jpg",
"description": "Little moments lead to big friendships..."
}
In charts mode, each record also includes a rank field - so you get ranking and metadata in the same response, no separate lookup call needed.
Calling it from JavaScript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('plum_spear/aztec-appstore-aso').call({
mode: 'search',
term: 'fitness tracker',
country: 'us',
maxResults: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'apps');
console.log(items[0]);
Calling it from Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("plum_spear/aztec-appstore-aso").call(run_input={
"mode": "lookup",
"appIds": ["389801252", "310633997"],
"country": "us",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["name"], item["rating"], item["version"])
Calling it from the CLI or plain REST
apify call plum_spear/aztec-appstore-aso --input '{"mode": "charts", "feed": "topfreeapplications", "country": "us", "maxResults": 100}'
curl "https://api.apify.com/v2/acts/plum_spear~aztec-appstore-aso/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"mode": "search", "term": "fitness tracker", "country": "us", "maxResults": 50}'
What people actually build with this
- ASO managers run search mode against target keywords to see who owns them, then lookup mode on a fixed competitor list to track rating, version and update-date shifts week over week.
- App marketers monitor Top Charts daily to catch a rising competitor before it breaks into the top ranks.
- Market researchers quantify an entire category in one run - who ranks, how they price, how well they're rated - for sizing studies or reports.
- Indie developers validate an app idea by checking how saturated a keyword already is and what the top apps charge, before writing a line of code.
- Growth and data teams use it as a building block: scheduled runs feed a data warehouse or alerting pipeline for continuous market monitoring.
Pricing
Pay-per-event: $0.15 per 1,000 results returned, plus a minimal actor-start event. No subscription, no monthly minimum, no hidden proxy costs - you only pay for the app records you actually get. Apify's free monthly platform credits are enough to try a real query before deciding whether it's worth it.
For context: the raw API calls above are free, so a single keyword check is genuinely fine to do yourself. Once you need one schema across search, lookup and charts, plus scheduled history so a snapshot becomes a trend, $0.15 per 1,000 results is a small price for not building and maintaining that normalization layer yourself.
Using it from an AI agent (MCP)
If you're wiring this into an agent instead of a script, actors published on Apify, including this one, are reachable through Apify's MCP server, which exposes them as callable tools for MCP-compatible clients. Same mode / term / appIds / feed input, no separate integration to write.
Wrap-up
Pulling raw App Store data yourself is genuinely easy - no proxy, no browser, just public JSON endpoints, and the snippets above will get you there for a one-off check. What they won't do on their own is give you one consistent schema across search, lookup and charts, handle Apple's undocumented rate limits gracefully, or turn a snapshot into a trend over time. That gap is what App Store Scraper & ASO Tool on Apify closes: pick a mode, get back the same clean JSON schema every time, priced at $0.15 per 1,000 results with no monthly commitment.
💡 Precisa monitorizar a sua marca nos assistentes de IA?
O GEO Tracker analisa a presença da sua empresa no ChatGPT, Gemini, Perplexity, Claude e mais — relatório PDF em 24h.
Top comments (0)