kestrel actors › Ecommerce
Amazon Best Sellers Scraper — Top 100 by Category & BSR
An amazon best sellers scraper that turns the Best Sellers and New Releases charts of any category into ranked rows: amazon best sellers rank, amazon asin, title, price, rating, rating count, image and URL — every entry on every page, in any of 20 marketplaces. No login, no API key, no browser.
Run Amazon Best Sellers Scraper on Apify →
Amazon best sellers scraper pricing: what one row costs
| Event | What it is | Price | Charged for |
|---|---|---|---|
product | Chart product row | $0.004 | One product from a Best Sellers, New Releases or Movers & Shakers chart: rank, ASIN, title, price, rating, rating count and image. Categories that return nothing are free. |
Pay per event, one per delivered product row. Free: status rows, every row removed by minRating or the cap, empty categories, and failed jobs. A product that sits in two categories' charts is delivered and billed once per run.
A chart page is 50 ranks, so one category's Top 100 is 100 rows; ten categories daily is 1,000 rows a day. Completing the lazy-loaded ranks is one small extra request per page (the page's own load-more call), so a Top 100 run is four requests in total — platform compute and proxy traffic stay at a fraction of a cent. Exact per-row pricing is on this page's pricing tab.
Amazon best sellers scraper input reference
Every field of Amazon Best Sellers, straight from the actor's input schema.
| Field | Type | Default | What it does |
|---|---|---|---|
categories | array | required | Category slugs to pull the chart for, e.g. "electronics", "kitchen", "books", "toys-and-games" — the segment after /gp/bestsellers/ in a Best Sellers URL. Nested paths work too. |
chart | string (enum) | "bestsellers" | Best Sellers (the standing sales-rank chart) or New Releases (the newest products gaining traction), pulled for each category above. One of: bestsellers, new_releases. |
pages | integer | 2 | A chart page holds 50 ranks, so 2 pages = the Top 100. Walking stops early when a page adds nothing new. Range 1-20. |
maxResultsPerCategory | integer | 0 | 0 = every entry on the pages requested. N = the top N only — the main cost control. Lazy-loaded entries are only requested for ranks you asked for. Range 0-2000. |
minRating | number | 0 | 0 = keep everything. 1-5 = keep only products rated at or above this. Filtered products are never charged. Range 0-5. |
fillRanks | boolean | true | Amazon renders 30 of a page's 50 entries and lazy-loads the rest. On (default), the missing ranks are fetched with the page's own load-more call (one small request per page; product pages only as a fallback) so the chart has no gaps. Off = only the entries the page renders (30 per page, one request). |
domain | string | "com" | Amazon marketplace: com, co.uk, de, fr, it, es, ca, com.au, co.jp, in, com.mx, com.br, nl, se, pl, sa, ae, sg, com.tr, com.be. |
sessions | integer | 4 | How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16. |
perIp | number | 0.5 | Pace for each session. Amazon tolerates about 0.5/s per IP; faster causes captchas, not speed. Range 0.1-2. |
proxyConfiguration | object | {"useApifyProxy": true} | Apify Proxy is required: Amazon captchas datacentre traffic that is not rotated. RESIDENTIAL works best. |
Example input
{ "categories": ["electronics"], "chart": "bestsellers", "pages": 2 }
{ "categories": ["kitchen", "home-garden", "pet-supplies"], "chart": "new_releases", "pages": 1, "maxResultsPerCategory": 20 }
Amazon best sellers scraper output fields
Two row types share one dataset: product (one chart entry with its rank, charged) and status (per category, free).
Dataset views in the Apify Console: Overview, Chart, Run status.
| Field | Type | What it holds |
|---|---|---|
type | string | Row type: product or status. |
rank | integer | The product's position in the chart — its best seller rank within the category. |
asin | string | Amazon's product id — feed these straight into an Amazon reviews run. |
title | string | Product title. Null only when a lazy-loaded entry's product page was unavailable. |
url | string | Product page URL. |
chart | string | bestsellers or new_releases. |
category | string | The category slug this chart was pulled for. |
page | integer | Which chart page the entry sits on (50 ranks per page). |
price | number | Price as a number, when one is shown. |
price_display | string | Price exactly as printed. |
currency | string | Currency actually rendered on the price (Amazon localises it to the caller). |
rating | number | Star rating out of 5. |
ratings_count | integer | How many ratings the product has. |
image | string | Product image URL. |
sponsored | boolean | Always false: charts carry no ads. Kept so exports line up with the Amazon Search Scraper dataset. |
query | string | Always null here (search rows only). Kept for column parity with the Amazon Search Scraper. |
position | integer | Always null here (search rows only). Kept for column parity with the Amazon Search Scraper. |
domain | string | Marketplace domain suffix. |
marketplace | string | Marketplace country name. |
status | string | Status rows: ok, no_results or error. |
target | string | Status rows: the category this job was given. |
kind | string | Status rows: always chart. |
products | integer | Status rows: product rows delivered. |
filtered | integer | Status rows: rows dropped by minRating, never charged. |
duplicates | integer | Status rows: ASINs another category already delivered. |
partial | integer | Status rows: delivered entries whose product page was unavailable (rank, ASIN and URL only). |
pages | integer | Status rows: how many pages were requested. |
error | string | Status rows: why the job failed, null when it did not. |
fetched_at | string | UTC timestamp when the row was fetched. |
Example row from a real run
Taken from the actor's README — this is the shape your dataset holds.
{
"type": "product", "rank": 31, "asin": "B0CFPJYX7P", "title": "Example Wireless Earbuds, Bluetooth 5.4",
"chart": "bestsellers", "category": "electronics", "page": 1,
"price": 24.99, "price_display": "$24.99", "currency": "USD",
"rating": 4.5, "ratings_count": 18342,
"image": "https://m.media-amazon.com/images/I/example.jpg",
"url": "https://www.amazon.com/dp/B0CFPJYX7P", "domain": "com", "marketplace": "US",
"query": null, "position": null, "sponsored": false,
"fetched_at": "2026-08-29T06:20:11+00:00"
}
Amazon best sellers scraper in Python, JavaScript and curl
Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/amazon-best-sellers-scraper").call(run_input={
"categories": ["electronics"], "chart": "bestsellers", "pages": 2,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["type"] == "product":
print(row["rank"], row["asin"], row["price"], row["rating"], row["title"])
JavaScript (Node)
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/amazon-best-sellers-scraper').call({
categories: ['kitchen'], chart: 'new_releases', pages: 1, maxResultsPerCategory: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'product').map((p) => [p.rank, p.asin, p.title]));
curl
curl -X POST "https://api.apify.com/v2/acts/kestrel~amazon-best-sellers-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H 'Content-Type: application/json' \
-d '{"categories":["electronics"],"pages":1,"maxResultsPerCategory":20}'
The same three calls are runnable files in the examples repository: examples/amazon-best-sellers-scraper.py|.js|.sh.
Limits and honest notes
rankis the chart position, not units sold. The marketplace does not publish sales volume;ratings_countand its change between runs are the closest public proxies.- Prices are what the page shows, in the currency it rendered. The store localises prices to the caller's location, so a
comrun can return a few rows in another currency;currencyis read from the price itself, never assumed from the marketplace. A row showsprice: nullwhen the page prints a range or no price at all. - The product-page fallback is rare but slower. When the load-more call is refused, each missing entry costs a product-page fetch; details from a product page occasionally print a different price or image size than the chart tile would. Both are the product's own data.
- With
minRatingset, an entry whose details could not be read has no rating and is filtered — free, and counted in the status row'sfiltered. Without a rating filter it is delivered with itsrank,asinandurland counted underpartial. - Movers & Shakers is not offered. That chart is rendered entirely in the browser — the page as served carries no entries — so it cannot be read honestly with this approach. See the FAQ.
- A captcha is served to repeat callers. The run detects it and rotates to a fresh IP rather than parsing the captcha page as an empty chart.
- Cheap proxies first, residential only when the store pushes back. A run starts on the proxies you chose — datacentre is the cheapest — and rotates to a fresh IP on a captcha or a 503. When the same page is refused twice in a row that is the IP range being blocked, not the page, so the run stops trusting that pool: its sessions re-open on a residential proxy and stay there for the rest of the run. Nothing to configure, no run has to fail to teach it, and a run that is never refused never leaves the cheap pool — so a run costs the least that still succeeds. The run's SUMMARY reports it as
http.escalated. - Slugs differ between marketplaces. If a category returns
no_results, open the chart in a browser on that marketplace and copy the segment exactly as it is written.
FAQ
How many products does one run return?
50 per page per category. pages: 2 is the Top 100; pages: 4 is the Top 200 where the category goes that deep. maxResultsPerCategory caps each category independently, and the walk stops on its own when a page adds nothing new.
Why do other chart scrapers return 30 entries per page?
Because that is how many the page renders as HTML — the other 20 are lazy-loaded by the browser. This actor reads the page's complete ranked list and makes the page's own load-more call for the rest (falling back to product pages if that is refused), so no rank is skipped. Turn fillRanks off if you would rather have the 30 rendered entries and a single request per page.
Can I track Amazon BSR over time?
Yes — that is the main use. Schedule the run, keep the dataset, and rank plus fetched_at give you rank history per ASIN per category. Join two days on asin to get movement, entries and exits.
Does it need an Amazon API key?
No. The Product Advertising API needs an affiliate account with ongoing sales requirements, and it does not expose the chart as a shopper sees it. This reads the public pages.
Which categories can I use?
Any node that has a Best Sellers page: top-level slugs like electronics or books, and nested paths copied from a chart URL. The slug is the part after /gp/bestsellers/; New Releases uses the same slugs.
What about Movers & Shakers, Most Wished For and Gift Ideas?
Amazon movers and shakers is rendered client-side — the served page carries no entries — so it is not offered rather than offered broken. Amazon most wished for and Gift Ideas are not charts this actor pulls today. If you need one of them, open an issue on this page; demand decides what gets built next.
Is the rank the same as the BSR on a product page?
It is the product's position in this category chart. A product page prints its rank in several nodes at once (a top-level category and one or more sub-categories); the chart for a given node lists products in that node's rank order, so the two agree for the node you pulled.
Does it return the ASIN so I can scrape reviews next?
Yes, every product row carries asin. Feed those into the Amazon Reviews Scraper and you have rank and price from here, customer opinion from there.
Which marketplaces are supported?
Twenty, listed in the input table. Set domain per run; the same category in two marketplaces should be two runs, because ranks are not comparable across them.
Can I export to CSV or Excel?
Yes. Every run writes an Apify dataset, exportable to CSV, Excel, JSON or XML from the Console, or over the API with ?format=csv.
Is there a free tier?
Empty categories, filtered rows, status rows and failed jobs are always free, so a run that finds nothing costs nothing beyond a fraction of a cent of platform compute. Apify's free plan includes monthly credit you can spend here.
How to use Amazon Best Sellers Scraper
Ready-made runs you can open and start
Each one is a saved input on the Apify Store — open it, press Start, and it runs with the fields already filled in. All 163 across the suite.
| Ready-made run | What it does |
|---|---|
| Amazon Top 100 Products by Category | Two chart pages is the Top 100, and fillRanks fetches the twenty ranks Amazon lazy-loads, so all hundred rows come back for three categories. |
| Amazon New Releases by Category | The New Releases chart is where a category tells you what is arriving. Two categories, fifty ranks each, on a schedule that shows what sticks. |
| Amazon BSR History from a Daily Run | Amazon publishes no rank history. One category pulled every day at the same hour builds it: rank, ASIN and date, appended run after run. |
| Best Sellers List Across 10 Categories | Ten categories, top twenty each: a cross-category league table in one run, for spotting which departments a brand already leads. |
| Scrape Amazon Best Sellers on amazon.co.uk | The UK charts are a different ranking from the US ones: two categories on amazon.co.uk, a hundred ranks each, prices in pounds. |
| Amazon Best Sellers Rank on amazon.de | German charts for beauty and kitchen: a hundred ranks each with German titles and euro prices, the ranking a DE seller is actually judged against. |
| Amazon Sales Rank Charts for Japan | Japanese best sellers for electronics and toys: fifty ranks each in yen, a market where the leading brands differ from every Western chart. |
| Amazon Product Research Tool: 4+ Stars | minRating 4 drops weakly rated chart entries before billing, so what is left is products that both sell and satisfy - the sourcing shortlist. |
| Amazon Best Sellers CSV, 1000 Ranks Deep | Twenty chart pages of one category: a thousand ranked ASINs with price and rating, exported as CSV - the long tail, not just the top hundred. |
| BSR Tracker at 30 Ranks a Page (Cheaper) | fillRanks off takes only the ranks Amazon renders, about thirty a page: two-thirds of the rows, a third of the requests, and a faster daily run. |
Related scrapers
Run Amazon Best Sellers Scraper
The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/amazon-best-sellers-scraper