/amazon-product-offers returns deliveryDate on every offer (and on buyBox) as ONE normalized date, YYYY-MM-DD, or the string "N/A" when the page shows no delivery promise for that offer. It is never a range and never a day count: a page showing "Nov 2 - Nov 6" becomes 2026-11-02, and relative text like "tomorrow" is resolved against the current date before the response is built. The address behind that date is the marketplace's default postcode unless you pass geo_location, and two offers from the same seller with two different delivery dates are not duplicates of each other.
Key Takeaways
deliveryDateis always one value:YYYY-MM-DDor"N/A", never a range and never a number of days - do the day-count math yourself if you need it.- A date range or a relative term on the page ("tomorrow", a weekday) is resolved to a single date before the response is built; there is no separate "earliest/latest" pair.
"N/A"means the page showed no delivery promise for that offer, not that the lookup failed.- Without
geo_location, dates reflect the marketplace's default postcode; a buyer in another region can see different dates. - The same seller can legitimately appear twice with two different
deliveryDatevalues: the API's own duplicate check keys on(sellerId, deliveryDate, condition, fulfillmentType).
What does the deliveryDate field actually contain?
One string per offer: YYYY-MM-DD when the API could read and parse a delivery promise, "N/A" when it could not. There is no separate "earliest" and "latest" pair, no day-count field, and no time component - just the one resolved date. Build your own range or urgency label from that single value if your use case needs one.
| Item | Value |
|---|---|
| Field | deliveryDate (per offer, in offers[] and buyBox) |
| Type | string: YYYY-MM-DD, or N/A |
| Shape | ONE date, never a range, never a day count |
| Source | the page's delivery-promise text, normalized by the API |
| Default address | the marketplace's default postcode, unless geo_location is passed |
| N/A means | the page showed no delivery promise for that offer - not a lookup error |
One offer's deliveryDate (illustrative data)
{
"sellerId": "A2FASTSELLER",
"condition": "New",
"fulfillmentType": "FBA",
"totalPrice": 29.99,
"deliveryDate": "2026-11-02"
}
How does a date range or "tomorrow" turn into one date?
The API strips known non-date words (the shipping term, free-shipping phrasing, articles) from the raw promise text, then tries a set of regex date patterns (including a dedicated pattern for the Japanese 月/日 format), falls back to fuzzy date parsing, then to the first date of a hyphenated range, and finally to relative terms such as "today"/"tomorrow"/"day after tomorrow" mapped per marketplace language. Whichever path matches is adjusted to a future date and formatted YYYY-MM-DD. When none of those steps produce a date, the field stays "N/A".

Why do two offers from the same seller both show a deliveryDate?
Because a different delivery window is not an exact duplicate. The API already drops exact duplicate offer rows on (sellerId, deliveryDate, condition, fulfillmentType); a seller whose page shows two delivery windows for the same condition and fulfilment type differs on that last key field, so both rows survive. Code that assumes "one row per seller" and keeps only the first will silently drop a real, distinct offer - see the offers seller-count script for the full dedupe-vs-count distinction.
Does geo_location change the delivery date?
Yes. Without geo_location, the API reads the page with the marketplace's own default postcode, so deliveryDate reflects that location's shipping promise. Passing geo_location (a ZIP/postcode) asks for another delivery address on a best-effort basis: if every address change attempt is refused or lands elsewhere, the call still runs and returns a billed 200 from whatever location Amazon ended up using, with no field telling you which postcode was actually applied.

