# Amazon BSR Tracker: Watch Bestseller Rank Move Over Time (`amazonscrapers/amazon-bsr-tracker`) Actor

Track Amazon bestseller rank for a list of products. Every rank Amazon assigns, not just the first, with category, badges, units bought last month and a timestamp per row so scheduled runs become a rank history. 19 marketplaces.

- **URL**: https://apify.com/amazonscrapers/amazon-bsr-tracker.md
- **Developed by:** [Amazon Scrapers](https://apify.com/amazonscrapers) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 products with a bestseller ranks

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?

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 BSR tracker: bestseller rank history by ASIN, every category level

Give it the products you care about. Get back every bestseller rank Amazon
assigns them, with the category each rank belongs to, stamped with the moment it
was read, so scheduled runs become a record of how those ranks moved.

Bestseller rank is the closest thing Amazon gives you to a public sales figure.
It is also the number that moves first: a product losing ground shows it in the
rank days before it shows in reviews, and weeks before it shows in price.

Seventeen fields per product, nineteen marketplaces, and no dependence on the buy
box, which means this Actor works on marketplaces where the price ones do not.

### Every rank, not just the first

Amazon gives a product several ranks, one per category level. A speaker might be
number 812 in Electronics and number 6 in Portable Bluetooth Speakers on the same
day.

Most tools return the first one. That is the broad rank, and it is the one that
barely moves: a product can slide from 6 to 40 in its narrow category while its
Electronics rank shifts by a few hundred and looks like noise.

This Actor returns all of them, each with its category name, in
`bestsellerRanks`. The narrow rank is where a change shows up first, and you
cannot watch it if you never received it.

Getting that right took a specific repair worth mentioning, because it shows how
easily this field goes quiet. An earlier version required a hash before the
number. amazon.com prints one and amazon.co.uk does not, so the field came back
on six of six American products and zero of five British ones. Making the hash
optional took it to five of five. A rank tracker that silently returns nothing on
your marketplace is worse than one that fails loudly.

### Every field it returns

Seventeen per product. Every one of them is there to make a rank readable: the
rank itself, what category it is in, and enough context to tell a real move from
a product going out of stock.

#### Price and offer

| Field | What it holds |
| --- | --- |
| `price` | Current price from Amazon's own buy box data |
| `currency` | Three-letter code, from the page itself |

#### Demand and rank

| Field | What it holds |
| --- | --- |
| `boughtInPastMonth` | Units bought last month as a number |
| `bestsellerRanks` | Every rank with its category, not just the first **(this is what you are charged for)** |
| `isBestSeller` | The orange best seller badge |
| `isAmazonChoice` | The Amazon's Choice badge |
| `rating` | Average stars, read in every shop language |
| `reviewCount` | How many ratings, as a number |

#### Stock and delivery

| Field | What it holds |
| --- | --- |
| `inStock` | True, false, or null when Amazon does not say |

#### Identity and catalogue

| Field | What it holds |
| --- | --- |
| `asin` | The product id |
| `title` | Full product title, shortened only past 1000 characters |
| `brand` | Brand name |
| `marketplace` | Which shop this row came from |
| `url` | The page this was read from |
| `scrapedAt` | Timestamp of the read |

#### Description, media and variants

| Field | What it holds |
| --- | --- |
| `category` | The most specific category |
| `categories` | The full breadcrumb path |

### A real row

```
asin               B0DXXYS4BJ
title              Roku Streaming Stick HD with Voice Remote
brand              Roku
bestsellerRanks    #6 in Electronics, #1 in Streaming Media Players
category           Streaming Media Players
categories         Electronics > Television & Video > Streaming Media Players
isBestSeller       false
isAmazonChoice     false
boughtInPastMonth  10000
rating             4.6
reviewCount        23344
price              29.99
inStock            true
scrapedAt          2026-08-26T09:14:22.031Z
```

Two ranks on that product. Ten thousand units last month and no best seller
badge, which tells you the badge is not simply a function of volume.

### Why the stock column is in a rank tracker

`inStock` is here for one reason: a rank collapse on a product that went out of
stock is not a demand signal, and without this column you would read it as one.

The same goes for `price`. A rank that drops the week a price doubles is a
different story from a rank that drops while nothing else moved. Neither field is
what you came for, and both are what stop you drawing the wrong conclusion.

### What it charges for

This Actor charges on `bestsellerRanks`, and on nothing else.

Amazon does not rank every product. New listings, low-volume products and some
categories carry no rank at all. For those the row still arrives with the rating,
the price and the category, and you are not charged for it.

That makes it safe to run across a mixed list: you pay for the products Amazon
ranks and not for the ones it ignores. Measured on 26 August 2026 across a sixty
product list taken from a mixed American catalogue, fifty of fifty-nine delivered
products carried at least one rank, so roughly one in six rows arrived free.

On a list you built from a bestseller page, the share with a rank is close to all
of them, because that is what a bestseller page is.

### Turning it into a history

Run once and you have today's ranks. Set it to run daily from the Schedules tab
and point every run at the same named dataset, and the rows accumulate. Sorting
by `asin` and `scrapedAt` gives the rank line per product per category.

Nothing here computes a delta for you. That is deliberate: which comparison
matters depends on your question, and a dataset with honest timestamps is more
useful than a "rank changed" flag whose rules you cannot inspect.

Daily is the right cadence. Amazon recalculates hourly, but the number printed on
a page lags, and hourly runs mostly buy you the same value three times.

### Reading rank honestly

Rank is a ranking, not a quantity. Moving from 40 to 20 in a category with a
thousand products is a very different amount of sales from moving 40 to 20 in a
category with fifty.

That is why this Actor returns `boughtInPastMonth` alongside. Amazon's units
figure is rounded and only appears on listings with enough recent sales, but
where it exists it anchors the rank to something real. A rank without a volume
next to it is a number you can only compare against itself.

Reading that field correctly also took a repair. Amazon splits the figure across
two separate elements, so an earlier version found it on one product in six. It
now finds it on six in six where Amazon prints it at all.

### What to put in

**ASINs or product URLs**, as many as you like. Paste them in any shape: a bare
`B08N5WRWNW`, a full URL with tracking parameters, a spreadsheet cell that still
says `ASIN: B08N5WRWNW`. Duplicates are removed across everything you give it.

**A category or bestseller link**, if you want to establish which products to
watch before you start watching them. Most people run a category once, take the
ASINs, and track those.

**A search term**, when the set you want to track is defined by what shoppers
search for rather than by a category.

### Settings worth knowing about

| Setting | What it does |
| --- | --- |
| `marketplace` | One of nineteen. Ranks are read in the shop's own format, which differs more than you would expect. |
| `proxyCountry` | Leave empty and it follows the marketplace. |
| `maxItems` | Hard ceiling on delivered products. Zero means no ceiling. |
| `maxItemsPerList` | How many products to take from each category or bestseller link. |
| `maxListPages` | How deep to walk each list. Amazon caps bestseller lists at a hundred products anyway. |
| `lanes` | How many products are read at once. Eight by default. Rank data does not come from the buy box, so pushing to sixteen costs less here than it does on the price Actors. |
| `maxAttempts` | Attempts per product, each from a different address. Twelve by default. |
| `residentialFallback` | Off by default. Measured 2026-08-26: turning it on cost roughly six times as much for two more products out of sixty. |
| `economical` | Leave it off. The rank block sits late in the page, so this is one Actor where hanging up early genuinely costs you the field you came for. |

### How it works, in plain terms

Amazon refuses roughly six requests in ten to an address it has not seen before.
For rank tracking a refused product is a gap in a time series, which is worse than
a gap in a one-off report, because you cannot go back and fill it.

Every product gets up to twelve attempts, each from a different exit address, and
the same address is never used twice in a row. Addresses that work are remembered
for the rest of the run and tried first. The pace adapts: when refusals climb the
runner slows down rather than pushing harder.

Anything still refused at the end goes into a second pass at half speed, using
only the addresses that already proved themselves.

Because this Actor charges on the rank rather than on a price, the retry rule
follows the rank: a page that came back without one gets another attempt, and a
page that came back with one stops being asked for. That sounds obvious and it
was not: an earlier version judged every Actor on whether a price arrived, so
this one burned twelve attempts on pages that had handed over the rank on the
first try.

### Reading the run log

```
Done: 10 charged (+7 delivered without a price, free), 3 refused by Amazon, 0 other. 1645 KB per charged result, 214 attempts in total.
Exit addresses: 184 used, 1 proven good, 9 burned.
Attempts by outcome: {"refused by Amazon":139,"page did not carry the field this Actor needs":59,"http_500":1,"ok":10,"network error":2,"proxy error":2,"delivered, product unavailable":1}
```

`charged` is products that came back with at least one rank. The bracketed number
arrived complete and unranked, and cost you nothing.

If a run ever delivers rows and charges for none of them, the log says so in
capitals at the bottom.

### Reliability, measured

Three consecutive runs of twelve products on 26 August 2026, at the default
settings:

```
run 1   12 of 12 delivered, 5 with a rank
run 2   10 of 12 delivered, 5 with a rank
run 3   10 of 12 delivered, 5 with a rank
```

And one run of sixty products the same day: 59 delivered,
50 with a rank, in 7.8 minutes.

Worth reading out of that: the longer run has the better delivery rate,
98 percent against 89, and that is not luck. The engine spends the first
products of a run working out which exit addresses Amazon is tolerating today, so
a short run pays the whole warm-up over fewer products. If delivery matters more
to you than turnaround, batch your work into larger runs.

The second is the residential switch, and the honest version is less dramatic
than it sounds. Measured 26 August on the same sixty products, twice: with the
switch off, fifty-one came back with the field this Actor charges on, for $0.0205
of platform usage. With it on, fifty-three came back, for $0.1192. Two more
products for roughly six times the cost.

That is why it is off by default. It is in your input form, so if a marketplace
is refusing almost everything you can turn it on and pay for the difference.

### What you can build with it

**A competitor watch.** Daily ranks on the products you compete with, in their
narrow categories, where a slide shows up first.

**A launch curve.** Track a new product from listing day and watch the narrow
rank climb. `boughtInPastMonth` appears once volume is high enough, which is
itself a milestone worth recording.

**A category health check.** Run a bestseller list once a week and watch how much
the top ten churns. A category where the same ten sit still is a different
opportunity from one that reshuffles weekly.

**A seasonality record.** Ranks over a year show when a category wakes up. Since
Amazon publishes no history, the only way to have it next year is to start
collecting it now.

**A badge tracker.** `isBestSeller` and `isAmazonChoice` move independently of
rank and are Amazon's own judgement about a listing.

### Working it into your stack

Every run writes to a dataset you can download as CSV, JSON, Excel or XML, or
pull through the API. `bestsellerRanks` is a list, so if your destination wants a
flat table, either take the first entry or expand one row per rank.

Schedules take a cron expression. Webhooks fire on completion, which is where a
rank-drop alert belongs: compare the last two rows for an ASIN in your own code
and keep the threshold where you can change it.

### Marketplaces

Nineteen, from amazon.com to amazon.co.jp, read in their own language.

Rank data does not depend on the buy box, and that is this Actor's quiet
advantage. On marketplaces where Amazon leaves the buy box out and the price
Actors come back empty, the ranks still arrive. Measured on amazon.de, where no
product returned a price: ranks and specifications came back normally. The
`price` column will be empty there, the ranks will not.

### Pricing

Four dollars per thousand products with a rank, plus three cents to start a run.

Watching fifty products daily costs twenty-three cents a day: fifty times four
tenths of a cent, plus the three cent start fee. You are not
charged for unranked products or for products that never arrived, and platform
usage is included, so there is no separate compute or proxy bill.

### The rest of this family

Eleven Actors on one engine. Next to a rank tracker the useful ones are:

- **Amazon Bestsellers Scraper** to build the watchlist in the first place, with
  all sixty-four fields per product.
- **Amazon Niche Research** when the question is whether a category is worth
  entering rather than how one product is doing in it.
- **Amazon Price Tracker** for the price line under the rank line.
- **Amazon Stock Checker** so you can tell a rank collapse from a stockout.

### Questions people ask

**How often does rank update.** Amazon recalculates hourly, but the number you
see on a page can lag. Daily is the sensible cadence.

**Why does a product have no rank.** Amazon does not rank everything. New
listings and low-volume products often carry none, and some categories do not use
rank at all. Those rows are free.

**Which rank should I watch.** The narrowest one. It moves first and it reflects
the competition you are actually in.

**Can I get an alert on a rank drop.** Not from this Actor directly. Put a
webhook on the run and compare the last two rows for an ASIN.

**Does it work where prices do not.** Yes. That is the point of charging on the
rank rather than the price.

**How many products can it track.** No built-in ceiling. Sixty products took
7.8 minutes on the measured run, so fifty is around 7.

### What it does not collect

No review text, no reviewer names, no customer questions, no seller names. You
get the aggregate rating and the count, which are facts about a product rather
than about a person.

### Legal note

This Actor reads public pages and takes product facts: ranks, categories, badges,
aggregate ratings. Business information about an item, not information about a
person.

You are responsible for what you do with the output. Check Amazon's terms for
your own use case.

# Actor input Schema

## `productUrls` (type: `array`):

Amazon product URLs or bare ASINs, one per line. Leave empty if you are using Search instead. Duplicates are removed so you are never charged twice.

## `search` (type: `array`):

Anything that lists products: a plain search term ("tower fan"), an Amazon search URL, a category link, or a bestsellers page. Each one is crawled and every product found is scraped. Use this when you do not know the ASINs up front.

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

Which Amazon site to read. The currency of the price follows the marketplace and the proxy country.

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

Which country the requests should appear to come from. Leave empty and it follows the marketplace: amazon.de is read from Germany, amazon.co.uk from the UK. This matters more than it looks -- Amazon shows different prices, currencies and even availability depending on where you appear to be.

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

Hard ceiling on delivered products. 0 means no ceiling: the Actor keeps going until the lists run out or your limits stop it.

## `maxItemsPerList` (type: `integer`):

Products to take from each search term or category link. Up to 10000.

## `proxyConfiguration` (type: `object`):

Datacenter proxies were enough in testing: 30 out of 30 products returned with zero blocks. They are the cheaper option and the default. Switch to residential if you start seeing blocked pages at higher volume. Running without any proxy stops after roughly 85-120 products, because Amazon closes the door on a single IP address for over an hour.

## `lanes` (type: `integer`):

How many products to read at the same time. Each lane uses its own exit address, so more lanes never means asking one address more often. Eight is the tested default and the most accurate. Sixteen finishes the same sixty products in about half the time, but measured on 2026-08-24 it returned six more products without a price, because Amazon strips the buy box sooner under pressure. Raise it when speed matters more than the last few prices.

## `maxAttempts` (type: `integer`):

How often to retry a product Amazon refuses, each time from a different exit IP. A refusal is a property of one address, not of the product, and a refused attempt costs about 4 KB. Twelve tries is cheap insurance against a run that comes back short.

## `residentialFallback` (type: `boolean`):

After half the attempts have failed, retry through residential addresses instead of datacenter ones. Measured 2026-08-26: residential transfer was 90 percent of the bill on a sixty product run ($0.150 of $0.167) while a run that never touched it delivered all sixty anyway. Leave it off unless you are losing products; turn it on when a marketplace refuses everything.

## `economical` (type: `boolean`):

Hang up on each product page once the price is in, instead of reading it to the end. Roughly three times less traffic and about three times faster. Measured cost: over 30 products this lost 2 ratings, 4 review counts and 1 brand, because Amazon sometimes puts the rating below the price. Prices were identical either way. Leave this off unless you only need prices and are paying per gigabyte.

## `maxListPages` (type: `integer`):

Result pages to walk per list. Up to 100. Amazon itself stops earlier on most searches.

## `minDelayMs` (type: `integer`):

Pause between products. Measured 2026-08-23: at 1-2 seconds this returned 10 of 15 products; at 8-15 seconds it returned 12 of 15 on the same list. Pace turned out to matter more than anything else. The market leader waits about 14 seconds per product.

## `maxDelayMs` (type: `integer`):

Upper bound for the pause between products. The actual pause is random between the minimum and this value, which looks less mechanical than a fixed interval.

## `retryPauseMs` (type: `integer`):

How long to wait before trying a refused product again. Kept short because the retry already switches to a different exit IP, and waiting longer only makes the run more expensive.

## Actor input object example

```json
{
  "productUrls": [
    "B09B8V1LZ3",
    "https://www.amazon.com/dp/B0DXXYS4BJ"
  ],
  "search": [],
  "marketplace": "com",
  "maxItems": 0,
  "maxItemsPerList": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "lanes": 8,
  "maxAttempts": 12,
  "residentialFallback": false,
  "economical": false,
  "maxListPages": 5,
  "minDelayMs": 8000,
  "maxDelayMs": 15000,
  "retryPauseMs": 300
}
```

# Actor output Schema

## `products` (type: `string`):

One row per product per run, stamped with the moment it was read. Products Amazon does not rank are delivered and never charged for.

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

Counts per outcome, exit addresses used, bytes per charged result.

# 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 = {
    "productUrls": [
        "B09B8V1LZ3",
        "https://www.amazon.com/dp/B0DXXYS4BJ"
    ],
    "search": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("amazonscrapers/amazon-bsr-tracker").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 = {
    "productUrls": [
        "B09B8V1LZ3",
        "https://www.amazon.com/dp/B0DXXYS4BJ",
    ],
    "search": [],
}

# Run the Actor and wait for it to finish
run = client.actor("amazonscrapers/amazon-bsr-tracker").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 '{
  "productUrls": [
    "B09B8V1LZ3",
    "https://www.amazon.com/dp/B0DXXYS4BJ"
  ],
  "search": []
}' |
apify call amazonscrapers/amazon-bsr-tracker --silent --output-dataset

```

## MCP server setup

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

```

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/pm3vcF0fjQqRTYPFi/builds/hz4FZtDicKz4eBmJw/openapi.json
