Home / Blog / Amazon Black Friday Deals API: Track New and Dropped Deals Across the Week

Amazon Black Friday Deals API: Track New and Dropped Deals Across the Week

Use the Amazon Deals API to track Black Friday week deals over time, not just one poll: snapshot /amazon-deals on a schedule, keep a first-seen/last-seen baseline per ASIN, see what is new and what dropped out, and avoid the billed 400 for requesting more than 50 deals. A tested Python script with a persistent baseline file.

November 01, 2026
5 min read
SellerMagnet Team
Share & Bookmark
Terminal running a Python script that snapshots the Amazon Deals API across two days and reports new and dropped deals

To use the Amazon Deals API for Black Friday week, call /amazon-deals with marketplaceId on a schedule, not once: the endpoint has no event calendar and no history of its own, it only answers what is on the deals page right now, for up to 50 deals per call. To see what is new and what dropped out over the week, keep your own baseline keyed by ASIN across every scheduled run.

Key Takeaways

  • /amazon-deals returns the current deals page only; a week's worth of data means your own scheduled snapshots, not one call.
  • count above 50 is a billed 400 ("Count Max: 50"); clamp it to 50 locally before every call.
  • category_id is never validated: an unknown department slug just returns fewer deals, billed the same as a correct one.
  • Track by ASIN, not dealId: dealId and asin can both be "N/A" on a given row, but ASIN is the only one usable as a baseline key often enough to be useful.
  • A page with no deal data at all is a retryable 500, refunded; an empty deals list on a page that does carry deal data is real and billed.

Why one /amazon-deals call is not enough for Black Friday week

A single call to /amazon-deals is a snapshot of right now: up to 50 deals, in Amazon's own "FEATURED" sort, for one marketplace and optionally one department. It carries no start or end date for the sale itself, and it does not remember what was there an hour ago. For a week where the deal list changes several times a day, that means running the same call on a schedule and keeping the history yourself: that is what the script below does.

One trimmed row of /amazon-deals' data.deals[] (illustrative data)

{
  "dealId": "D1",
  "asin": "B0CL61F39H",
  "productTitle": "Wireless Earbuds Pro",
  "dealPrice": {"priceTotal": 39.99, "priceCurrency": "EUR"},
  "basisPrice": 59.99,
  "savings_percentage": 33,
  "dealStartTime": "2026-11-24T00:00:00Z",
  "dealEndTime": "2026-11-30T23:59:00Z",
  "isLightningDeal": false
}
Terminal-theme table of what each Amazon Deals API scenario costs during a Black Friday week tracking schedule
What a scheduled /amazon-deals run can cost, scenario by scenario.

How do I track new and dropped deals across the week in Python?

The script below snapshots /amazon-deals for one marketplace, clamps a caller-supplied count to the API's own ceiling of 50 before paying for the call, and merges the result into a baseline file keyed by ASIN. Rows with asin == "N/A" are counted but never tracked, since there is no stable key for them. Each run reports which ASINs are new since the last run and which ASINs it had seen before but are missing from today's page. It uses sellermagnet.py (ApiError, new_session, get) and retry.py (get_with_retry) from the Python quickstart.

black_friday_snapshot.py - track new and dropped deals across scheduled runs

"""Black Friday week deal tracking: snapshot /amazon-deals on a schedule and track first-seen / last-seen
per ASIN across the week, instead of reading one poll in isolation.

Behaviour this relies on (re-checked 2026-11-01):
- /amazon-deals params: marketplaceId (required), category_id (optional department slug, NOT validated - an
  unknown value is passed straight to Amazon's own department filter and just returns fewer or no deals),
  count (default 30, max 50; a count above 50 is a billed 400 "Count Max: 50", charged before the error is
  returned).
- data.deals[]: dealId ("N/A" when the page gives none), asin ("N/A" sometimes too), productTitle,
  dealPrice.priceTotal (0.0 when unreadable), basisPrice (number or null), savings_percentage (badge % or
  computed) or null, dealStartTime/dealEndTime (ISO 8601 UTC or "N/A").
- An empty deals list on a page that still carries deal data is real data and billed; a page with no deal
  data at all answers a retryable 5xx, not a billed empty list.
- No Amazon calendar dates are used here: "Black Friday week" means whatever 7-day window the caller is
  running snapshots for, taken from the system clock, never hardcoded.

Helpers: sellermagnet.py and retry.py from https://sellermagnet-api.com/blog/amazon-product-api-python
"""
import json
import pathlib

