app-store-scraper Library Guide for Python and npm
- Two separate packages share the name
app-store-scraper: the Python one (PyPI 0.3.5) and the npm one (0.18.0). They have different authors and different APIs, so I tested both. - The Python
app-store-scraperpinsrequests==2.23.0. On Python 3.13 that pin drags in an old urllib3 and the package fails to import withModuleNotFoundError: No module named 'urllib3.packages.six.moves'before you can call a single method. - The npm
app-store-scraperworked for me.store.reviews()returned 50 reviews per page with apage(1 to 10) andsort(RECENTorHELPFUL), capped near 500 reviews per query.store.app()returned full metadata. - App metadata also returns cleanly from Apple's public iTunes lookup endpoint. For Google Play,
google-play-scraperpulled 5 newest reviews on the first try. For review volume past that 500 ceiling I hand the App Store call to ChocoData.
I went into this expecting a quick win. The package is called app-store-scraper, it is on PyPI, the README is short. I ran pip install app-store-scraper, opened a Python 3.13 shell, typed from app_store_scraper import AppStore, and got a ModuleNotFoundError before I had written a single line of scraping. This guide is the record of what actually happened when I used the app store scraper python library in June 2026, what the npm package of the same name does differently, and the routes that returned real data for me.
Everything below is code I ran myself against live App Store and Google Play targets. Where a route failed, I show the exact status code or traceback. Where it worked, I show the numbers it returned.
There are two packages called app-store-scraper
Before any code, clear up the naming, because it has cost me an hour before now. Two unrelated projects share the string app-store-scraper, and a tutorial written for one will not run on the other. The Python package and the npm package have different authors, different parameters, and different review ceilings.
Python app-store-scraper | npm app-store-scraper | |
|---|---|---|
| Registry | PyPI | npm |
| Latest version | 0.3.5 | 0.18.0 |
| Last published | Nov 12, 2020 | 0.18.0 dist-tag |
| Author | Eric Lim (cowboy-bebug) | facundoolano |
| Install | pip install app-store-scraper | npm install app-store-scraper |
| Review fetch | batches of 20 via how_many | page 1 to 10 (about 500 max) |
| Key dependency risk | pins requests==2.23.0 | depends on deprecated request |
| Worked in my June 2026 test | No (fails to import on 3.13) | Yes |
I pulled those versions straight from the registries on June 16, 2026: the PyPI JSON API reports the PyPI app-store-scraper package at 0.3.5 with requires_dist: ["requests (==2.23.0)"], and the npm registry returns the app-store-scraper npm package at 0.18.0 as the latest dist-tag. The npm package is still busy, at 29,592 downloads in the week of June 9 to 15, 2026 per the npm downloads API. Both projects host their code on GitHub, so if you only have the app-store-scraper github link from a tutorial, check the language of the repo before you trust the snippet. The Python release has not shipped since November 2020, and that five-year gap is exactly where the trouble starts when you try to scrape Apple App Store data with it.
Using the app store scraper python library (and what it returned)
As a Python app store scraper, app-store-scraper exposes one class, AppStore, and one review method. Its PyPI page serves as the package’s documentation and shows this usage:
from app_store_scraper import AppStore
app = AppStore(country="us", app_name="instagram", app_id=389801252)
app.review(how_many=20)
print(app.reviews_count)
print(app.reviews[0])
The AppStore constructor wants a country (ISO alpha-2) and an app_name. You pass app_id to skip an internal name lookup, which I did using Instagram’s real track ID, 389801252. The .review() method takes three arguments and nothing else:
| Parameter | Type | What it does |
|---|---|---|
how_many | int | Total reviews to fetch. The library pulls in fixed batches of 20. Omit it and it tries to fetch all of them. |
after | datetime | Keep only reviews newer than this timestamp. |
sleep | int | Seconds to pause between requests, to ease the rate. |
There is no page parameter and no num parameter on the Python side. The library fetches in fixed batches of 20 internally and appends to app.reviews. So a search for an “app-store-scraper reviews page parameter” or a “reviews num parameter” points to the npm package, which I cover further down.
The install pins a 2020-era requests, and it breaks the import
Here is the part the README does not flag. Version 0.3.5 declares requests==2.23.0 as a hard pin in its requirements, which you can read in the source on GitHub. Installing it into a clean virtualenv pulled this exact dependency set on June 16, 2026:
| Package | Version pip installed | Released around |
|---|---|---|
app-store-scraper | 0.3.5 | Nov 2020 |
requests | 2.23.0 | 2020 |
urllib3 | 1.25.11 | 2020 |
chardet | 3.0.4 | 2017 |
idna | 2.10 | 2020 |
That requests==2.23.0 pin forces urllib3 1.25.11, and urllib3 1.25.11 imports a six.moves compatibility shim that newer Python builds no longer ship. On Python 3.13 the import chain dies immediately:
from app_store_scraper import AppStore
# ModuleNotFoundError: No module named 'urllib3.packages.six.moves'
The full traceback ends in urllib3/exceptions.py, on the line from .packages.six.moves.http_client import IncompleteRead. The package never reaches its own code. You cannot construct an AppStore object, let alone call .review(), on a current Python until you replace that pinned stack. The fix is to install it isolated and then unpin urllib3:
python -m venv assvenv
assvenv/Scripts/python -m pip install app-store-scraper
assvenv/Scripts/python -m pip install --upgrade "urllib3>=2" "requests>=2.31"
Upgrading urllib3 clears the import. That keeps the app-store-scraper 0.3.5 requests dependency from poisoning the rest of your environment, and it lets the package load. With the import fixed, I ran the review pull against Instagram.
Once it imports, the reviews endpoint answers 401
from app_store_scraper import AppStore
app = AppStore(country="us", app_name="instagram", app_id=389801252)
app.review(how_many=20)
print("reviews_count:", app.reviews_count)
The library targets Apple’s amp-api.apps.apple.com reviews endpoint, the same one the App Store web client uses. I called that endpoint directly to see what it returns to an unauthenticated client:
import urllib.request
url = ("https://amp-api.apps.apple.com/v1/catalog/us/apps/"
"389801252/reviews?platform=web&limit=20")
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
urllib.request.urlopen(req, timeout=20) # raises HTTPError: 401
The endpoint answered HTTP 401 Unauthorized. That tier wants a bearer token the App Store web client mints for itself, and the library does not supply one, so the call fails. A 2020 package built against an endpoint Apple has since gated returns no review data in 2026, and that matched my run. The metadata side is healthier, because it uses a different, supported Apple endpoint, which the next section covers.
How do you get App Store metadata that actually returns data?
Apple’s iTunes Search “lookup” endpoint is public, documented, and returned live data for me. You do not need a library for it at all:
import requests
r = requests.get(
"https://itunes.apple.com/lookup",
params={"id": "389801252", "country": "us"},
timeout=20,
)
app = r.json()["results"][0]
print(app["trackName"], app["averageUserRating"], app["userRatingCount"])
When I ran this on June 16, 2026 it returned, in under a second:
| Field | Value |
|---|---|
trackName | |
bundleId | com.burbn.instagram |
averageUserRating | 4.69061 |
userRatingCount | 29,156,196 |
version | 433.0.0 |
primaryGenreName | Photo & Video |
formattedPrice | Free |
The parameters and rate guidance live in Apple’s own iTunes Search API documentation, where Apple asks callers to stay around 20 requests per minute from a single source. This endpoint covers ratings, screenshots, version, developer fields, and genre. It does not return review text, which is the gap the libraries were meant to fill. The full customer-review dataset with author attribution lives behind Apple’s authenticated App Store Connect API, at /v1/apps/{id}/customerReviews, and that one returns reviews only for apps your own account manages.
The npm app-store-scraper: page and sort parameters
The npm package is the more actively maintained of the two, and it is where the page and sort questions come from. It worked in my test. Install it and run the basic reviews call:
npm install app-store-scraper
const store = require("app-store-scraper");
store
.reviews({
id: 389801252, // Instagram
country: "us",
sort: store.sort.RECENT, // or store.sort.HELPFUL
page: 1, // 1 to 10
})
.then((reviews) => console.log(reviews.length, reviews[0].score, reviews[0].text.slice(0, 50)));
That call returned an array of 50 reviews for me, with real review fields. The parameters that matter for review collection, confirmed against the package README:
| Parameter | Default | Notes |
|---|---|---|
id or appId | required | Numeric track ID (id) or bundle ID (appId). |
country | us | Two-letter store code. |
sort | store.sort.RECENT | The other constant is store.sort.HELPFUL. |
page | 1 | Maximum is 10. Requesting page 11 throws. |
throttle | none | Requests per second cap, for example throttle: 2. |
There is no num parameter in this package, so a search for an “app-store-scraper reviews num parameter” comes up empty. You control volume with page and ordering with sort (setting app-store-scraper reviews sort recent means passing store.sort.RECENT, which is already the default). Apple serves roughly 50 reviews per page, so the ceiling is about 500 reviews per app per country store; I confirmed the bound by requesting page 11, which threw Page cannot be greater than 10, and the raw RSS feed behind it returns HTTP 400 for page 11 directly. To collect more than 500 you loop the 10 pages, switch country codes (each language and country store has its own review set), or move to a different data source.
The app() method returned full metadata that matched the lookup endpoint to the decimal:
store.app({ id: 389801252 }).then(console.log);
// title: "Instagram", score: 4.69061, version: "433.0.0", reviews: 29156196
Under the hood, the reviews call reads Apple’s RSS customer-reviews feed. I fetched that feed directly to confirm what it serves:
curl "https://itunes.apple.com/us/rss/customerreviews/id=389801252/sortby=mostrecent/page=1/json"
It came back HTTP 200 with 50 review entries, each carrying im:rating, im:version, title, content, author, im:voteSum, and im:voteCount. So the npm package is reading a feed that is currently populated, which is why its reviews call worked where the Python package’s gated endpoint did not. One caveat for anything long-lived: the npm package depends on the request HTTP library, which was fully deprecated on February 11, 2020 and receives no fixes. The dependency still functions, and it is worth knowing the foundation before you build a pipeline on it. For a one-off pull of a few hundred recent reviews, it does the job cleanly.
If you want the full ID-to-data walkthrough for both stores, I keep a longer version in how to scrape App Store and Google Play data.
How do you scrape Google Play reviews in Python?
The Google Play side was the smooth part of this test. The google-play-scraper package (version 1.2.7) has no external dependencies and a clean reviews API:
from google_play_scraper import app, reviews, Sort
info = app("com.instagram.android", lang="en", country="us")
print(info["title"], info["score"], info["installs"])
result, token = reviews(
"com.instagram.android",
lang="en",
country="us",
sort=Sort.NEWEST, # or Sort.MOST_RELEVANT
count=5,
)
for r in result:
print(r["score"], "-", r["content"][:60])
This ran first try in June 2026 and returned real data:
| Call | Result |
|---|---|
app() title | |
app() score | 4.006 |
app() installs | 5,000,000,000+ |
reviews() fetched | 5 of 5 requested |
| Review fields | score, content, at, appVersion, reviewId, thumbsUpCount, replyContent, userName |
The reviews() parameters are the ones to know:
| Parameter | Default | Notes |
|---|---|---|
count | 100 | Google Play caps a page at 200, so larger counts auto-paginate. |
sort | Sort.NEWEST | Sort.MOST_RELEVANT is the alternative. |
lang / country | en / us | Controls the locale of the reviews. |
filter_score_with | None | Pass an int 1 to 5 to keep only that star rating. |
The function returns a (results, continuation_token) tuple. Feed the token back in to continue, or call reviews_all() to drain every review, which fires more requests and is slower and easier to get throttled on. This is the google play store scraper python path I reach for first on small jobs. A hosted equivalent lives at the Google Play Reviews Scraper API when pagination across thousands of reviews stops being fun.
When the library is the wrong tool
The library route works until a pinned dependency or a gated endpoint gets in the way, and my June run shows both failure shapes plainly. The five-year-old PyPI package pins a 2020 requests, breaks the import on Python 3.13, and answers 401 on its review endpoint even after you patch the import. The npm package works today but caps you near 500 reviews and sits on an HTTP library deprecated since 2020. None of that is the maintainers’ doing. Apple gated one endpoint and the JavaScript ecosystem moved on from request, and a 2020 release cannot keep pace.
A hosted API moves those problems off your machine. You send an app ID, the proxy rotation and parsing run server-side, and parsed JSON comes back. The funnel here is ChocoData, and the call mirrors the metadata pull I did by hand above:
curl "https://chocodata.com/api/v1/appstore/app?id=389801252&api_key=$CHOCO_API_KEY"
The same request shape covers reviews and search through the matching endpoints, so you keep one pattern across the whole App Store surface and you are not capped at 500:
# App Store reviews, parsed and paged server-side
curl "https://chocodata.com/api/v1/appstore/reviews?id=389801252&country=us&sort=recent&api_key=$CHOCO_API_KEY"
# App Store search results
curl "https://chocodata.com/api/v1/appstore/search?term=photo%20editor&country=us&api_key=$CHOCO_API_KEY"
The Python version is the same GET with your key as a query parameter, loaded straight into pandas:
import requests
import pandas as pd
resp = requests.get(
"https://chocodata.com/api/v1/appstore/reviews",
params={"id": "389801252", "country": "us", "sort": "recent",
"api_key": "YOUR_CHOCO_API_KEY"},
timeout=30,
)
reviews = resp.json()["data"]["reviews"]
df = pd.DataFrame(reviews)
df.to_csv("instagram_appstore_reviews.csv", index=False)
print(df.head())
You can get an API key on the ChocoData sign-up page and drop it into the snippet. Here is how I decide between the routes I tested:
| Situation | What I reach for |
|---|---|
| One-off pull, a few hundred recent reviews, throwaway script | The npm app-store-scraper reviews call |
| App metadata only | Apple’s public iTunes lookup endpoint directly |
| Google Play reviews, small volume | google-play-scraper |
| More than 500 reviews per app, or many apps and countries | A hosted Apple App Store Scraper API |
| Repeated 401s, broken imports, or proxy management | A hosted API |
For where these tools stack up against each other on coverage and reliability, I put numbers behind the comparison in best App Store scrapers and APIs in 2026. If you are deciding which store surface to commit to first, the App Store Search Scraper and Android App Data Scraper endpoints are the two I lean on most.
The short version of my test: the name app-store-scraper covers two packages that behave nothing alike in 2026. The Python one will not import on a current interpreter until you unpin its 2020 dependencies, and even then its review endpoint is gated. The npm one works, returns about 50 reviews a page, and tops out near 500. Confirm which package a tutorial means, isolate the Python install so its pinned requests stays contained, and keep a hosted fallback ready for anything past a quick experiment.
FAQ
Is app-store-scraper a Python or a Node package?
Both names exist and they are separate projects. The Python app-store-scraper on PyPI is at version 0.3.5 (published November 12, 2020). The npm app-store-scraper is at 0.18.0. They have different authors, different parameters, and different review limits, so confirm which ecosystem a tutorial targets before you copy its code.
Why does app-store-scraper fail to import on Python 3.13?
Version 0.3.5 pins requests==2.23.0 in its requirements. That pin installs urllib3 1.25.11, which imports urllib3.packages.six.moves, a shim removed from modern Python. On Python 3.13 the import raises ModuleNotFoundError: No module named 'urllib3.packages.six.moves'. I reproduced it in a clean virtualenv in June 2026. Pinning a newer urllib3 clears the import, and then the reviews endpoint returns 401.
What is the page parameter in app-store-scraper npm reviews?
In the npm package, store.reviews() accepts page (default 1, maximum 10) and sort (store.sort.RECENT by default, or store.sort.HELPFUL). Requesting page 11 throws Page cannot be greater than 10. Apple serves roughly 50 reviews per page through this feed, so 10 pages is about 500 reviews per app per country.
How do I scrape Google Play reviews in Python?
Use google-play-scraper. Its reviews() function takes count, lang, country, and sort=Sort.NEWEST, and returns a continuation token for the next batch. I pulled 5 newest Instagram reviews with it in seconds. The Google Play Reviews Scraper API is the hosted version of the same job.
What is the difference between app-store-scraper and a hosted App Store API?
The library runs on your machine, so you own the pinned dependencies, the IP rotation, and the empty-response debugging. A hosted API such as the Apple App Store Scraper API takes an app ID and returns parsed JSON, with the proxy and parsing work on the server side. For a one-off pull the library is enough. For continuous collection I use the API.