Home / Blog / Amazon Estimated Sales Accuracy via API: What 'Bought Last Month' Really Means

Amazon Estimated Sales Accuracy via API: What 'Bought Last Month' Really Means

Three myths about the SellerMagnet sales estimate, checked against Amazon's own documentation and the API's code: it is not an exact count, most ASINs never get the 'bought in past month' figure at all, and a low estimate usually means thin signal, not zero sales. Two tested scripts show how to tell which path produced a given number.

October 26, 2026
5 min read
SellerMagnet Team
Share & Bookmark
Terminal window showing a Python function that classifies an Amazon sales estimate as anchored or modeled

SellerMagnet's estimated_monthly_sales is either Amazon's own 'bought in past month' figure or, for most ASINs, a model built from sales rank, reviews and listing age - and the two should not be trusted the same way. Call /amazon-product-statistics for the same ASIN and check stats.monthlySoldHistory: a value in its last entry means the estimate is anchored on Amazon's own number; an empty list means it is modeled, which is an order of magnitude, not a count.

Key Takeaways

  • estimated_monthly_sales is anchored on Amazon's own 'bought in past month' figure when Amazon publishes one, modeled otherwise.
  • Amazon's own wording for the figure calls it 'not an estimate' but a lower bound ('at least 1000'), and says most ASINs do not have it set.
  • A low or 0 estimate on the modeled path reflects thin input data (no monthlySold, short rank history, few reviews), not a verified sales count.
  • monthlySoldHistory on /amazon-product-statistics tells you which path produced a given estimate - an empty list means modeled.
  • Rating data (needed for the modeled path) costs at most one extra token, and only when it was refreshed within 14 days.

Myth 1: is the estimate an exact sales count?

No. Where Amazon shows a 'bought in past month' figure, Amazon's own search-page copy for it is explicit that the figure is 'not an estimate' - but it is a lower bound: Amazon rounds it down into buckets ('1K+' means at least 1,000, not exactly 1,000). SellerMagnet's estimateMonthlySells() takes that figure and nudges it slightly above the bucket floor (round(sold * 1.215) + sold ** (1/5)); it never invents a number Amazon did not already publish for that path.

Myth 2: does every product get a 'bought in past month' figure?

No - Amazon's own wording for the figure says most ASINs do not have this value set at all. Amazon only starts showing it once a listing crosses some internal volume threshold, so a majority of products on any marketplace have no figure to anchor on, and SellerMagnet falls back to the rank/review/age model for them. The sample below, from a real category page, is typical: only one of three ranked ASINs carried the figure.

Which path produced an estimate, and what that means for how much to trust it.
PathWhen it appliesWhat the number means
1 ANCHORAmazon's own 'bought in past month' figure is set for this ASINestimate sits inside Amazon's rounding bucket ('1K+' = 1,000-1,999)
2 MODELno such figure - most ASINssales rank, reviews and listing age; order of magnitude, not a count

Myth 3: does a low estimate mean the product barely sells?

Not reliably. A low or 0 number on the modeled path can mean the product genuinely sells little, or it can mean the model had little to work with: a short rank history, few reviews, or a very new or very old listing (the model damps both ends). Before 2026-09-19 the estimate request did not include rating, so every product lacking monthlySold lost the review/rating half of the model and estimated 0 regardless of its real sales; the request now always sends rating=1, but the model is still only as good as its inputs - it has no idea when its own inputs are thin.

sales_confidence.py - which path produced this ASIN's estimate

from retry import get_with_retry
from sellermagnet import new_session

# A failed estimate or statistics call is a refunded 500 (both propagate as ApiError), so retrying
# costs nothing on that path; 404 means the ASIN is not on this marketplace, not a failure.


def classify_estimate(session, asin: str, marketplace_id: str) -> dict:
    """One ASIN's estimate plus WHY it has that value: anchored on Amazon's own 'bought in past
    month' figure, or modeled from rank/reviews/age because Amazon shows no such figure for most
    listings. A low or 0 estimate on the modeled path reflects thin signal (no monthlySold, a
    short rank history, few reviews), not necessarily low sales."""
    estimate = get_with_retry(session, "amazon-product-search-estimated-sells", asin=asin, marketplaceId=marketplace_id)
    stats = get_with_retry(session, "amazon-product-statistics", asin=asin, marketplaceId=marketplace_id)
    sold = (stats.get("stats") or {}).get("monthlySoldHistory") or []   # [["YYYY-MM", units], ...]
    latest = sold[-1] if sold else None
    anchored = latest is not None
    return {
        "asin": estimate["asin"],
        "estimated_monthly_sales": estimate["estimated_monthly_sales"],
        "sales_rank": estimate["sales_rank"],
        "category": estimate["category"],
        "basis": "anchored" if anchored else "modeled",
        "amazon_bought_last_month": f"{latest[1]} (month {latest[0]})" if latest else None,
        "note": (
            "within Amazon's own rounding bucket" if anchored
            else "order of magnitude only - no 'bought in past month' figure shown for this ASIN"
        ),
    }


if __name__ == "__main__":
    session = new_session()
    print(classify_estimate(session, "B0CLTBHXWQ", "APJ6JRA9NG5V4"))      # has monthlySold
    print(classify_estimate(session, "B0NOFIGURE", "APJ6JRA9NG5V4"))      # no monthlySold shown

