Amazon names the Amazon Buy Box factors itself. Its US help page on the Featured Offer, the offer in the Buy Box, lists total price (item plus shipping), the delivery promise, free shipping, free returns and seller performance. The same page says Amazon does not consider star ratings, Prime eligibility or the fulfillment method, and out-of-stock offers cannot be featured. Two API calls per ASIN show where the featured offer stands on the factors the data contains.
Key Takeaways
- Amazon's US help names five Buy Box factors: total price including shipping, the delivery promise, free shipping, free returns and seller performance.
- The same page rules out customer reviews and star ratings, Prime eligibility and the fulfillment method: with all other factors equal, FBA and FBM offers are equally likely to be featured.
- Since September 2026 there is no seller eligibility requirement, but Amazon can still show no featured offer when no offer meets its customer experience requirements.
- The offers data covers three of the factors for every offer (
totalPrice,deliveryDate,shippingPrice), with limits; free returns and seller performance are not in it. - A factor check costs 2 credits per ASIN when nothing fails: one offers call for the live featured offer, one statistics call for 30 days of holder history.
What are the Amazon Buy Box factors, according to Amazon?
The Featured Offer is the offer with the Add to Cart and Buy Now buttons on a product page. Amazon's US Seller Central help page "Becoming the Featured Offer" says Amazon ranks qualified offers "according to the factors that customers care about most" and adds: "We rank offers the same way for every product, and we never use manual overrides to select one offer over another." When no single offer is clearly more attractive than the others, the page says, the Featured Offer could be a rotation of the best offers. The table lists the factors the page names, plus stock, and what the API returns for each: /amazon-product-offers reads the live offer listing (details on the Product Offers endpoint page).
| Factor | What Amazon's US help says | In the API data |
|---|---|---|
| Price | Total price, item plus shipping, "plays an important role" | totalPrice of every offer, buyBox.totalPrice of the featured one |
| Delivered promise | Faster offers and a specific delivery promise, not a range, are more likely to be featured | deliveryDate, one date per offer or N/A |
| Free shipping | Offers with free shipping are more likely to become featured | shippingPrice 0.0, also when no delivery price was shown |
| Free returns | Free returns can improve your chances | Not in the data |
| Seller performance | Dimensions include order volume, chargeback rate and Voice of the Customer complaints | Not in the data |
| Stock | An offer cannot be featured while it is out of stock | offers empty: no offers were read |
Which Buy Box myths does Amazon rule out?
The same page has a section on what Amazon does not consider, and a few more lines that settle the myths sellers repeat most:
- FBA wins the Buy Box. The fulfillment method is on the not-considered list: "If all other factors are equal, offers fulfilled by Amazon or Fulfilled by Merchant are equally likely to be featured." What counts is what the offer promises, such as a fast, specific delivery date and free shipping, whichever channel delivers it.
- Prime offers win. Prime eligibility of the offer is on the same list. A Prime member may still see a different featured offer, because Prime-enrolled customers may see lower shipping fees or faster delivery promises.
- Good seller ratings win. Amazon evaluates seller performance along dimensions such as order volume, chargeback rate and Voice of the Customer complaints, "not through customer-submitted seller reviews or star ratings".
- The lowest price always wins. "Total price (item price plus shipping fee) plays an important role in determining the Featured Offer", next to the four other factors it names. Amazon's US seller blog puts it this way: "Featured Offer prices are commonly at or below the lowest priced alternatives." Commonly, not always.
- New sellers have to qualify first. "As of September 2026, Amazon no longer maintains a seller eligibility requirement, and offers from all sellers are now eligible to be evaluated for the Featured Offer." Amazon still features no offer at all when none of the available offers meets its customer experience requirements; for that case the page points to three checks: the offer ships to the customer's location, it is in stock, and Pricing Health shows no pricing issue.
- Every shopper sees the same Buy Box. It is determined separately for each variation, and shoppers can see a different featured offer from another shipping location or with a Prime membership.