How do I find the fastest real offer in Python?
The script below reads the offers of each ASIN, ignores "N/A" and non-New offers, and finds the New offer with the earliest real deliveryDate, then compares it with the featured offer's own date. It skips anything that is not a 10-character ASIN before paying, because the offers endpoint bills a malformed ASIN like any other request. It uses sellermagnet.py (ApiError, new_session) and retry.py (get_with_retry) from the Python quickstart.
delivery_date.py - the New offer with the earliest real delivery date, per ASIN
"""Amazon offer delivery dates: find the fastest real New offer per ASIN and flag what deliveryDate cannot tell you.
deliveryDate on /amazon-product-offers is ONE normalized date per offer (YYYY-MM-DD), or the string "N/A" when the
page shows no delivery promise for that offer - never a range and never a number of days. The API resolves
whatever text Amazon shows (a weekday, a date range, a relative term like "tomorrow") into that one date before
the page's postcode. Two offers from the SAME seller with two different deliveryDate values are NOT a duplicate:
the API's own exact-duplicate dedupe key is (sellerId, deliveryDate, condition, fulfillmentType), so a seller
quoting two delivery windows (perhaps two listed conditions or warehouses) survives as two rows.
Helpers: sellermagnet.py and retry.py from https://sellermagnet-api.com/blog/amazon-product-api-python
"""
import re
from datetime import date
import requests
from retry import get_with_retry
from sellermagnet import ApiError, new_session
MARKETPLACE = "ATVPDKIKX0DER" # amazon.com
ASINS = ["B0CL61F39H", "B0NODATES1", "not-an-asin"]
MAX_FAILED = 3 # 502/503 are billed on every attempt: stop after 3 calls that keep failing
def earliest_new(offers: list) -> dict | None:
"""The New offer with the earliest real deliveryDate, or None when no New offer has one."""
dated = [o for o in offers if o.get("condition") == "New" and re.fullmatch(r"\d{4}-\d{2}-\d{2}",
str(o.get("deliveryDate")))]
return min(dated, key=lambda o: o["deliveryDate"]) if dated else None
def report(asin: str, data: dict) -> str:
offers = data.get("offers") or []
box = data.get("buyBox") or {}
if not offers:
return f"{asin}: no offers"
fastest = earliest_new(offers)
no_promise = sum(1 for o in offers if str(o.get("deliveryDate")) == "N/A")
if not fastest:
return f"{asin}: no New offer shows a delivery date ({no_promise} of {len(offers)} are N/A)"
box_date = str(box.get("deliveryDate"))
if re.fullmatch(r"\d{4}-\d{2}-\d{2}", box_date):
gap = (date.fromisoformat(box_date) - date.fromisoformat(fastest["deliveryDate"])).days
vs_box = "is the featured offer" if gap <= 0 else f"beats the featured offer by {gap} day(s)"
else:
vs_box = "featured offer has no delivery date to compare"
return (f"{asin}: fastest {fastest['deliveryDate']} ({fastest.get('sellerId', '?')}), "
f"{no_promise} of {len(offers)} offers N/A, {vs_box}")
def main():
session = new_session()
failed = 0
for raw in ASINS:
asin = raw.strip().upper()
if not re.fullmatch(r"[A-Z0-9]{10}", asin):
print(f"{raw}: skipped, not an ASIN (offers would bill it)")
continue
if failed >= MAX_FAILED:
print(f"stopped after {MAX_FAILED} failed calls")
break
try:
data = get_with_retry(session, "amazon-product-offers", asin=asin, marketplaceId=MARKETPLACE)
except ApiError as err:
if err.status in (401, 403):
print(f"stopped: {err}")
break
label = "not found (billed 404)" if err.status == 404 else f"error {err}"
print(f"{asin}: {label}")
failed += err.status >= 500
continue
except (requests.ConnectionError, requests.Timeout) as err:
print(f"{asin}: error {type(err).__name__}")
failed += 1
continue
print(report(asin, data))
if __name__ == "__main__":
main()
Output (illustrative data: five offers, one N/A, one Used ignored, a same-seller pair)
B0CL61F39H: fastest 2026-11-02 (A2FASTSELLER), 1 of 5 offers N/A, beats the featured offer by 3 day(s)
B0NODATES1: no New offer shows a delivery date (1 of 1 are N/A)
not-an-asin: skipped, not an ASIN (offers would bill it)
- 1 of 5 offers N/A on B0CL61F39H: the script counts
"N/A"offers without treating them as errors or as "no featured offer". - beats the featured offer by 3 day(s): a plain
date.fromisoformat()subtraction between twoYYYY-MM-DDstrings - deliveryDate never needs its own range parser on the client side. - no New offer shows a delivery date: a real, reportable state for B0NODATES1, not a failed call; the script still prints a result instead of skipping the ASIN.
- A Used offer in the fixture is excluded from "fastest New offer" by its
conditionfield, not by its deliveryDate.
What does a delivery date check cost?
1 credit per /amazon-product-offers call, the same call that returns price, seller and fulfilment type; deliveryDate is not a separate billed field. A malformed ASIN is billed the same way, since the endpoint does not validate the ASIN format before fetching. For every other offer field, see the offer listing reference; for comparing the featured offer against Amazon's other named factors, see the Buy Box factor check.
Frequently Asked Questions
What format is the deliveryDate field in the Amazon API?
YYYY-MM-DD, a single normalized date, or the string N/A when the page shows no delivery promise for that offer. It is never a range or a day count.
Does deliveryDate account for my own location?
Only if you pass geo_location. Without it, dates reflect the marketplace's default postcode, which may differ from a specific buyer's address.
Why does the same seller appear twice with different deliveryDate values?
They are not duplicates. The API drops exact-duplicate rows on (sellerId, deliveryDate, condition, fulfillmentType), so two real delivery windows both survive.
What does deliveryDate N/A mean?
The page showed no delivery promise for that offer. It is a real listing state, not a lookup error, and does not need a retry.
Does checking deliveryDate cost extra credits?
No. It is part of the standard /amazon-product-offers response, 1 credit per ASIN, the same call used for price and seller fields.
Bottom line: read deliveryDate as one resolved date or N/A, remember it reflects the marketplace's default postcode unless you set geo_location, and never collapse two different delivery dates from the same seller as a duplicate. A free account includes 150 credits to try it, and the product offers endpoint page lists every other field in the response.