Example output (illustrative ASINs)

{'asin': 'B0CLTBHXWQ', 'estimated_monthly_sales': 1218, 'sales_rank': 15, 'category': 'Videogiochi', 'basis': 'anchored', 'amazon_bought_last_month': '1200 (month 2026-09)', 'note': "within Amazon's own rounding bucket"}
{'asin': 'B0NOFIGURE', 'estimated_monthly_sales': 42, 'sales_rank': 88210, 'category': 'Casa e cucina', 'basis': 'modeled', 'amazon_bought_last_month': None, 'note': "order of magnitude only - no 'bought in past month' figure shown for this ASIN"}
Terminal-style table comparing the anchored and modeled estimate paths
The response never says which path it took - stats.monthlySoldHistory does.

How do I check a whole category, not just one ASIN?

Pull a page from /amazon-bestsellers for a category_id, then run the same anchored/modeled check per ASIN (get_with_retry() and new_session() come from the Python quickstart; category IDs come from the category ID guide). A row with no ASIN ("N/A") or one that 404s on this marketplace is skipped, not counted - the 404 is a real answer (not tracked here), never a billed retry target. count is capped at 50 and offset + count must not exceed 50 on this endpoint - the API returns a billed 400 past either limit.

category_confidence.py - anchored vs modeled share across a category page

from collections import Counter

from retry import get_with_retry
from sellermagnet import ApiError, new_session

from sales_confidence import classify_estimate

# 50 is the API's own page cap on /amazon-bestsellers (offset + count must also stay <= 50), so
# one category page is the largest sample this costs 1 credit for; the classify_estimate call
# below costs 2 more per ASIN (estimate + statistics).


def category_split(session, category_id: str, marketplace_id: str, sample: int = 10) -> dict:
    page = get_with_retry(session, "amazon-bestsellers", category_id=category_id,
                           marketplaceId=marketplace_id, count=50)
    rows = []
    basis_counts = Counter()
    for b in page["bestsellers"][:sample]:
        asin = b.get("asin")
        if not asin or asin == "N/A":
            continue                                        # a bestsellers row can carry asin "N/A"
        try:
            row = classify_estimate(session, asin, marketplace_id)
        except ApiError as err:
            if err.status == 404:
                continue                                     # not tracked on this marketplace
            raise
        basis_counts[row["basis"]] += 1
        rows.append({"rank": b.get("rank"), "asin": row["asin"], "basis": row["basis"],
                     "estimated_monthly_sales": row["estimated_monthly_sales"]})
    total = sum(basis_counts.values()) or 1
    return {
        "rows": rows,
        "anchored_pct": round(100 * basis_counts["anchored"] / total, 1),
        "modeled_pct": round(100 * basis_counts["modeled"] / total, 1),
    }


if __name__ == "__main__":
    result = category_split(new_session(), "281407", "ATVPDKIKX0DER", sample=6)
    for row in result["rows"]:
        print(row["rank"], row["asin"], row["basis"], row["estimated_monthly_sales"])
    print(f"anchored {result['anchored_pct']}% / modeled {result['modeled_pct']}%")

Example output (illustrative category sample)

1 B0ANCHOR1 anchored 2004
3 B0MODEL001 modeled 55
5 B0MODEL002 modeled 19
anchored 33.3% / modeled 66.7%
Terminal-style table of a real category sample split between anchored and modeled estimates
One anchored row in three - typical for a category page, not a cherry-picked example.

Two credits to know, one to guess

The estimate call is one credit; checking monthlySoldHistory on /amazon-product-statistics to see which path it took is a second credit on the same ASIN. For a one-off research question, pay both. For a sweep over many ASINs where you only need the number, the estimate alone is enough - just treat a 0 or very small result as low-confidence rather than a verified zero.

Frequently Asked Questions

Is estimated_monthly_sales an exact sales count?

No. It is either Amazon's own rounded 'bought in past month' figure or, for the majority of ASINs that do not show one, a model output - an order of magnitude, not a count.

Why did an estimate come back as 0 or a very small number?

That ASIN has no 'bought in past month' figure and little else to model from (thin rank history, few reviews, a young or very old listing). It signals low confidence, not necessarily zero sales.

How do I know which path produced a given estimate?

Call /amazon-product-statistics for the same ASIN and read stats.monthlySoldHistory. A value in its last entry means the estimate was anchored; an empty list means it was modeled.

Does requesting the rating add a second credit every time?

At most one extra token, and only when the rating and review-count series were refreshed within the last 14 days; otherwise the call still costs the base token.

Which products does Amazon show the bought-last-month figure for?

Amazon's own wording for it calls it a lower bound ('at least 1000') and says most ASINs do not have the value set at all, so expect it on a minority of listings, weighted toward higher-volume ones.

Bottom line: estimated_monthly_sales is Amazon's own rounded figure when Amazon publishes one, and a rank/review/age model for the majority of ASINs that do not - check monthlySoldHistory on the same ASIN before treating a low number as a verified sales count. The sales estimate page and the bestsellers page cover both endpoints, and a free account includes 150 credits to check a category's worth yourself.

Ready to Extract Amazon Data at Scale?

Start building with SellerMagnet API today. Real-time product data, competitive pricing, and review analytics at your fingertips.

150 free API credits • No credit card required • Cancel anytime