How do you check the factors on your own ASINs?
Put the ASINs in asins.txt, one per line. For each ASIN the script calls /amazon-product-offers once and compares the featured offer with the other New offers on the factors in the data: the landed price against the cheapest and the delivery date against the earliest, and it shows the featured offer's shipping price. It also shows the fulfillment channel and where the seller rating stands, two signals Amazon says it does not consider. Then it calls /amazon-product-statistics once and counts the sellers that held the Buy Box in the last 30 days and the share of that time nobody did. get_with_retry comes from retry.py, and ApiError and new_session from sellermagnet.py, both published in the Python quickstart. Errors are reported per row: a 400 from the offers endpoint or a 401 or 403 from either endpoint stops the run (a parameter, key or credits problem that would repeat on every ASIN), and so do three ASINs that keep failing. A 400 from the statistics endpoint, which answers on 11 marketplaces, only leaves the history empty.
Install requests, set the key, list the ASINs, run the check
pip install requests
export SELLERMAGNET_API_KEY="your-key-here"
echo B0CL61F39H > asins.txt
python3 buybox_factors.py
buybox_factors.py: the featured offer against the other New offers, plus 30 days of holder history
"""Amazon Buy Box factor check: where the featured offer stands on the factors Amazon names, for a list of ASINs.
Amazon's US help lists the Featured Offer (Buy Box) factors: total price (item plus shipping), the delivery promise,
free shipping, free returns and seller performance. /amazon-product-offers shows the first three for every offer on
the first page of the listing, so the script compares the featured offer with the other New offers on them. It also
prints the seller rating and the channel, which Amazon says it does not consider, and reads the holder history of
/amazon-product-statistics: how many sellers held the Buy Box in the last 30 days and how long nobody did.
Helpers: sellermagnet.py and retry.py from https://sellermagnet-api.com/blog/amazon-product-api-python
"""
import re
from collections import Counter
from datetime import date, datetime, timedelta, timezone
import requests
from retry import get_with_retry
from sellermagnet import ApiError, new_session
MARKETPLACE = "ATVPDKIKX0DER" # amazon.com; the statistics endpoint answers on 11 of the 23 marketplaces
ASIN_FILE = "asins.txt" # one ASIN per line
WINDOW = timedelta(days=30) # how far back the holder history is read
MAX_FAILED = 3 # 502/503 are billed on every attempt: stop after 3 ASINs that keep failing
ROW = "{:<10} {:<13} {:<7} {:<8} {:<5} {:<7} {:>7} {}"
DAY = re.compile(r"\d{4}-\d\d-\d\d")
def read_asins(path):
keep, bad = [], []
with open(path, encoding="utf-8-sig") as fh: # utf-8-sig also reads a file saved with a byte order mark
for line in fh:
asin = line.strip().upper()
if not asin:
continue
if not re.fullmatch(r"[A-Z0-9]{10}", asin):
bad.append(asin) # both endpoints bill a malformed ASIN, so it never leaves the script
elif asin not in keep:
keep.append(asin) # each ASIN once: a repeat would cost 2 more credits
return keep, bad
def priced_new(offer):
"""A New offer whose item price was read: an unread one has priceWithoutShipping 0.0, totalPrice = shipping."""
return offer.get("condition") == "New" and bool(offer.get("priceWithoutShipping"))
# The featured offer against the other New offers: landed price and seller rating against the best of them,
# delivery date against the earliest.
def live_check(data):
box = data.get("buyBox") or {}
total = box.get("totalPrice") or 0
if not data.get("offers"):
return None, "no offers"
if data.get("buyBoxSuppressed") or not total:
return None, "no featured offer"
if box.get("condition") != "New":
return None, "featured offer not New"
new = [o for o in data["offers"] if priced_new(o)]
landed = round(total, 2)
low = min([o["totalPrice"] for o in new] + [landed])
gap = round(landed - low, 2)
price = f"+{gap:.2f}" if gap > 0 else "lowest"
days = [o["deliveryDate"] for o in new if DAY.fullmatch(str(o.get("deliveryDate")))]
own = str(box.get("deliveryDate"))
delivery = "n/a"
if days and DAY.fullmatch(own):
late = (date.fromisoformat(own) - date.fromisoformat(min(days))).days
delivery = "earliest" if late <= 0 else f"+{late} day{'s' if late > 1 else ''}"
mine = box.get("positivePercentage") or 0
rating = "-" # Amazon's own offer and unrated sellers have positivePercentage 0
if mine:
best = max([o.get("positivePercentage") or 0 for o in new] + [mine])
rating = "top" if best <= mine else f"-{best - mine} pts"
channel = "Amazon" if box.get("sellerId") == "Amazon" else box.get("fulfillmentType", "?")
return {"channel": channel, "featured": f"{channel} {landed:.2f}", "price": price, "delivery": delivery,
"ship": f"{box.get('shippingPrice') or 0:.2f}", "rating": rating}, ""
# buyBoxSellerIdHistory lists changes of holder: each entry holds the Buy Box until the next one (or now).
# "-1" = nobody held the Buy Box; "-2" = a seller not identified yet, which counts as one holder.
def holder_check(history, now):
if not history:
return None
changes = sorted(((datetime.strptime(ts, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc), str(who))
for ts, who in history), key=lambda c: c[0])
start = max(now - WINDOW, changes[0][0]) # a history that began inside the window covers less time
if start >= now:
return None
holders, nobody = set(), timedelta(0)
for (ts, who), (until, _) in zip(changes, changes[1:] + [(now, None)]):
begin, end = max(ts, start), min(until, now)
if end <= begin:
continue # this holder's time ended before the window opened
if who == "-1":
nobody += end - begin
else:
holders.add(who)
return len(holders), nobody / (now - start), (now - start).days
def history(session, asin, now):
"""-> (holders, share of the time without a holder, days covered) or None, a note, and whether it failed."""
try:
stats = get_with_retry(session, "amazon-product-statistics", asin=asin, marketplaceId=MARKETPLACE)
except ApiError as err:
if err.status in (401, 403):
raise # key or credits: stop the run
return None, f"history error {err.status}", err.status >= 500 # a 400 here is free, the run goes on
except (requests.ConnectionError, requests.Timeout) as err:
return None, f"history error {type(err).__name__}", True
held = holder_check(stats.get("buyBoxSellerIdHistory"), now)
return held, "" if held else "no holder history", False
def line(asin, cells=("-",) * 6, note=""):
return ROW.format(asin, *cells, note).rstrip()
def main():
asins, bad = read_asins(ASIN_FILE)
if bad:
print("skipped, not an ASIN:", ", ".join(bad))
session, now = new_session(), datetime.now(timezone.utc)
print(line("asin", ("featured", "price", "delivery", "ship", "rating", "holders"), "note"))
checks, held_all, answered, failed = [], [], 0, 0
for asin in asins:
if failed >= MAX_FAILED:
print(f"stopped after {MAX_FAILED} failed ASINs")
break
try:
data = get_with_retry(session, "amazon-product-offers", asin=asin, marketplaceId=MARKETPLACE)
live, note = live_check(data)
held, why, outage = history(session, asin, now)
except ApiError as err:
if err.status in (400, 401, 403): # parameters, key or credits: every other ASIN would fail too
print(f"stopped: {err}")
break
print(line(asin, note=f"error {err.status}"))
failed += err.status != 404 # a 404 is a billed answer (no such product), not an outage
continue
except (requests.ConnectionError, requests.Timeout) as err:
print(line(asin, note=f"error {type(err).__name__}"))
failed += 1
continue
answered += 1
failed += outage
cells = [live[k] for k in ("featured", "price", "delivery", "ship", "rating")] if live else ["-"] * 5
notes = [note, why]
if held:
holders, share, covered = held
held_all.append(holders)
notes += [f"no holder {share:.0%}" if share >= 0.005 else "",
f"history {covered} days" if covered < WINDOW.days else ""]
checks += [live] if live else []
print(line(asin, cells + [held[0] if held else "-"], ", ".join(n for n in notes if n)))
print(f"\nfeatured offers: {len(checks)} of {answered} ASINs that answered")
if checks:
dated = [c for c in checks if c["delivery"] != "n/a"]
rated = [c for c in checks if c["rating"] != "-"]
channels = Counter(c["channel"] for c in checks)
print(f" at the lowest landed price: {sum(c['price'] == 'lowest' for c in checks)} of {len(checks)}")
print(f" with the earliest delivery date: {sum(c['delivery'] == 'earliest' for c in dated)} of {len(dated)}")
print(f" with the best seller rating: {sum(c['rating'] == 'top' for c in rated)} of {len(rated)} rated")
print(" by channel:", ", ".join(f"{k} {n}" for k, n in sorted(channels.items())))
print(f"Buy Box held by 2 or more sellers in {WINDOW.days} days: {sum(h >= 2 for h in held_all)} of "
f"{len(held_all)} ASINs with history")
if __name__ == "__main__":
main()
Output of buybox_factors.py on amazon.com (illustrative data, prices in USD)
skipped, not an ASIN: 4.00638E+12
asin featured price delivery ship rating holders note
B0CL61F39H Amazon 444.99 lowest earliest 0.00 - 2
B0EXAMPLE1 FBM 24.99 +1.00 earliest 0.00 -7 pts 3
B0EXAMPLE2 FBA 19.49 lowest +1 day 0.00 top 1
B0EXAMPLE3 FBM 39.99 +0.01 earliest 0.00 -3 pts 2
B0EXAMPLE4 - - - - - 1 no featured offer, no holder 25%
B0EXAMPLE5 - - - - - 1 no offers, no holder 20%
B0EXAMPLE6 - - - - - - error 404
B0EXAMPLE7 - - - - - - error 502
featured offers: 4 of 6 ASINs that answered
at the lowest landed price: 2 of 4
with the earliest delivery date: 3 of 4
with the best seller rating: 1 of 3 rated
by channel: Amazon 1, FBA 1, FBM 2
Buy Box held by 2 or more sellers in 30 days: 3 of 6 ASINs with history
The illustrative asins.txt had ten lines: eight ASINs, one repeated in lowercase, and 4.00638E+12, a barcode in scientific notation that never reaches the API. On B0EXAMPLE1 the featured offer is an FBM offer at 24.99, 1.00 above the cheapest New offer, an FBA offer at 23.99, but it arrives on 27 October, four days earlier, and its seller is rated 92% against 99%. B0EXAMPLE3 is Amazon's own free shipping example in numbers: the page says "a $40 offer with free shipping is preferable to a $35 offer with $5 shipping", and here 39.99 with free delivery is featured over 34.99 plus 4.99 shipping, which lands 0.01 lower. B0EXAMPLE2 goes the other way: the FBA offer is featured at the lowest price although an FBM offer arrives a day earlier. B0EXAMPLE4 has an offer but no featured offer, and nobody held the Buy Box for 25% of the last 30 days; B0EXAMPLE5 returned no offers at all.