import requests

from retry import get_with_retry
from sellermagnet import ApiError, new_session

MARKETPLACE = "A1PA6795UKMFR9"   # amazon.de; swap for any marketplace ID
MAX_COUNT = 50                    # the API's own ceiling: never send more, it would be a billed 400
BASELINE_PATH = pathlib.Path("deals_baseline.json")


def load_baseline(path):
    if path.exists():
        return json.loads(path.read_text())
    return {}


def save_baseline(path, baseline):
    path.write_text(json.dumps(baseline, indent=2, sort_keys=True))


def snapshot(session, marketplace_id, count=30, category_id=None):
    """One /amazon-deals call. count is clamped locally so a caller-supplied value can never trigger the
    endpoint's own billed 400 for count > 50."""
    params = {"marketplaceId": marketplace_id, "count": min(int(count), MAX_COUNT)}
    if category_id:
        params["category_id"] = category_id   # not validated server-side: an unknown slug just thins the list
    from sellermagnet import get   # low-level helper: /amazon-deals is not wrapped by lookup()
    return get_with_retry(session, "amazon-deals", **params)["deals"]


def apply_snapshot(baseline, deals, run_date):
    """Merge one day's deals into the running baseline. Returns (new_asins, untracked_count).
    Dedupes by ASIN within the snapshot (the page can repeat an ASIN, or give asin == "N/A" for several
    rows); "N/A" rows are counted but never tracked, since there is no key to track them by."""
    seen_this_run = set()
    new_asins = []
    untracked = 0
    for deal in deals:
        asin = deal.get("asin", "N/A")
        if asin == "N/A" or not asin:
            untracked += 1
            continue
        if asin in seen_this_run:
            continue   # same ASIN twice on one page: count once per day
        seen_this_run.add(asin)
        price = (deal.get("dealPrice") or {}).get("priceTotal")
        row = baseline.get(asin)
        if row is None:
            baseline[asin] = {
                "title": deal.get("productTitle", "")[:60],
                "first_seen": run_date,
                "last_seen": run_date,
                "lowest_price": price,
            }
            new_asins.append(asin)
        else:
            row["last_seen"] = run_date
            if price is not None and (row["lowest_price"] is None or price < row["lowest_price"]):
                row["lowest_price"] = price
    return new_asins, untracked


def dropped_since(baseline, run_date, still_present):
    """ASINs tracked before today that did not appear in today's snapshot (dropped out of the deal list)."""
    return sorted(asin for asin, row in baseline.items()
                  if row["last_seen"] != run_date and asin not in still_present)


def run_day(session, run_date, count=30, category_id=None, baseline_path=BASELINE_PATH):
    baseline = load_baseline(baseline_path)
    try:
        deals = snapshot(session, MARKETPLACE, count=count, category_id=category_id)
    except ApiError as err:
        if err.status in (401, 403):   # key or credits: every later call fails the same way too
            print(f"{run_date}: stopped - {err}")
            return
        print(f"{run_date}: marketplace/category rejected - {err}")   # free 400, nothing to merge
        return
    except (requests.ConnectionError, requests.Timeout) as err:
        print(f"{run_date}: network error ({type(err).__name__}), baseline unchanged, retry next schedule")
        return
    new_asins, untracked = apply_snapshot(baseline, deals, run_date)
    still_present = {d.get("asin") for d in deals}
    dropped = dropped_since(baseline, run_date, still_present)
    print(f"{run_date}: {len(deals)} deals seen, {len(new_asins)} new, {untracked} without an ASIN, "
          f"{len(dropped)} dropped out")
    for asin in new_asins:
        print(f"  NEW   {asin}  {baseline[asin]['title']}")
    for asin in dropped:
        print(f"  GONE  {asin}  last seen {baseline[asin]['last_seen']}")
    save_baseline(baseline_path, baseline)


def main():
    session = new_session()
    # Simulate two scheduled snapshots during the week: the baseline file carries first_seen across runs.
    run_day(session, "2026-11-24", count=75)          # a caller-mistake count, clamped to 50 before paying
    run_day(session, "2026-11-26")


