# Amazon Reviews & Ratings Scraper: Histogram, Top Reviews (`crawleast/amazon-reviews-snapshot`) Actor

No-login Amazon snapshot: product face, rating aggregates with the 5-star histogram and the top reviews with full text (up to 8 US plus up to 5 other-country). $0.02 per snapshot, $0.008 ratings-only. Failed lookups are free, and so is any product with no rating data at all.

- **URL**: https://apify.com/crawleast/amazon-reviews-snapshot.md
- **Developed by:** [Kyle Wang](https://apify.com/crawleast) (community)
- **Categories:** E-commerce, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 product & reviews snapshots

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Amazon Reviews Snapshot — Ratings, Histogram & Top Reviews

One ASIN in, one snapshot out. **No login, no cookies, no account of yours at risk** — every request is an ordinary anonymous product-page fetch.

For each product you get:

- the **full product face**: title, brand, price (and list price), availability, feature bullets, hi-res main image
- the **rating aggregates**: average stars, total rating count and the full **5-star histogram**
- the **top reviews with complete text**: author, stars, headline, date, reviewer country, Verified Purchase badge, the variation bought and the helpful count

> ### Please read this before you buy
>
> **This Actor returns Amazon's top-review block and never paginates.** In our cloud measurements that block is **up to 8** reviews from the US marketplace plus, on many products, **up to 5** more that Amazon labels *top reviews from other countries* — measured up to 13 rows, 8 on many products, and fewer when a product simply has fewer reviews (a 50-ASIN batch measured 13 on 14 products, 8 on 13, 9 on one and 1 on one). Every review carries a `country` field, so you can tell the two blocks apart.
>
> Those top reviews are all an anonymous session is ever served. Full review export does not exist without logging in, and this Actor will never ask you for your account, your cookies or your credentials. If you need thousands of reviews per product, this is not the tool.
>
> What it is instead: the **zero-friction** way to get a product's rating picture and its most influential reviews in one call, with nothing to configure and nothing to rotate.

### Example output

Real output for `B09B8V1LZ3`, copied from cloud run `ZChVn0u530yL9mpU4` (2 of 6 feature bullets and 1 of 8 review objects shown here; the Actor returns them all, with every review body in full):

```json
{
  "asin": "B09B8V1LZ3",
  "url": "https://www.amazon.com/dp/B09B8V1LZ3",
  "fetchedAt": "2026-10-24T02:43:08.901Z",
  "mode": "snapshot",
  "fetchStatus": "ok",
  "blockReason": null,
  "title": "Amazon Echo Dot (newest model) - Vibrant sounding speaker, Designed for Alexa+, Great for bedrooms, dining rooms and offices, Charcoal",
  "brand": "Amazon",
  "priceAmount": 79.99,
  "priceCurrency": "USD",
  "priceIsRange": false,
  "listPriceAmount": null,
  "availability": "In Stock",
  "bulletPoints": [
    "Your favorite music and content – Play music, audiobooks, and podcasts from Amazon Music, Apple Music, Spotify and others or via Bluetooth throughout your home.",
    "Alexa is happy to help – Ask Alexa for weather updates and to set hands-free timers, get answers to your questions and even hear jokes. Need a few extra minutes in the morning? Just tap your Echo Dot to snooze your alarm."
  ],
  "mainImage": "https://m.media-amazon.com/images/I/61J2sQtBYDL._AC_SL1500_.jpg",
  "ratingAverage": 4.7,
  "ratingCount": 201344,
  "histogram5": 82,
  "histogram4": 11,
  "histogram3": 3,
  "histogram2": 1,
  "histogram1": 3,
  "reviews": [
    {
      "position": 1,
      "reviewId": "R2R4AN7J0D9RVL",
      "author": "VO",
      "profileUrl": "/gp/profile/amzn1.account.AHCLNOUDY6KCOP54E3TS3JGWILBQ",
      "rating": 5,
      "title": "Great Little Speaker!",
      "date": "2026-10-16",
      "dateRaw": "Reviewed in the United States on October 16, 2026",
      "country": "United States",
      "verified": true,
      "variation": "Color: Charcoal | Size: Pack of 1 | Configuration: Device only",
      "body": "I absolutely love my Echo Dot! It has great sound quality, is super easy to set up, and makes everyday life so much easier. I love being able to ask A …",
      "helpfulCount": 1
    }
  ],
  "reviewsCount": 8,
  "reviewsCoverage": "top",
  "variantHit": true,
  "variantRetries": 0,
  "attempts": 1,
  "bytesFetched": 2135257,
  "proxyMode": "DATACENTER",
  "billedEvent": "snapshot"
}
```

### Pricing

Pay per event. **No startup fee, and failed lookups are free.**

| Event | Price | You get |
|---|---|---|
| `snapshot` | **$0.02** per product | Product face + rating aggregates + histogram + the top reviews with full text |
| `ratings` | **$0.008** per product | Product face + rating aggregates + histogram, no review text |
| *(nothing)* | **$0.00** | Blocked, not-found and error rows, and any product that came back with no rating data at all — delivered so you can see what happened, never charged |

Three things worth knowing:

- A product whose page variant never showed reviews is **delivered and billed as `ratings`, not `snapshot`**. You pay for what you received. The row says so explicitly: `reviewsCoverage: "none"`, `variantHit: false`, and `reviews: []`.
- A product that came back with **no rating average, no rating count and no histogram** is delivered **free**, with `billedEvent: null` and `freeReason: "no_rating_data"`. Old, new and not-yet-rated products are common in bulk lists; the product face is still yours, the ratings tier is not charged because there was nothing in it. `RUN_SUMMARY.delivered.okUncharged` counts them.
- Retrying the review draw **never raises your bill**. Every row is charged once, at most.

`billedEvent` on each row and `RUN_SUMMARY.billing` (intended vs. actually charged, per event) make the bill fully auditable.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `asins` | string list | `B09B8V1LZ3` | ASINs or amazon.com `/dp/` URLs, one per line. Duplicates collapse. |
| `includeReviews` | boolean | `true` | `false` = the ratings-only tier: same product face and aggregates, no review text, one request per product. |
| `maxItems` | integer | `100` | Cap per run; the rest land in `skippedBy.unattempted`. Also sets the zero-delivery fuse: `max(2, 10% of the batch)` consecutive walled products. |
| `maxVariantRetries` | integer | `3` | Extra review draws per product. See the measured numbers below. |
| `proxyCountry` | select | `US` | Only US in this version — amazon.com prices and availability are US. |
| `maxTotalChargeUsd` | number | `20` | Hard budget cap; the run stops gracefully when it is reached. The platform minimum, $0.04, buys 5 ratings-only rows or 2 full snapshots. |

Only **amazon.com** is supported. `.de` / `.co.uk` / `.co.jp` URLs are rejected before any request is made and are not charged.

### How the review draw works

Amazon renders the top reviews into the product page's HTML only in **some** of its anonymous server-side variants. The same product can come back with reviews on one request and without them on the next, so the Actor simply asks again through a **fresh proxy session** until the block appears.

Measured, not modelled — and it moves. Pooled across **23 snapshot-mode cloud runs at the default `maxVariantRetries: 3`: 101 products, 163 draws** (`node cloud-tests/cost-report.mjs` recomputes this from the archived evidence whenever you want the current figure).

- **First draw: 37 of 101 products (37%) came back with reviews.**
- **By the fourth draw: 60 of 101 (59%)**, at 1.61 draws per product. The 41 misses were still delivered with their product face and rating aggregates, and billed at the cheaper `ratings` price.
- **These numbers have moved before, in both directions.** The same pool read 57% first draw / 89% by the fourth draw on 2026-10-24 (n=54), and the single largest batch ever run measured 62.5% / 90.6% (run `WrgFps9vWRKPLhw0b`). On 2026-10-26 Amazon was walling much harder — 28 of 50 products in a 50-ASIN batch came back `blocked` — and the products that did get through carried the review block far less often. Read any single figure as a snapshot of Amazon's current mood, not as a specification.

| `maxVariantRetries` | Draws | What it buys (measured, n=101, pooled 2026-10-26) |
|---|---|---|
| 0 | 1 | 37% of products came back with reviews on the first draw. |
| **3 (default)** | **4** | 59% came back with reviews, at 1.61 draws per product. |
| 6 | 7 | The ceiling. Each extra draw costs a few seconds and 2–4 MB of transfer, and of the 7 products that ever reached a fourth draw only 2 got reviews from it — so draws five to seven buy very little. |

`RUN_SUMMARY.variantStats.hitRateByDraw` reports the real per-draw hit rate of *your* run. That is the only figure that describes the batch you actually paid for, and it is the one to tune against.

### Output

**Dataset** — one row per requested ASIN. See the example above for the full shape; the fields you will use most:

| Field | Meaning |
|---|---|
| `fetchStatus` | `ok`, `blocked`, `notFound` or `error`. Only `ok` rows are billed. |
| `blockReason` | Why a non-`ok` row happened: `robot_wall`, `guard`, `soft_wall`, `signin_wall`, `arbitration`, `region_redirect`, `no_product_face`, `not_found`, `network`, `parse`. |
| `ratingCount` | Ratings behind `ratingAverage`, scoped to this product only (never a neighbouring carousel's count). |
| `histogram5`…`histogram1` | Star shares in whole percent; they sum to 100 ±1 from rounding. |
| `reviews[].country` | Which marketplace the reviewer wrote from. Amazon's own split: the first block is US, the rest are labelled *from other countries*. |
| `reviewsCount` | How many review rows came back: up to 8 from amazon.com plus up to 5 from Amazon's other-country block — measured up to 13, 8 on many products — and 0 when no variant carried the block or in ratings-only mode. A product with fewer reviews than that yields fewer rows. |
| `reviewsCoverage` | `top` = reviews parsed, `none` = no variant carried them, `null` = ratings-only mode. |
| `priceIsRange` | `true` when Amazon showed a variation "from" price rather than one offer's price. |
| `attempts`, `bytesFetched`, `proxyMode` | Cost telemetry for that product. |

**Key-value store**

- `RUN_SUMMARY` — the full report: requested/delivered counts, `skippedBy`, `variantStats`, field `coverage`, request and byte usage, the `billing` ledger and a `completeness` identity check proving every requested ASIN ended in exactly one terminal state.
- `OUTPUT` — the one-glance version (processed, delivered, withReviews, variantHitRate, chargedUsd, trigger).

### Limits, honestly

- **The top-review block only: no pagination, no filtering, no sorting.** This is Amazon's anonymous ceiling, not a throttled tier: up to 8 US reviews plus up to 5 that Amazon labels as from other countries, and fewer than that only when the product has fewer reviews.
- **amazon.com only.** Other storefronts are rejected up front.
- **US catalogue only.** Prices, availability and reviews are amazon.com's.
- **No account support, ever.** There is no input for cookies, sessions or credentials, and one will not be added.
- **Every wall is retried, and retries are free.** Only a 404 ends a product on the first request; anything Amazon serves instead of a product page is tried again on a fresh proxy session, up to four attempts. A product that is still walled after that is delivered as a free `blocked` row.
- **Requests leave through Apify's datacenter proxy by default.** Datacenter transfer is not metered on our plan, so it measured roughly 3× cheaper per delivered product than a residential exit ($0.0010 vs $0.0033 on the same 50-ASIN batch). In that paired test the residential arm also hit Amazon's automated-access wall on 2 of its first 4 products and tripped the zero-delivery fuse, while the datacenter arm delivered 32 of 37. One paired run is not a verdict on wall rates — but it gave us no reason to pay 8× per GB for the other exit. Either way: expect a minority of products to come back `blocked` on any given run. Those rows are free, and re-running just them usually works.
- **The run window, not memory, is the ceiling.** The Actor caps its own working budget inside the platform timeout; products it cannot reach in time are listed in `skippedBy.deadline` with `partial: true`, and nothing undelivered is billed. Each product costs one fetch plus up to `maxVariantRetries` extra draws, so review mode with a high retry setting is what eats the window. Split very large jobs across runs. Entries that cannot be read as an ASIN, product URL or image URL are listed in `RUN_SUMMARY.invalidInputs` with a reason each and are not charged — and they do **not** set `partial`, because re-submitting them gets the same verdict. `partial` means the Actor stopped short (budget, deadline, fuse or `maxItems`), not that the input list had a typo in it.
- **A walled exit stops the run early, and the threshold scales with your batch.** After `max(2, 10% of the batch)` consecutive products come back walled or unreachable the run stops rather than spending your budget on requests Amazon will not answer: 2 for batches up to 20, 5 for 50, 10 at the 100 default. Everything it never got to is listed in `skippedBy.fuse`, uncharged, and `humanReadableReason` says how many. Walls are an exit-IP state, not a verdict on those ASINs — re-run the tail in a smaller batch, or later.
- **Runs need 1024 MB.** A 37-product batch peaked at 862 MB, so 512 MB is offered nowhere: the default is 2048 MB and the floor is 1024 MB. Compute is deducted from our payout, not added to your bill.
- **A killed run still leaves an account.** Once anything has been charged, `RUN_SUMMARY` is refreshed every 10 seconds from the loop and again on SIGTERM/SIGINT, so an abort, a hard timeout or an out-of-memory kill leaves a readable ledger behind rather than only the platform's charge counter. Such a copy carries `partial: true` and a `persistReason` naming which path wrote it, and its numbers are a **lower bound** — the platform's own counter is authoritative for what you were charged.
- **A tight budget downgrades a product instead of dropping it.** When the room left in `maxTotalChargeUsd` will not cover a `snapshot`, that product is fetched as ratings-only: the row says `mode: "ratings"`, is billed at the ratings price, and `RUN_SUMMARY.notes` counts how many products were downgraded. Note that Apify's own `maxTotalChargeUsd` floor is $0.04, which buys five ratings rows or two snapshots — below that the run delivers nothing at all.
- **Amazon changes its front end.** When it does, the affected field comes back `null` and the field name is listed in `RUN_SUMMARY.notes` as `PARSE_ERRORS` — the rest of the row still delivers. Report it and the anchor gets fixed.

### Troubleshooting

| Symptom | What it means |
|---|---|
| `reviewsCoverage: "none"` on many products | Raise `maxVariantRetries`, or accept the ratings-only price — you were billed $0.008, not $0.02. |
| `fetchStatus: "blocked"`, any `blockReason` except `not_found` | Amazon walled all four attempts at that product, each through a fresh proxy session. These walls are transient — the same ASIN typically delivers minutes later — so re-run just those ASINs. Not charged. |
| `blockReason: "not_found"` | HTTP 404: the ASIN is dead or withdrawn. Retrying will not help. Not charged. |
| `zeroDeliveryFuse: true` | `RUN_SUMMARY.fuseLimit` consecutive products came back walled or unreachable (2 on a small batch, 10% of a large one), so the run stopped instead of burning your budget. Nothing after the fuse was attempted or charged; the count is in `skippedBy.fuse`. A `notFound` does **not** count toward this: a 404 is Amazon answering normally about a dead ASIN, so a list that starts with dead products still runs through to the live ones behind them. |
| `trigger: "budget"` | `maxTotalChargeUsd` was reached. Raise it or lower `maxItems`. At the $0.04 platform minimum the Actor delivers ratings-only rows rather than nothing. |
| `freeReason: "no_rating_data"` | The product page had a title and a product face but no rating average, no rating count and no histogram — usually a brand-new, withdrawn or never-rated ASIN. The row is delivered and **not charged**. |
| Empty run, `errorCode: "VALIDATION_ERROR"` | No input could be read as an ASIN — see `RUN_SUMMARY.invalidInputs`. Zero charges. |

### Support

Issues and questions through the Actor's page. If a field comes back `null` on a product where Amazon clearly shows it, open an issue with the ASIN — that is a markup change and it gets fixed.

# Changelog

This Actor's version history is a separate document: https://apify.com/crawleast/amazon-reviews-snapshot/changelog.md

# Actor input Schema

## `asins` (type: `array`):

Amazon ASINs (B09B8V1LZ3) or amazon.com product URLs (/dp/<ASIN>, /gp/product/<ASIN>), one per line. Bare ASINs must be 10 characters of A-Z/0-9 (so ISBN-10 book ASINs work too); anything that cannot be normalised to an ASIN is reported in RUN_SUMMARY.invalidInputs and never charged. Duplicates are collapsed, so the same ASIN is fetched and billed once per run. Only amazon.com is supported in this version — non-US storefronts (.de/.co.uk/.jp) are rejected uncharged.

## `includeReviews` (type: `boolean`):

On (default): full snapshot — product face + rating aggregates + the top reviews with complete text, billed $0.02 per product. Off: ratings-only — product face + average rating + review count + 5-star histogram and no review text at all, billed $0.008 per product. Turn it off when you only need rating analytics at scale. Amazon embeds only the top reviews in an anonymous product page: up to 8 from amazon.com and, on some products, up to 5 more that Amazon labels as from other countries. Neither mode paginates and neither needs your account.

## `maxItems` (type: `integer`):

Cap on how many distinct ASINs one run processes, first come first served; the rest are reported in skippedBy.unattempted. The zero-delivery fuse scales with this batch: it stops the run after max(2, 10% of the batch) consecutive walled products, and everything it never got to is listed in skippedBy.fuse, uncharged. The run window, not memory, is the binding limit: the Actor caps its own working budget well inside the platform timeout and cuts the remainder with trigger="deadline" (partial: true, nothing unbilled). Each product costs one page fetch plus up to maxVariantRetries extra draws, so review mode with a high retry setting is what eats the window. Split very large jobs across runs.

## `maxVariantRetries` (type: `integer`):

Amazon only embeds the top reviews in the server-rendered product page for SOME anonymous requests, and the same product can come back with reviews on one request and without them on the next. When a draw has no review block the Actor re-requests the page through a fresh proxy session, up to this many extra draws. Measured, not modelled, and it moves with how hard Amazon is walling: pooled across 23 cloud runs at this setting (101 products, 163 draws, 2026-10-26) 37 of 101 first draws carried reviews and 60 of 101 products had reviews by the fourth draw (1.61 draws per product); the same pool read 57% and 89% two days earlier. 3 is still the value that pays for itself; 0 = one draw, 6 = the ceiling. RUN_SUMMARY.variantStats.hitRateByDraw reports your own run's real per-draw rate. Retries never raise your bill — a product that still shows no reviews after the last draw is delivered with reviewsCoverage="none", variantHit=false and billed at the $0.008 ratings-only price. Ignored when includeReviews is off.

## `proxyCountry` (type: `string`):

Country of the proxy exit used for the requests. Only US is supported in this version: amazon.com serves its prices, availability and reviews to a US exit, and that is the catalogue this Actor reports on. Fixed to US.

## `maxTotalChargeUsd` (type: `number`):

Hard budget cap for this run. The Actor checks the cap before starting each product: when the next product cannot be delivered within it, the run stops gracefully, marks the remainder in skippedBy.budget and reports trigger="budget". Everything already delivered stays delivered and billed. Minimum 0.04 (Apify platform limit), which buys 5 ratings-only rows or 2 full snapshots; the gate reserves the cheaper tier, and a product that cannot be afforded as a full snapshot is fetched ratings-only rather than dropped. A run can cost at most maxItems x $0.02 — $4.00 at the 200-item ceiling, $2.00 at the default 100 — so the default $20 never binds; lower it if you want a ceiling you can rely on.

## Actor input object example

```json
{
  "asins": [
    "B09B8V1LZ3",
    "https://www.amazon.com/dp/B0CHX1W1XY"
  ],
  "includeReviews": true,
  "maxItems": 100,
  "maxVariantRetries": 3,
  "proxyCountry": "US",
  "maxTotalChargeUsd": 20
}
```

# Actor output Schema

## `results` (type: `string`):

Every requested ASIN in processing order: product face, rating aggregates with the 5-star histogram, the top reviews with complete text, and the fetch/billing status of that row.

## `overview` (type: `string`):

The dataset as a flat table: title, brand, price, average rating, rating count, how many Top reviews came back and what each row was billed as.

## `ratings` (type: `string`):

The dataset reduced to rating analytics: average stars, total ratings and the 5-star histogram share per product.

## `runSummary` (type: `string`):

Machine-readable report: requested/delivered counts, skipped products by reason, review-variant hit rate by draw, field coverage, request and byte usage, and the full charge ledger (intended vs charged events and amount).

## `output` (type: `string`):

One-glance run result: processed, delivered, withReviews, variantHitRate, zeroDeliveryFuse, chargedEvents, chargedUsd and the stop trigger.

# 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 = {
    "asins": [
        "B09B8V1LZ3"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawleast/amazon-reviews-snapshot").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 = { "asins": ["B09B8V1LZ3"] }

# Run the Actor and wait for it to finish
run = client.actor("crawleast/amazon-reviews-snapshot").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "asins": [
    "B09B8V1LZ3"
  ]
}' |
apify call crawleast/amazon-reviews-snapshot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawleast/amazon-reviews-snapshot"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/QbZ68vuMaUDnpPRxj/builds/yRqTUq2KnBvQSJlcy/openapi.json
