# Amazon Bestsellers Scraper — Full Product Row per Rank (`steadyfetch/amazon-bestsellers-scraper`) Actor

Name an Amazon category or paste a Best Sellers link and get every rank as a full product row: price, buy box, stock, rating, reviews, variants, images and specs. From $1.50 per 1,000 bestsellers, one flat fee, no add-on events. Blocked or empty categories are never charged.

- **URL**: https://apify.com/steadyfetch/amazon-bestsellers-scraper.md
- **Developed by:** [Steadyfetch Team](https://apify.com/steadyfetch) (community)
- **Categories:** E-commerce, Automation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 bestsellers

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Amazon Bestsellers · Flat Fee

**Name an Amazon category and get the full product row for every rank — price, buy box, stock, rating, reviews, variants, bestseller ranks, images and the full specifications table, all in one row. One flat fee per bestseller. There is no second event to pay for, and blocked or empty categories are never charged.**

Type `electronics`, or paste a Best Sellers link. Get finished product objects back, in rank order — not a name, a thumbnail and a link you then have to feed somewhere else.

> **Unofficial.** This actor is not affiliated with, endorsed by, or sponsored by Amazon. "Amazon" and all related marks are trademarks of Amazon.com, Inc. or its affiliates. It reads only the public Best Sellers pages an ordinary browser sees, and never signs in.

### Output

One row per rank: every field the product page carries, plus the list context. This is a real row from a verified run, trimmed with `…` where a field is long:

```json
{
  "rank": 2,
  "listType": "bestsellers",
  "listCategory": "electronics",
  "listCategoryName": "Electronics",
  "listCategoryPath": ["Electronics"],
  "listPage": 1,
  "asin": "B0GJTFXNRX",
  "marketplace": "amazon.com",
  "url": "https://www.amazon.com/dp/B0GJTFXNRX",
  "title": "Apple AirTag (2nd Generation): Tracker for Keychain, Wallet, and More",
  "brand": "Apple",
  "price": 29.0,
  "currency": "USD",
  "inStock": true,
  "buyBoxSeller": "Amazon.com",
  "rating": 4.5,
  "reviewCount": 9466,
  "images": ["https://m.media-amazon.com/images/I/61Qd9Z9caRL._AC_SL1200_.jpg", "…"],
  "variants": [],
  "categoryPath": ["Electronics", "GPS & Navigation"],
  "bestSellersRank": [{ "rank": 2, "category": "Electronics" }],
  "specs": { "Brand": "Apple", "…": "…" },
  "scrapedAt": "2026-08-27T00:00:00.000Z",
  "charged": true,
  "status": "ok"
}
```

**[Browse the full live example dataset →](https://api.apify.com/v2/datasets/Oo1sdqN1cMNWzlaEZ/items?clean=true\&format=json)** — one real run of the current code, unedited: the Electronics best sellers, two delivered product rows with the Sold-by cluster populated, one uncharged `not_a_product` row where Amazon's own subscription plan sits at rank #1, and the run's own summary row last.

`rank` is the number printed on the Best Sellers badge. `listCategoryPath` is the category's own place in Amazon's tree, read off the page — so a subcategory row tells you it is `["Electronics", "Headphones"]` without you having to look it up. A product ranked in two of your categories is returned **once**, tagged with the first list that surfaced it, and charged once.

Prices and review counts are parsed in the page's own locale, so `4,4 von 5 Sternen` is a rating of 4.4 and `(4.295)` on amazon.de is four thousand reviews, not four. **`price` can legitimately be `null`** — Amazon does not always print one, and that row still carries the title, rating, ranks, images and specs, with `priceNote` saying what the page showed instead. The Sold-by cluster (`buyBoxSeller`, `buyBoxSellerId`, `fulfilledByAmazon`, `soldByAmazon`) works the same way — when a served page names no seller, those four come back null and `buyBoxNote` says so on that row. And because best-seller lists are full of Amazon's own devices: on those pages `specs` are read from the Technical-details grid, while `categoryPath` and `bestSellersRank` come back empty because Amazon renders neither there — that is the page, not a parsing gap.

The last row of every run is a summary carrying `categories`, `ok`, `categoryMisses`, `productMisses`, `chargedRows` and `unchargedRows` — so the invoice reconciles from the dataset itself, without opening the billing tab.

### For AI agents & LLMs — copy this block

```
Actor: steadyfetch/amazon-bestsellers-scraper
Input: { "categories": ["electronics"], "maxItemsPerCategory": 20 }
Output: one row per rank; charged rows have status "ok" and charged true.
Pricing: one priced event ("bestseller"), all-inclusive, no start fee, no add-ons.
Misses: no_products / blocked / not_found / unavailable_in_marketplace / not_a_product /
        parse_failed / source_unavailable all ship as rows with charged:false — read `status`.
Notes: 60 ranks per category is the hard maximum (see below). Limits are hard limits.
       Omit an optional field or send it as null — both mean "use the default", the
       "marketplace" and "list" dropdowns included. A value they do not offer is not
       silently swapped for the default: the run returns one uncharged row naming the
       values that work. "categories" is the one field that must carry a real value.
       Safe to resurrect: a resumed run re-delivers and re-charges nothing.
```

Works over the Apify API, MCP, n8n and Make. Running it with an empty category list returns uncharged sample rows, so a workflow can be wired up and tested before it costs anything.

### What one ranked product costs

One priced event, `Bestseller`, and nothing else. No start fee, no per-category fee, no per-field fee, no "detail" upcharge. The store header reads **from $1.50/1,000 bestsellers**.

| Your Apify plan | Price per bestseller | Per 1,000 bestsellers |
|---|---|---|
| Apify Free plan | $0.006 | $6.00 |
| Bronze | $0.0035 | $3.50 |
| Silver | $0.0022 | $2.20 |
| **Gold, Platinum, Diamond** | **$0.0015** | **$1.50** |

The header shows the Gold price. The whole ladder is printed here because an actor whose promise is billing honesty cannot have an undisclosed tier ladder.

**What can fail, and what a failure costs you: nothing.** The two misses you will actually see are `no_products` (the page loaded and that category has no ranked list — usually a department name that does not exist in that store) and `blocked` (a robot check in front of every route we tried). Both ship as real rows carrying `charged: false`, as do `not_found`, `unavailable_in_marketplace`, `not_a_product`, `parse_failed`, `source_unavailable` and `invalid_input`. A row is charged only if it is a product.

### 60 ranks per category — and why not 100

Amazon's Best Sellers pages advertise a top 100 and **put 60 of it in the page**: ranks 1–30 on page one, ranks 51–80 on page two. Ranks 31–50 and 81–100 are filled in by the browser after the page loads, so they are not in the HTML at all — nobody can return them without driving a real browser, and we are not going to bill you for a browser.

So the cap is 60, the default is 60, and this paragraph exists instead of a "top 100" claim we cannot keep. Want more of a department? Pass its **subcategories** — each one is its own list of 60, and subcategory links work as input exactly like the top-level ones.

### Full product detail on every rank — on purpose

This actor returns the **full** product object for each rank: the same row **Amazon Product Scraper · Flat Fee** returns, plus the rank, the list, the category and the page it came from. There is deliberately no cheaper "list rows only" mode — that would need a second price, and this family sells one thing at one price.

### What you give it

| Input | Example |
|---|---|
| `categories` (one per line) | `electronics`, `kitchen`, or `https://www.amazon.com/gp/bestsellers/kitchen/` |
| `marketplace` | `amazon.com` (default), `amazon.co.uk`, `amazon.de` |
| `list` | Best Sellers (default), New Releases, Most Wished For |
| `maxItemsPerCategory` | `60` (default and maximum) |

A pasted link decides its own store and its own list, so you can mix `amazon.de` and `amazon.com` categories in one run. Both URL shapes Amazon serves work — `/gp/bestsellers/electronics/` and the `/Best-Sellers-Electronics/zgbs/electronics/` alias — and so do subcategory links.

**Department names are per store.** `electronics` is a department on `amazon.com` and `amazon.co.uk`; on `amazon.de` the same shelf is called something else. If a name does not exist in the store you picked, you get an uncharged row saying exactly that rather than a silently empty run.

**Movers & Shakers is not offered.** Amazon builds that grid in the browser: the page comes back at 330 KB with a heading and **zero rows in it**. We measured it, and we would rather say so than sell an always-empty list.

### Error codes — what each uncharged row means

| `status` | What happened | Charged | Worth re-running |
|---|---|---|---|
| `ok` | Complete product row | yes | — |
| `no_products` | The page loaded but that category has no ranked list — usually a department name that does not exist in that store | no | no |
| `blocked` | Amazon asked us to prove we are not a robot, on every route we tried | no | yes |
| `not_found` | A ranked product's page was gone by the time we fetched it | no | no |
| `unavailable_in_marketplace` | A ranked id was not served in that country's catalogue | no | no |
| `not_a_product` | The rank is one of Amazon's own subscription plans, not a product listing — it has no price, seller or reviews of its own | no | no |
| `parse_failed` | Amazon served a page in a layout we do not recognise. That one is on us | no | no |
| `source_unavailable` | Amazon dropped or throttled the request | no | yes |
| `invalid_input` | A category line could not be read — the row says what to type instead | no | no |
| `cap_reached` / `stopped_at_limit` | A limit you set stopped the run; earlier rows were delivered normally | no | see `resumeCursor` |

### Limits are exact

`Max products per category`, `Max products (whole run)`, `Max run seconds` and the run's own maximum total charge all stop the run *cleanly*: it still finishes as SUCCEEDED, the summary row names the one setting that stopped it, and `resumeCursor` lists the categories still to do. A run with room for two products does not buy a second page of ranks it cannot deliver.

**And they stay exact when the run moves.** Apify moves long runs between servers, and it restarts the container when it does. This actor re-reads its own results before it does anything else, so a moved or resurrected run does not fetch, deliver or charge for a single product you already have — it picks up at the next rank. Your product limit counts the rows you already received, so a run capped at 100 gives you 100, not 200. If a charge was interrupted between a row landing and being billed, that one charge is completed, and never made twice.

### FAQ

**How do I scrape Amazon best sellers?** Type the department name, or paste the Best Sellers link. No Amazon account, no PA-API keys, no seller credentials.

**Can I get New Releases and Most Wished For too?** Yes — same flat fee, same row. Pick the list, or paste one of those links.

**Why only 60 ranks?** Because 60 is what Amazon puts in the page. See the section above; ranks 31–50 and 81–100 are browser-side.

**What does it cost per 1,000 bestsellers?** From $1.50/1,000 on Gold and above; see the ladder above. Nothing else is billable.

**Why is a price null, or in the wrong currency?** Amazon did not print one, or served the page from an unexpected region — `priceNote` says which. The delivery location is pinned to the marketplace's own country to keep this rare.

### Honesty contract

Amazon changes its pages often. When a category can't be listed, or an item can't be fetched or parsed, we return it with an honest status and **never charge for it**.

This actor may fail when the platform changes things — failed items are never charged.

Issues are answered in about three hours during working hours. If a field ever comes back empty that should not be, open an issue with the category and it gets fixed.

### Steadyfetch Amazon suite

One actor per entry point, the same product row underneath. All-inclusive pay per event, no start fee,
charged only on delivery.

| What you paste | Actor |
|---|---|
| ASINs or product URLs | [**Amazon Product Scraper · Flat Fee**](https://apify.com/steadyfetch/amazon-product-scraper) |
| Search keywords | [**Amazon Search Scraper · Flat Fee**](https://apify.com/steadyfetch/amazon-search-scraper) |
| A Best Sellers category | **this actor** |
| A seller ID or storefront | [**Amazon Seller Scraper · Flat Fee**](https://apify.com/steadyfetch/amazon-seller-scraper) |

**The rest of the steadyfetch shelf** — same contract everywhere: all-inclusive pay per event,
no start fee, charged only on delivery.

| Family | Actors |
|---|---|
| Ad creative intelligence | [Facebook](https://apify.com/steadyfetch/facebook-ads-transcript-scraper) · [Google Ads video](https://apify.com/steadyfetch/google-ads-video-transcript-scraper) · [TikTok](https://apify.com/steadyfetch/tiktok-ads-transcript-scraper) · [LinkedIn](https://apify.com/steadyfetch/linkedin-ads-transcript-scraper) · [Google Ads text & OCR](https://apify.com/steadyfetch/google-ads-creative-text-scraper) |
| Trends & keywords | [Google Trends](https://apify.com/steadyfetch/google-trends-scraper) · [Trends Now](https://apify.com/steadyfetch/google-trends-now-scraper) · [Breakout keywords](https://apify.com/steadyfetch/breakout-keywords-scraper) · [Autocomplete keywords](https://apify.com/steadyfetch/google-keyword-suggest-scraper) · [Keyword volume & CPC](https://apify.com/steadyfetch/keyword-search-volume-scraper) · [Social trends](https://apify.com/steadyfetch/social-trends-scraper) |
| YouTube transcripts | [YouTube videos](https://apify.com/steadyfetch/youtube-transcript-scraper) · [YouTube channels](https://apify.com/steadyfetch/youtube-channel-transcripts) |
| Instagram | [Reel transcripts](https://apify.com/steadyfetch/instagram-reel-transcript-scraper) · [Profile posts](https://apify.com/steadyfetch/instagram-profile-posts) |
| Jobs | [Indeed](https://apify.com/steadyfetch/indeed-jobs-scraper) · [Career sites by domain](https://apify.com/steadyfetch/company-jobs-by-domain) · [Glassdoor](https://apify.com/steadyfetch/glassdoor-jobs-scraper) · [Multi-board](https://apify.com/steadyfetch/multi-job-board-scraper) · [Google Jobs](https://apify.com/steadyfetch/google-jobs-scraper) |
| **Amazon** | [Products](https://apify.com/steadyfetch/amazon-product-scraper) · [Search](https://apify.com/steadyfetch/amazon-search-scraper) · [Bestsellers](https://apify.com/steadyfetch/amazon-bestsellers-scraper) · [Sellers](https://apify.com/steadyfetch/amazon-seller-scraper) |
| Any media file | [Speech to Text · any link or file](https://apify.com/steadyfetch/media-transcriber) |

### Feedback & support

Open an issue on the actor's Issues tab with the category you used. Fields that come back empty when they should not are treated as bugs, not as expected behaviour.

# Actor input Schema

## `categories` (type: `array`):

Amazon categories, e.g. "electronics" or a link like https://www.amazon.com/gp/bestsellers/kitchen/. Send the field as a list — leaving it out is fine, but this one field cannot be null. Every optional field below accepts null — the Marketplace and List dropdowns included — and null there simply means "use the default". A dropdown value that is not on its list is answered with one uncharged row naming the values that work, never quietly swapped for the default.

## `marketplace` (type: `string`):

Which Amazon store to read. Amazon runs a separate catalogue — and a separate set of department names — per country. A pasted link decides its own store. Null or empty means amazon.com; a store that is not on this list is answered with one uncharged row naming the stores that are.

## `list` (type: `string`):

Which ranked list to read for the categories you typed as plain names. A pasted link decides its own list. Movers & Shakers is not offered: Amazon builds that grid in the browser, so the page carries no rows to read. Null or empty means Best Sellers; a list that is not on this one is answered with one uncharged row naming the lists that are.

## `maxItemsPerCategory` (type: `integer`):

How many ranks to return per category, in Amazon's own rank order. 60 is the ceiling because 60 is what Amazon puts on the page: ranks 1-30 on page one and 51-80 on page two. Ranks 31-50 and 81-100 are loaded by the browser and are not in the page at all.

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

Hard cap on delivered products across all categories. 0 means no run-wide cap.

## `maxRunSeconds` (type: `integer`):

The run stops cleanly before this and reports what is left, instead of being killed by the platform timeout.

## `includeVariants` (type: `boolean`):

Every colour, size or flavour of each product, with its own ASIN. Turn off for a smaller row; the price does not change.

## `includeSpecs` (type: `boolean`):

The product-details table (brand, model, dimensions, materials and so on). Turn off for a smaller row; the price does not change.

## Actor input object example

```json
{
  "categories": [
    "electronics",
    "kitchen"
  ],
  "marketplace": "amazon.com",
  "list": "bestsellers",
  "maxItemsPerCategory": 60,
  "maxItems": 0,
  "maxRunSeconds": 3600,
  "includeVariants": true,
  "includeSpecs": true
}
```

# Actor output Schema

## `bestsellers` (type: `string`):

One row per rank: the rank, the list it came from, the category and its path, plus price, currency, list price, stock, buy-box seller, rating, reviews, bought-last-month, variants, bestseller ranks, images, bullet points and the specifications table. Every row carries `charged` and `status`, so the invoice reconciles from the dataset itself.

## `unchargedRows` (type: `string`):

Categories that listed nothing or were blocked, and individual ranked products that could not be fetched. Each says which it was, whether re-running is worth it, and none of them were charged.

## `summary` (type: `string`):

Products delivered, categories that listed nothing, products that could not be fetched, duplicates merged, what stopped the run, and the charged-event totals.

## `errors` (type: `string`):

Present only when Amazon blocked, throttled or dropped a request after every route was tried: the category or product and the reason. These are re-runnable, not permanent.

# 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 = {
    "categories": [
        "electronics",
        "kitchen"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyfetch/amazon-bestsellers-scraper").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 = { "categories": [
        "electronics",
        "kitchen",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("steadyfetch/amazon-bestsellers-scraper").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 '{
  "categories": [
    "electronics",
    "kitchen"
  ]
}' |
apify call steadyfetch/amazon-bestsellers-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/IH8XGkBtmwDemdj06/builds/RgppxIgD1tFtvGQLh/openapi.json