if __name__ == "__main__":
    main()

Output (illustrative data: a caller-mistake count, a billed retry, a duplicated ASIN on the page, and a deal dropping out between the two runs)

2026-11-24: 4 deals seen, 2 new, 1 without an ASIN, 0 dropped out
  NEW   B0CL61F39H  Wireless Earbuds Pro
  NEW   B0XMASGIFT  Smart Plug 4-Pack
2026-11-26: 3 deals seen, 1 new, 0 without an ASIN, 1 dropped out
  NEW   B0NEWITEM1  4K Streaming Stick
  GONE  B0XMASGIFT  last seen 2026-11-24

calls= 3
  • count=75 in the first run is clamped to 50 before the request is sent, so the endpoint's own billed 400 for count > 50 never happens; the 75 never reaches the API.
  • the first attempt answers 502 and is retried once per get_with_retry; both attempts are billed, which is why the example's total call count is higher than the number of scheduled runs.
  • B0CL61F39H appears twice on one page (a dealId can repeat with a different ID for the same ASIN); the script counts it once per day, not once per row.
  • B0XMASGIFT is GONE on the second run: it is still in the baseline with its last known last_seen date, it simply did not appear on that day's page - it may come back on a later run, or the deal may have ended.
Terminal-theme sequence of two scheduled Amazon Deals API calls across a Black Friday week, updating a first-seen and last-seen baseline per ASIN
Two scheduled snapshots: the baseline keeps first-seen, updates last-seen, and flags a dropped ASIN.

What does a category_id filter change on this schedule?

Passing category_id restricts the snapshot to one department, but the parameter is never checked against a real category tree: an unrecognised or misspelled slug is passed to Amazon's own filter as-is and simply returns fewer or no deals, billed the same as a correct value. For a tracking schedule that difference matters more than for a one-off call, because a silently-wrong filter looks exactly like a quiet department every single run. See the category_id post for how to validate it against the real tree first.

What does a week of Black Friday deal snapshots cost?

1 credit per scheduled run, independent of how many deals come back, plus 1 credit for every billed retry on a 502/503. A 500 with no Retry-After is refunded and retried once; a second 500 in a row is reported instead of retried again. An invalid marketplaceId is free. Running the snapshot every few hours for a week adds up fast, so pick a schedule that matches how often the deals page actually changes rather than polling continuously; for the base endpoint and its countdown/history-verdict uses, see the Deals API overview and the Lightning Deals countdown post.

Scenario coverage at a glance.
ScenarioResult
count > 50 requestedclamped to 50 before the call
Invalid marketplaceIdfree 400, no retry
500, no deal data on pagerefunded, retried once
502/503retried up to 4x, each billed
Deal with asin == "N/A"counted, not tracked
ASIN missing from today's pageflagged GONE, baseline kept

Frequently Asked Questions

Does the Amazon Deals API tell me when Black Friday starts?

No. /amazon-deals has no event calendar; it returns whatever is on the deals page right now. Track it yourself by snapshotting the endpoint on a schedule through the week, which is what the script below does.

Is an empty deals list during the week an error?

Not by itself. An empty deals array on a page that still carries the deal data structure is real and billed. Only a page with no deal data at all is a retryable 500, which SellerMagnet retries once automatically before you see it.

Can I filter the snapshot to one department?

Yes, with category_id, but it is never validated: an unknown or misspelled slug is not rejected, it just returns fewer or no deals, billed the same as a correct one. See the category_id post before relying on a filtered count.

Why track by ASIN instead of dealId?

dealId can be "N/A" for several rows on the same page, so it cannot key a baseline. ASIN is missing on some rows too (also "N/A"), but it is present often enough to track first-seen and last-seen per product across days; rows without an ASIN are counted but not tracked.

What does a week of snapshots cost?

1 credit per scheduled run (plus a billed retry on a 502/503), independent of how many deals came back. Seven runs a day for a week is 7 credits a day at minimum; a 500 is refunded, so it does not add to that.

Bottom line: treat /amazon-deals as a feed you poll, not a history you can query, keep a small ASIN-keyed baseline file across runs, and clamp count to 50 yourself so a typo never turns into a billed 400. A free account includes 150 credits to try the schedule end to end; see pricing for what a full week of polling costs at scale.

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