kestrel actors docs

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

EventWhat it isPriceCharged for
productChart product row$0.004One 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.

FieldTypeDefaultWhat it does
categoriesarrayrequiredCategory 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.
chartstring (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.
pagesinteger2A chart page holds 50 ranks, so 2 pages = the Top 100. Walking stops early when a page adds nothing new. Range 1-20.
maxResultsPerCategoryinteger00 = 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.
minRatingnumber00 = keep everything. 1-5 = keep only products rated at or above this. Filtered products are never charged. Range 0-5.
fillRanksbooleantrueAmazon 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).
domainstring"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.
sessionsinteger4How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16.
perIpnumber0.5Pace for each session. Amazon tolerates about 0.5/s per IP; faster causes captchas, not speed. Range 0.1-2.
proxyConfigurationobject{"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.

FieldTypeWhat it holds
typestringRow type: product or status.
rankintegerThe product's position in the chart — its best seller rank within the category.
asinstringAmazon's product id — feed these straight into an Amazon reviews run.
titlestringProduct title. Null only when a lazy-loaded entry's product page was unavailable.
urlstringProduct page URL.
chartstringbestsellers or new_releases.
categorystringThe category slug this chart was pulled for.
pageintegerWhich chart page the entry sits on (50 ranks per page).
pricenumberPrice as a number, when one is shown.
price_displaystringPrice exactly as printed.
currencystringCurrency actually rendered on the price (Amazon localises it to the caller).
ratingnumberStar rating out of 5.
ratings_countintegerHow many ratings the product has.
imagestringProduct image URL.
sponsoredbooleanAlways false: charts carry no ads. Kept so exports line up with the Amazon Search Scraper dataset.
querystringAlways null here (search rows only). Kept for column parity with the Amazon Search Scraper.
positionintegerAlways null here (search rows only). Kept for column parity with the Amazon Search Scraper.
domainstringMarketplace domain suffix.
marketplacestringMarketplace country name.
statusstringStatus rows: ok, no_results or error.
targetstringStatus rows: the category this job was given.
kindstringStatus rows: always chart.
productsintegerStatus rows: product rows delivered.
filteredintegerStatus rows: rows dropped by minRating, never charged.
duplicatesintegerStatus rows: ASINs another category already delivered.
partialintegerStatus rows: delivered entries whose product page was unavailable (rank, ASIN and URL only).
pagesintegerStatus rows: how many pages were requested.
errorstringStatus rows: why the job failed, null when it did not.
fetched_atstringUTC 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

  • rank is the chart position, not units sold. The marketplace does not publish sales volume; ratings_count and 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 com run can return a few rows in another currency; currency is read from the price itself, never assumed from the marketplace. A row shows price: null when 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 minRating set, an entry whose details could not be read has no rating and is filtered — free, and counted in the status row's filtered. Without a rating filter it is delivered with its rank, asin and url and counted under partial.
  • 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 runWhat it does
Amazon Top 100 Products by CategoryTwo 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 CategoryThe 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 RunAmazon 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 CategoriesTen 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.ukThe 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.deGerman 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 JapanJapanese 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+ StarsminRating 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 DeepTwenty 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.

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