To get an Amazon product's sales rank history as JSON, call the SellerMagnet /api/amazon-product-statistics endpoint with the ASIN and marketplace. The response carries stats.salesRankHistory, the Best Sellers Rank in the root category over time with full timestamps, stats.salesRank, one daily series per category the product is ranked in, the current bestSellerRank, and the category path. Prices, ratings and monthly sales come in the same response for the same credit.
Key Takeaways
- stats.salesRankHistory is the root-category BSR as [timestamp, rank] pairs; stats.salesRank has one [date, rank] series per category.
- A rank of -1 means the product was unranked at that moment - drop it, do not average it.
- bestSellerRank is the current root-category rank; '-' when there is none.
- Lower is better: build trends on the median of the last 30 ranked days, not on single readings.
- graphs=true adds a ready-made PNG of the rank series for the same credit, when the series has at least one rank.
What does the sales rank history look like?
Two series and a summary. salesRankHistory is the product's main Best Sellers Rank - the one Amazon lists first, which is the root category for almost every product - sampled whenever the rank was recorded, with the time of day. salesRank is a dictionary keyed by category name (in the marketplace language) with a [date, rank] series per category, cut to the day; several readings can share a date, so keep the last one. rootCategory and categoryTree give the path from root to leaf. The history is already recorded back to trackingSince: one call returns it, no polling job of your own is needed to build it.
Rank fields of /api/amazon-product-statistics for a PlayStation 5 on amazon.it (trimmed)
{
"success": true,
"data": {
"asin": "B0CLTBHXWQ",
"bestSellerRank": 15,
"rootCategory": {"id": 412603031, "name": "Videogiochi"},
"categoryTree": [
{"catId": 412603031, "name": "Videogiochi"},
{"catId": 20904349031, "name": "PlayStation 5"},
{"catId": 20904364031, "name": "Console"}
],
"trackingSince": "2023-12-18",
"stats": {
"salesRankHistory": [["2025-06-12 09:14:00", 21], ["2025-06-13 02:10:00", -1], ["2025-06-14 01:58:00", 15]],
"salesRank": {
"Videogiochi": [["2025-06-12", 21], ["2025-06-13", -1], ["2025-06-14", 15]],
"Console": [["2025-06-12", 2], ["2025-06-14", 1]]
},
"monthlySoldHistory": [["2025-05", 1000], ["2025-06", 1000]]
}
}
}
| Field | Shape | Notes |
|---|---|---|
stats.salesRankHistory | [["YYYY-MM-DD HH:MM:SS", rank], ...] | Root category, with time of day; -1 = unranked at that moment |
stats.salesRank | {category name: [["YYYY-MM-DD", rank], ...]} | One series per category, cut to the day; may contain -1; several entries can share a date - keep the last |
bestSellerRank | integer or "-" | Current root rank; if the latest reading is -1, falls back to the previous reading when that one is at most two days earlier, otherwise "-" |
rootCategory / categoryTree | object / list | Root category and the path to the leaf category |
stats.monthlySoldHistory | [["YYYY-MM", units], ...] | Amazon's 'bought in past month', last value per month |
Why does the series contain -1 and gaps?
A -1 is a recorded moment at which the product had no rank - out of stock, suppressed, or freshly listed. Gaps are simply times with no recording. Neither is a rank: averaging -1 into a trend drags it towards zero, which reads as "best seller". Drop the -1 readings, keep the last value per day, and compare periods on the median, which shrugs off a single spike from a lightning deal.
rank_trend.py - daily table and a 30-day trend from the root series
import os
import statistics
import requests
def rank_history(asin: str, marketplace_id: str) -> dict:
resp = requests.get("https://sellermagnet-api.com/api/amazon-product-statistics",
params={"asin": asin, "marketplaceId": marketplace_id,
"api_key": os.environ["SELLERMAGNET_API_KEY"]}, timeout=90)
try:
body = resp.json()
except ValueError: # e.g. an HTML error page from a proxy
body = {}
if resp.status_code != 200 or not body.get("success"):
raise RuntimeError(f"{asin}: {resp.status_code} {body.get('message')}")
return body["data"]
def daily_ranks(series: list) -> dict:
"""[[timestamp, rank], ...] -> {date: rank}; -1 (unranked) dropped, last reading per day kept."""
out = {}
for stamp, rank in series:
if isinstance(rank, (int, float)) and rank > 0:
out[stamp[:10]] = int(rank)
return out
def trend(daily: dict, days: int = 30) -> dict:
dates = sorted(daily)
recent, earlier = dates[-days:], dates[-2 * days:-days]
now = statistics.median(daily[d] for d in recent) if recent else None
before = statistics.median(daily[d] for d in earlier) if earlier else None
return {"median_now": now, "median_before": before, # windows are ranked DAYS, not calendar days
"change_pct": round((now - before) / before * 100, 1) if now and before else None}
data = rank_history("B0CLTBHXWQ", "APJ6JRA9NG5V4")
daily = daily_ranks(data["stats"]["salesRankHistory"])
print("current:", data.get("bestSellerRank"), "in", data["rootCategory"]["name"])
print("days with a rank:", len(daily), "since", data.get("trackingSince"))
print("30-day trend:", trend(daily)) # negative change_pct = rank improved

