How to scrape Amazon reviews, and what the real limit is
Any listing promising every Amazon review of a product is describing something a logged-out client cannot reach. The public product page renders 8 to 13 reviews and the full archive is behind a sign-in. Here is what is genuinely available, how to use it well, and how to tell a ceiling from a block.
The number nobody puts in the listing
Open any Amazon product page while logged out and count the reviews. You will find between eight and thirteen. Amazon renders a compact sample — a mix of recent and most-helpful — and puts the archive behind a signed-in session.
The /product-reviews/<ASIN>/ route, the one every tutorial from 2019 uses, is now login-walled. It does not fail cleanly: a logged-out request gets HTTP 200 with a roughly 325 KB sign-in page. A scraper that trusts the status code and counts review blocks finds zero and reports "this product has no reviews". That is the single most common source of silently wrong Amazon review data, and it is why any honest Amazon reviews actor checks the body for the sign-in marker rather than the status line.
So the ceiling is real, it applies to everyone, and no proxy pool moves it. What matters is what you can build under it.
What 13 reviews per product is actually good for
Depth per product is gone. Breadth across a catalogue is not, and breadth is what most Amazon review work needs:
- Complaint detection across a catalogue. Thirteen recent-and-helpful reviews per ASIN across 500 ASINs is 6,500 rows for about $32 — and Amazon has pre-selected the ones shoppers actually see, which is the set that moves conversion.
- Rating trend per product, cheaply. The free product row carries
rating,ratings_countandreviews_visible. Run it weekly and the delta inratings_countis the closest public proxy for review velocity. - Competitor monitoring. The same thirteen reviews are the ones on your competitor's page. What a shopper reads before buying is a more useful sample than a five-year archive.
- Verified-purchase filtering. Every row carries the verified flag, the helpful votes, the variant reviewed and the country the review was written in ("Reviewed in ..." — that is the country of writing, not the reviewer's residence).
What it is not good for: longitudinal review-text analysis, review-fraud studies that need the full distribution, or anything that needs reviews older than the page shows.
Working code
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/amazon-reviews-scraper").call(run_input={
"asins": ["B0BSHF7WHW"], "maxRating": 3, "verifiedOnly": True, "requireText": True,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["type"] == "product":
print(row["title"], row["rating"], "from", row["ratings_count"], "ratings;",
row["reviews_visible"], "reviews on the page")
elif row["type"] == "review":
print(row["rating"], row["country"], row["verified"], row["text"][:80])
Feeding a catalogue is the normal shape of the job — take ASINs from a search or a chart run and pass them straight in:
search = client.actor("kestrel/amazon-search-scraper").call(run_input={
"queries": ["mechanical keyboard"], "pages": 2, "domain": "com"})
asins = [r["asin"] for r in client.dataset(search["defaultDatasetId"]).iterate_items()
if r["type"] == "product" and r.get("asin")]
client.actor("kestrel/amazon-reviews-scraper").call(run_input={"asins": asins[:100], "maxRating": 3})
A real row, and the row that tells you the truth
{
"type": "review", "asin": "B0BSHF7WHW", "domain": "com", "marketplace": "US",
"rating": 5.0, "title": null,
"text": "I upgraded from the i9 Intel MacBook Pro from 2019, and this laptop seriously blows that away.",
"author": "Michael M", "country": "the United States", "date_text": "July 22, 2023",
"verified": true, "helpful_votes": 65, "variant": null,
"url": "https://www.amazon.com/gp/customer-reviews/RCA7TI5EBH5VK"
}
And the free product row alongside it:
{
"type": "product", "asin": "B0BSHF7WHW", "title": "Apple 2023 MacBook Pro Laptop",
"rating": 4.7, "ratings_count": 387, "reviews_visible": 13,
"price": null, "currency": "USD"
}
reviews_visible: 13 next to ratings_count: 387 is the contract. You can see exactly what you got and exactly what exists, without inferring either.
Note title: null on the review — the compact blocks Amazon renders on product pages frequently omit the headline. The body is always there.
Blocks versus ceilings, and how the run tells them apart
Amazon serves a captcha to repeat callers from one IP. That is a block: the run detects the captcha page, rotates to a fresh IP and retries, rather than parsing a challenge page as an empty product. It also escalates pools — a run starts on the cheap datacenter proxies you chose and only moves its sessions to residential when the same page is refused twice in a row, which is the signature of the range being blocked rather than the page being missing. A run that is never refused never leaves the cheap pool, so it costs the least that still succeeds.
The distinction matters because the two failure modes look identical in a naive scraper's output — zero rows — and only one of them is worth retrying.
Prices are a separate question
Amazon withholds price markup from unauthenticated clients often enough that you should not build a price tracker on the product page. On one 1.8 MB page in testing there was no price element at all. When a price is rendered it is the buybox offer only — not the marketplace's cheapest seller, not used offers, and null on pages where no buying option is shown.
Prices and ranks are reliably rendered on result and chart pages, which is where Amazon Search and Amazon Best Sellers read them. If you want price tracking, use those; if you want product attributes, images and stock, use Amazon Product.
Run it
Amazon Reviews Scraper — $0.005 per review row and $0.003 per product row, with status rows, unknown ASINs, duplicates and filtered reviews free. The Scope section on that page says the 8-13 ceiling in the listing itself, which is the test worth applying to any Amazon review scraper you consider: if the listing does not tell you the ceiling, the ceiling is still there.
Related: how to build a voice-of-customer dataset across Amazon, Trustpilot and the app stores, and which review sites can be scraped without a browser.
FAQ
How many Amazon reviews can a scraper actually get per product?
Between 8 and 13 from the public product page, ordered by Amazon's own mix of recent and most-helpful. The dedicated /product-reviews/ route is login-walled: a logged-out request returns a sign-in page that is served as HTTP 200 and is around 325 KB, so a scraper that does not check the body reports it as a successful empty page.
Why does the product row say ratings_count 387 but only 13 reviews came back?
Because those are different numbers. ratings_count is every star rating the product has ever received, most of which have no text at all. reviews_visible is how many review bodies were on the page you read. Both are in the free product row so the gap is visible rather than implied.
Can I get more reviews with a residential proxy or a browser?
No. This is a ceiling, not a block. A block changes with the IP or the fingerprint; a ceiling is what the page renders for anyone who is not signed in. Rotating proxies past it just means paying more for the same 13 rows.
Why is price often null on Amazon review runs?
Amazon frequently withholds price markup from unauthenticated clients — on one 1.8 MB product page in testing there was no price element at all. Treat price on a review run as a bonus. Prices and ranks are rendered reliably on search and chart pages, which is where the search and best-sellers scrapers read them.
Run Amazon Reviews Scraper on Apify →
Other actors used on this page: apify.com/kestrel/amazon-product-scraper, apify.com/kestrel/amazon-search-scraper.