# Amazon Bestsellers Scraper - $0.26 per 1,000, No API Key (`dami_studio/amazon-bestsellers-scraper`) Actor

The Best Sellers and New Releases pages publish a top 60 each, across 22 marketplaces, and that ranking comes back as a table: rank, ASIN, title, price, rating, review count, image, link and category path. No account. $0.26 per 1,000 products.

- **URL**: https://apify.com/dami\_studio/amazon-bestsellers-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.26 / 1,000 product scrapeds

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/platform/actors/running/actors-in-store#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 Scraper

Point it at any Amazon Best Sellers, New Releases or Movers & Shakers list and get the ranking back as a table: position, ASIN, title, price, star rating, review count, image, product link and the category path. Paste the URLs, or just type department names and pick a marketplace.

No account, no cookies, no login, no browser.

- Works from the URL you copied out of the address bar, in either link shape Amazon uses.
- Or skip URLs entirely. Type "Home & Kitchen" and it resolves to the right list on its own.
- 22 marketplaces, from amazon.com to amazon.co.jp. You can mix countries in one run.
- Prices come back in the marketplace's own currency, not converted for wherever the request left from.
- Empty input returns one labelled sample row, free.

### Price

**$0.26 per 1,000 products.** Plus **$0.0007 per run** as the platform start fee, billed per gigabyte of run memory, so exactly that on the default 1 GB.

No volume tiers, no minimum spend, no subscription.

| Products | Total |
|---|---|
| 100 | $0.0267 |
| 1,000 | $0.2607 |
| 10,000 | $2.6007 |
| 100,000 | $26.0007 |

#### What gets charged

- One `product-scraped` event per product row written to the dataset. Nothing else is metered per row.
- Free: the sample row an empty run returns, and every diagnostic row. They all carry `"charged": false`.
- A category Amazon returns empty (which Movers & Shakers usually is) costs nothing beyond the start fee.
- A category name that doesn't exist on the marketplace you chose gets a free diagnostic row saying so.
- The same product appearing twice inside one category list is dropped before it's charged.
- A run that finds nothing costs the start fee and nothing else.
- Rows never leave the dataset without a charge and are never charged without a row. The billed event is a named one, so nothing is quietly attached to `apify-default-dataset-item`.

### Input

```json
{
  "categoryUrls": [
    "https://www.amazon.com/gp/bestsellers/electronics/",
    "https://www.amazon.co.uk/Best-Sellers-Books/zgbs/books/"
  ],
  "categories": [
    "Home & Kitchen",
    "Toys & Games"
  ],
  "listType": "bestsellers",
  "domain": "com",
  "maxItemsPerCategory": 60
}
```

| Field | What it does |
|---|---|
| `categoryUrls` | Amazon list URLs copied straight from the site. Both shapes work: `/gp/bestsellers/electronics/` and `/Best-Sellers-Electronics-Headphones/zgbs/electronics/172541/`. So do `/gp/new-releases/...` and `/gp/movers-and-shakers/...`. Each URL carries its own marketplace and list type, so you can mix countries and list types freely. Up to 50 per run. |
| `categories` | Department names written the way Amazon writes them in that marketplace's own language ("Home & Kitchen", "Toys & Games", "Bücher"), or the slug out of a URL ("electronics", "toys-and-games", "books/2"). These use the marketplace and list type set below. Up to 50 per run. |
| `listType` | `bestsellers`, `new-releases` or `movers-and-shakers`. Applies only to the names in `categories`. A URL already says which list it is. |
| `domain` | Which marketplace the names in `categories` belong to. Ignored for `categoryUrls`. |
| `maxItemsPerCategory` | How many ranked products per category, counting from rank 1. 60 is both the default and the ceiling, because that's everything Amazon renders. Lower it while testing, since you pay per product row. |
| `proxyUrls` | Leave empty. Only for callers who want traffic to leave through proxy servers they already pay for, as `http://user:pass@host:port`. |

Run it with empty input and you get one labelled sample row, free.

### Output

One row per product. This is a real row from a real run:

```json
{
  "ok": true,
  "charged": true,
  "recordType": "product",
  "categoryName": "Electronics",
  "categoryPath": "Electronics",
  "listType": "bestsellers",
  "marketplace": "amazon.com",
  "inputUrl": "https://www.amazon.com/gp/bestsellers/electronics/",
  "rank": 2,
  "asin": "B0DCH8VDXF",
  "title": "Apple EarPods Headphones with USB-C Plug, Wired Ear Buds with Built-in Remote to Control Music, Phone Calls, and Volume",
  "brand": null,
  "price": "$16.91",
  "priceValue": 16.91,
  "currency": "USD",
  "rating": 4.5,
  "reviewCount": 16728,
  "image": "https://images-na.ssl-images-amazon.com/images/I/513OSdW4elL._AC_UL900_SR900,600_.jpg",
  "productUrl": "https://www.amazon.com/dp/B0DCH8VDXF",
  "previousRank": null,
  "rankChangePercent": null,
  "page": 1,
  "scrapedAt": "2026-08-16T13:29:52.126Z"
}
```