Which category's rank should I track?
The root category rank (salesRankHistory, bestSellerRank) is comparable across products of the same department and is what most tools call BSR. The leaf category ranks in salesRank ("Console": 1) are what shoppers see as #1 in Console and are easy to win in a small niche. Track the root for demand, the leaf for badges. Note that salesRank is keyed by name in the marketplace language - Videogiochi on amazon.it, Video Games on amazon.com - so key your storage by the catId from categoryTree if you compare marketplaces.

How do I turn rank into sales?
Rank is relative: #15 in Videogiochi means fifteen products sold more recently, not a number of units. For units, read stats.monthlySoldHistory from the same response - Amazon's bought in past month figure - or call the sales estimate endpoint, which uses that figure where it exists and a rank model where it does not. For the live category leaderboard around your product, the bestsellers endpoint lists the top 50 of any category ID.
Current rank, category and last month's units with curl and jq
curl -sG "https://sellermagnet-api.com/api/amazon-product-statistics" \
--data-urlencode "asin=B0CLTBHXWQ" \
--data-urlencode "marketplaceId=APJ6JRA9NG5V4" \
--data-urlencode "api_key=$SELLERMAGNET_API_KEY" \
| jq -r '.data | [.bestSellerRank, .rootCategory.name, (.stats.monthlySoldHistory | last | .[1])] | @tsv'
Marketplaces and billing
Recorded history exists for amazon.com, .ca, .com.mx, .com.br, .co.uk, .de, .fr, .it, .es, .in and .co.jp; other marketplace IDs get a free 400. A malformed ASIN is a billed 400, an unknown one a billed 404. Timestamps are UTC.
Frequently Asked Questions
How do I get Amazon sales rank history with an API?
Call /api/amazon-product-statistics with the ASIN and marketplace and read stats.salesRankHistory (root category, timestamped) and stats.salesRank (one daily series per category).
What does a sales rank of -1 mean?
The product had no rank at that moment - out of stock, suppressed or new. Drop those readings before averaging; bestSellerRank shows '-' instead.
How far back does the rank history go?
To trackingSince, the date recording started for that ASIN. There is no date-range parameter; filter on your side.
Is BSR the same across categories?
No. The root-category rank is what most tools call BSR; leaf-category ranks are the badges shoppers see, and each series is separate in stats.salesRank.
Can I get the rank history as a chart?
Yes. Add graphs=true and data.graphs.salesRankHistory holds a PNG URL for the same single credit; the key is absent when the series holds no rank at all.
Why is the sales rank history empty for my ASIN?
Child ASINs of a variation family often carry no rank of their own; the family's rank sits on the parent listing. An empty series with bestSellerRank '-' usually means exactly that.
Bottom line: one call returns the root and per-category rank series, drop the -1 readings, trend on medians, and read units from monthlySoldHistory beside it. The sales rank history page shows the endpoint, and a free account includes 150 credits.