The summary counts four featured offers, so read it as an illustration, not a market figure: run the script on your own ASINs and read each row first. Two of the four were not the cheapest offer, two came from FBM sellers, and only one of the three rated sellers had the best rating, which fits what Amazon's page says and proves nothing beyond these listings. For the size of the price gap across many listings, the Buy Box price gap guide measures the featured offer against the cheapest New offer.
Reading the offers and the holder history
This is the B0EXAMPLE1 offers response the run used, trimmed to the fields the check reads. buyBox is the featured offer, which is also offers[0], and its totalPrice is the unrounded sum of item price and shipping, so the script rounds it before comparing; in offers[] every totalPrice is already rounded to 2 decimals. positivePercentage is the seller rating (0 for Amazon's own offer and for a seller without one), and an offer whose item price could not be read has priceWithoutShipping 0.0, so the script leaves it out.
Trimmed /amazon-product-offers response for B0EXAMPLE1 (illustrative values)
{
"data": {
"asin": "B0EXAMPLE1",
"buyBox": {
"condition": "New",
"deliveryDate": "2026-10-27",
"fulfillmentType": "FBM",
"positivePercentage": 92,
"priceWithoutShipping": 24.99,
"sellerId": "A3EXAMPLESELLR",
"shippingPrice": 0.0,
"totalPrice": 24.99
},
"buyBoxSuppressed": false,
"offers": [
{
"condition": "New",
"deliveryDate": "2026-10-27",
"fulfillmentType": "FBM",
"positivePercentage": 92,
"priceWithoutShipping": 24.99,
"sellerId": "A3EXAMPLESELLR",
"shippingPrice": 0.0,
"totalPrice": 24.99
},
{
"condition": "New",
"deliveryDate": "2026-10-31",
"fulfillmentType": "FBA",
"positivePercentage": 99,
"priceWithoutShipping": 23.99,
"sellerId": "A4EXAMPLESELLR",
"shippingPrice": 0.0,
"totalPrice": 23.99
},
{
"condition": "New",
"deliveryDate": "2026-10-29",
"fulfillmentType": "FBM",
"positivePercentage": 96,
"priceWithoutShipping": 22.49,
"sellerId": "A5EXAMPLESELLR",
"shippingPrice": 4.49,
"totalPrice": 26.98
}
]
},
"success": true
}
buyBoxSellerIdHistory from /amazon-product-statistics (parameters on the Product Statistics endpoint page) lists changes of holder in UTC: each seller keeps the Buy Box until the next entry. The script counts the holders over the last 30 days, including the one who held it when the window opened, and the time marked -1, when nobody held it. -2 is a seller whose ID is not known yet, so it counts as a holder, and on amazon.com Amazon appears as ATVPDKIKX0DER, not as Amazon. The result is a share of time: the Featured Offer percentage in Seller Central's Business Reports counts page views instead, and only for your own offers. The Buy Box history guide turns the same series into a time share per seller.
Trimmed /amazon-product-statistics response for B0EXAMPLE1 (illustrative values)
{
"data": {
"asin": "B0EXAMPLE1",
"buyBoxSellerIdHistory": [
["2026-09-12 10:00:00", "A4EXAMPLESELLR"],
["2026-09-30 18:30:00", "A3EXAMPLESELLR"],
["2026-10-11 07:05:00", "A5EXAMPLESELLR"],
["2026-10-13 21:45:00", "A3EXAMPLESELLR"],
["2026-10-19 08:00:00", "A4EXAMPLESELLR"],
["2026-10-21 16:20:00", "A3EXAMPLESELLR"]
],
"trackingSince": "2024-03-02"
},
"success": true
}
Credit math for a factor check
Every request costs 1 credit, so the check costs 2 credits per ASIN when nothing fails: 100 ASINs cost 200 credits. The run above made 17 billed requests: 6 offers and 6 statistics calls for the ASINs that answered, the 404 for B0EXAMPLE6 (billed, not retried, and the script skips its statistics call) and 4 attempts for B0EXAMPLE7, which kept failing with 502; the repeat and the barcode cost nothing. A 404 is billed and not retried, 502 and 503 are billed on every attempt (up to 4 here), a 500 without a Retry-After header is refunded and retried once, and 429, 401, 403 and gate 400s are free, including the statistics endpoint's 400 on the 12 marketplaces it does not cover. MAX_FAILED stops the run after 3 ASINs that keep failing on either endpoint, so an outage costs at most 12 failed attempts. Plans and credit prices are on the pricing page.
What the data cannot show
- Free returns and seller performance: the offers data has no return policy and no order volume, chargeback or complaint figures, so two of the five factors stay invisible.
- A delivery range:
deliveryDateholds one date per offer, so you cannot see whether Amazon showed a specific date or a range, which its help says is less likely to be featured. - Free shipping versus no shipping shown:
shippingPriceis 0.0 in both cases. - Every shopper's Buy Box: one call reads one delivery location, the marketplace's default postcode unless you pass
geo_location, and Prime members can see a different featured offer. - Every offer: one call reads the first page of the offer listing, the featured offer plus the offers Amazon loads first.
- A sure stock-out: an empty
offerslist can also come from a page whose offers could not be read, so check again before you call a listing out of stock. - Why an offer is featured: the check shows where the featured offer stands on the visible factors; the ranking itself stays Amazon's.
Frequently Asked Questions
What are the Amazon Buy Box factors?
Amazon's US help names total price including shipping, the delivery promise, free shipping, free returns and seller performance, judged on dimensions such as order volume, chargeback rate and Voice of the Customer complaints. An offer that is out of stock cannot be featured.
Does FBA help you win the Buy Box?
Not by itself. Amazon lists the fulfillment method among the factors it does not consider: with all other factors equal, FBA and FBM offers are equally likely to be featured. Fast, specific delivery dates and free shipping count, whichever channel provides them.
Do seller ratings affect the Buy Box?
Not according to Amazon's US help. It evaluates seller performance on dimensions such as order volume, chargebacks and complaints, not through customer-submitted seller reviews or star ratings. Prime eligibility of the offer is not considered either.
Do sellers still need Buy Box eligibility?
No. As of September 2026 Amazon no longer keeps a seller eligibility requirement, and offers from all sellers are eligible to be evaluated. Amazon can still show no featured offer when no offer meets its customer experience requirements.
What does a Buy Box factor check cost in credits?
Two credits per ASIN when nothing fails: one offers call and one statistics call. A 404 is billed, 502 and 503 are billed on every attempt, a 500 without Retry-After is refunded, and 429, 401, 403 and gate 400s are free.
Bottom line: Amazon's own help names the Buy Box factors, total price, delivery promise, free shipping, free returns and seller performance, and rules out star ratings, Prime eligibility and the fulfillment method. Two API calls per ASIN show where the featured offer stands on price, delivery and shipping and whether the Buy Box changes hands; they cannot show returns, seller performance or why Amazon picked an offer.