#### Field notes

- `rank` is the position Amazon prints on the badge, not a number counted here. Page 1 carries ranks 1-30 and page 2 carries 51-80, which is exactly what Amazon renders.
- `asin` is Amazon's product id. Stable, and the right key for diffing one run against the next.
- `brand` is the byline Amazon prints on the ranking card: author for a book, artist for music, studio for a film. Cards for hard goods carry no byline at all, so this is null for most of Electronics, Home & Kitchen and so on.
- `price` is exactly the text on the card, symbol and all. For books showing several formats it's the "offers from" price, which is the lowest of them.
- `priceValue` is the same figure as a number, with the marketplace's separators resolved, so `1 234,56 zł` becomes `1234.56`.
- `currency` is read from the price text when it names one, otherwise from the marketplace. It always describes the `price` string in the same row.
- `rating` is stars out of five at read time, or null when nothing has been rated yet.
- `reviewCount` is the ratings count next to the stars, as a number. Null when there are none.
- `image` is the largest size Amazon offers for that card, pulled from the card's own image set rather than the small default.
- `categoryPath` is the ancestor chain down to the list you asked for, like `Electronics > Headphones`. For a top-level department it's just the category name.
- `previousRank` is the rank 24 hours earlier and `rankChangePercent` the size of the jump. Both are Movers & Shakers fields and are null on Best Sellers and New Releases rows, which publish no rank movement.
- `page` is 1 for ranks 1-30 and 2 for ranks 51-80. Useful when you want only the top of a list.

Real rows carry `"charged": true`. Sample rows carry `"_sample": true`, diagnostic rows carry `"_diagnostic": true` and an `errorCode`. Neither is billed.

### How it works

The ranking is read straight out of the logged-out list page, which Amazon renders on its own servers. One request per 30 products, no page rendering, no headless browser, no login.

Requests leave through a rotating pool of addresses, and a refused address is answered by moving to another one rather than by waiting. The marketplace's own currency and language preference goes out with every request, so a list read from another country still comes back priced the way a local shopper sees it.

Department names are matched against the marketplace's own Best Sellers menu rather than a hard-coded list. That's why "Home & Kitchen" finds the right list even though its address says something else entirely, and why the names work in German, Japanese or Spanish.

Each category is paged until `maxItemsPerCategory` is reached, and a product id already seen in that category is skipped.

### What people use it for

- Product research. Pull the top 60 of a category every morning and watch which ASINs enter and leave.
- Tracking your own product, or somebody else's, by ASIN across a category and its subcategories over time.
- Sourcing decisions. The ranking, the price band and the review counts in one table.
- Spotting new entrants early with the New Releases list, which is the same run with one field changed.
- Price-band research. `priceValue` and `currency` are already typed, so the median price of a category is one spreadsheet formula.
- Comparing the same category across marketplaces. The same URL pattern works on all 22, and each row says which marketplace it came from.

### Reading the output

- **Real rows** carry `"charged": true` and `"recordType": "product"`. One billed event each.
- **The sample row** carries `"_sample": true` and `"charged": false`. There's exactly one, only when the input named no category at all.
- **Diagnostic rows** carry `"_diagnostic": true`, `"charged": false` and an `errorCode`: `NO_RESULTS` when Amazon returned the list page with no products on it, `BAD_INPUT` when a URL isn't an Amazon list link or a name matches no department, `NOT_FOUND` when the category doesn't exist, `BLOCKED` when Amazon refused every address tried, `TIME_BUDGET` when time ran out. Each carries a plain-English `error` and the `inputUrl` it belongs to.

Filter on `charged == true` and you have exactly the rows you paid for.

### Limits

- Amazon renders 30 products per list page and links exactly two pages, so a list tops out at 60 products and the ranks you get are 1-30 and 51-80. Ranks 31-50 and 81-100 aren't rendered on Amazon's own pages, so nothing can read them from this surface.
- Movers & Shakers is supported, but Amazon has been answering most Movers & Shakers categories with an empty list. When that happens you get a free `NO_RESULTS` row and no charge. The run is fine. The list genuinely has nothing on it.
- `brand` is null for most hard goods. Amazon doesn't print a byline on those ranking cards. It isn't a parsing gap, and filling it would mean opening every product page.
- Only what the ranking card shows comes back. Bullet points, descriptions, variations, seller, stock and shipping live on the product page and are out of scope, because reading them would mean one request per product instead of one per thirty.
- Prices are a snapshot at read time and move constantly. A book with several formats shows the lowest "offers from" price, which isn't always the format at that rank.
- Best Sellers is recalculated hourly and New Releases changes daily, so two runs an hour apart legitimately disagree.
- A category name is matched against that marketplace's Best Sellers menu, which lists departments. A deeper subcategory ("Headphones", "Espresso Machines") isn't in that menu. Use its URL, or the `slug/node` form, for those.
- Kindle, Audible, Apps and Digital Music lists are readable, but their cards carry less than a physical product's. Some have no price, and formats vary.
- Hard ceilings: 50 categories per run and 60 products per category.
- Amazon sometimes answers with a short interstitial page instead of the list. The run moves to another address. If every attempt is refused you get an uncharged `BLOCKED` row rather than a partial list presented as complete.

### Questions

**Do I need a category URL, or can I just say what I want?**

Either. `categoryUrls` takes links copied from Amazon. `categories` takes department names as Amazon writes them, in that marketplace's own language, and resolves them against Amazon's own menu. Both work in the same run.

**Why do the ranks jump from 30 to 51?**

Because Amazon's own Best Sellers pages do. Page one renders 30 of its 50-product block and page two starts again at 51. Open the list in a browser and you'll see the same gap. Everything here is a product Amazon actually rendered, with the rank Amazon printed on it.

**Can I get more than 60 products from one category?**

Not from this surface. 60 is everything Amazon renders. To go deeper, scrape the subcategories. Each has its own list of 60, and `categoryPath` tells you which is which.

**What if a category returns nothing?**

One uncharged diagnostic row for that category explaining why, and the run carries on to the others.

**Do I need a proxy?**

No. The run brings its own egress and its cost is already inside the price. `proxyUrls` exists only for callers who want traffic to leave through servers they already own.

**Will the run fail if Amazon blocks it?**

No. It moves to a different address and tries again. If everything is refused you get an uncharged diagnostic row and the run still finishes as succeeded. A failed run would still bill the start fee, which would mean paying to be told something went wrong.

**Can I run it on a schedule?**

Yes. Nothing is held between runs. Diff on `asin` to see what entered or left the list, and on `rank` to see what moved.

# Actor input Schema

## `categoryUrls` (type: `array`):

Amazon list URLs copied straight from the site. Both link shapes work - https://www.amazon.com/gp/bestsellers/electronics/ and https://www.amazon.com/Best-Sellers-Electronics-Headphones/zgbs/electronics/172541/ - and so do New Releases (/gp/new-releases/...) and Movers & Shakers (/gp/movers-and-shakers/...) links. The marketplace and the list type are read from the URL itself, so you can mix countries in one run. Up to 50 URLs.

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

Department names as Amazon writes them in that marketplace's own language ("Home & Kitchen", "Toys & Games", "Bücher"), or the slug from the URL ("electronics", "toys-and-games", "books/2"). Names are resolved against the marketplace's own Best Sellers menu, which is why "Home & Kitchen" correctly becomes home-garden and "Tools & Home Improvement" becomes hi. Uses the marketplace and list type chosen below. Up to 50.

## `listType` (type: `string`):

Which ranking to read for the entries in "Category names or slugs". Category URLs ignore this - they already say which list they are. Note that Amazon returns empty Movers & Shakers lists for most categories; when that happens you get a free diagnostic row, never a charge.

## `domain` (type: `string`):

Which Amazon marketplace the entries in "Category names or slugs" belong to. The marketplace's own currency and language preference is sent with every request, so prices come back in the local currency instead of being converted for whatever country the request happens to leave from.

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

How many ranked products to return for each category, counting from rank 1. Amazon renders 30 products per list page and offers two pages, so 60 is the ceiling and also the default. Lower it while you are testing - you pay per product row.

## `proxyUrls` (type: `array`):

Leave this empty. By default the run rotates a large pool of addresses that cost you nothing per gigabyte. Fill it in only if you specifically want the traffic to leave through proxy servers you already pay for, in the form http://user:pass@host:port.

## Actor input object example

```json
{
  "categoryUrls": [
    "https://www.amazon.com/gp/bestsellers/electronics/"
  ],
  "categories": [],
  "listType": "bestsellers",
  "domain": "com",
  "maxItemsPerCategory": 60
}
```

# Actor output Schema

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

Every row in the default dataset: rank, asin, title, brand, price, priceValue, currency, rating, reviewCount, image, productUrl, categoryName, categoryPath, listType, marketplace, page, previousRank, rankChangePercent, inputUrl. An empty, blocked or unmatched run returns a single uncharged row explaining what happened instead.

# 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 = {
    "categoryUrls": [
        "https://www.amazon.com/gp/bestsellers/electronics/"
    ],
    "categories": [],
    "maxItemsPerCategory": 60
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/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 = {
    "categoryUrls": ["https://www.amazon.com/gp/bestsellers/electronics/"],
    "categories": [],
    "maxItemsPerCategory": 60,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/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 '{
  "categoryUrls": [
    "https://www.amazon.com/gp/bestsellers/electronics/"
  ],
  "categories": [],
  "maxItemsPerCategory": 60
}' |
apify call dami_studio/amazon-bestsellers-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/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/uX9XFOJZBPhNlPTF6/builds/nAHpexRyE6DBFpmWZ/openapi.json
