To get Amazon deals as JSON, call the SellerMagnet /api/amazon-deals endpoint with a marketplace ID and, optionally, a department. One request returns up to 50 current deals with the deal price, the list price it is measured against, the savings percentage, start and end times and a lightning-deal flag. Poll it every few minutes, keep a set of the dealIds you have seen, and you have a deal feed that costs one credit per poll.
Key Takeaways
- One request returns up to 50 deals (count, default 30) for one marketplace, optionally one department.
- dealPrice.priceTotal and basisPrice are numbers in the marketplace currency; basisPrice is null when the page shows no list price.
- dealStartTime and dealEndTime are ISO 8601 in UTC, so a countdown is one subtraction.
- An empty deals list is real data (nothing on offer for that filter) and costs a credit; errors come back as 5xx.
- dealId is the key for deduplication across polls; the same ASIN can be in several deals over time.
What does the deals API return?
The endpoint reads the marketplace's deals page and returns its deal collection as a list. Prices are in the marketplace currency (dealPrice.priceCurrency), times are UTC, and savings_percentage is taken from the deal badge or, when the badge shows none, computed from the two prices. coupon_savings carries the badge text when there is one, such as 20 % Rabatt on amazon.de.
One deal from /api/amazon-deals on amazon.de (example values)
{
"success": true,
"data": {
"deals": [
{
"dealId": "5675cf93",
"asin": "B094YCTK6P",
"productTitle": "IceUnicorn Krabbelschuhe Baby Lauflernschuhe",
"dealLink": "https://www.amazon.de/dp/B094YCTK6P",
"dealPrice": {"priceTotal": 15.89, "priceCurrency": "EUR"},
"basisPrice": 18.69,
"savings_percentage": 15,
"coupon_savings": null,
"dealStartTime": "2025-07-08T04:15:00.000Z",
"dealEndTime": "2025-07-08T16:15:00.000Z",
"isLightningDeal": true,
"image_url": "https://images-eu.ssl-images-amazon.com/images/I/717dw8J-hbL.jpg"
}
]
}
}
| Field | Type | Meaning |
|---|---|---|
dealId | string | Identifier of the deal - use it to deduplicate across polls |
asin | string | The product on offer |
dealPrice.priceTotal | number | Deal price in dealPrice.priceCurrency; 0.0 when the page price could not be read - skip those |
basisPrice | number or null | List price the saving is measured against; null when the page shows none |
savings_percentage | integer or null | From the deal badge, else computed from the two prices |
coupon_savings | string or null | Badge text as shown, e.g. 20 % Rabatt |
dealStartTime / dealEndTime | ISO 8601, UTC | Deal window; N/A when the page gives none |
isLightningDeal | boolean | Time-limited lightning deal. The endpoint does not return a claimed percentage or a Prime-exclusive flag |

How do I monitor Amazon deals with Python?
Poll the endpoint, compare with the dealIds stored from the last poll, and report two things: deals that are new, and deals that end within the next two hours. The state lives in a small JSON file, so a cron job can run the script every 15 minutes without a database.
deal_watch.py - new deals and deals ending soon, one credit per run
import datetime as dt
import json
import os
import pathlib
import requests
API = "https://sellermagnet-api.com/api/amazon-deals"
STATE = pathlib.Path("deals_seen.json")
ENDING_SOON = dt.timedelta(hours=2)
def fetch_deals(marketplace_id: str, category_id: str | None = None, count: int = 50) -> list:
params = {"marketplaceId": marketplace_id, "count": count,
"api_key": os.environ["SELLERMAGNET_API_KEY"]}
if category_id:
params["category_id"] = category_id
resp = requests.get(API, params=params, 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"{resp.status_code} {body.get('message')}")
return body["data"]["deals"]
def ends_in(deal: dict) -> dt.timedelta | None:
end = deal.get("dealEndTime")
if not end or end == "N/A":
return None
return dt.datetime.fromisoformat(end.replace("Z", "+00:00")) - dt.datetime.now(dt.timezone.utc)
seen = set(json.loads(STATE.read_text())) if STATE.exists() else set()
deals = fetch_deals("A1PA6795UKMFR9", category_id="electronics")
for deal in deals:
price = deal["dealPrice"]["priceTotal"]
basis = deal.get("basisPrice") # number, or null when the page shows no list price
key = deal["dealId"] if deal.get("dealId") not in (None, "N/A") else deal["asin"]
left = ends_in(deal)
if price <= 0:
continue # price could not be read on this poll
if key not in seen:
print(f"NEW {deal['asin']} {price} {deal['dealPrice']['priceCurrency']}"
f" (was {basis}, -{deal.get('savings_percentage')}%) {deal['productTitle'][:50]}")
if left is not None and dt.timedelta(0) < left <= ENDING_SOON:
print(f"ENDS {deal['asin']} in {int(left.total_seconds() // 60)} min")
STATE.write_text(json.dumps(sorted({d["dealId"] if d.get("dealId") not in (None, "N/A") else d["asin"]
for d in deals})))
Why dealId and not asin
A product can be discounted again next week under a new deal, and one poll can hold the same ASIN twice with different deal windows. dealId names the offer; asin names the product. When the page shows no deal ID the field is N/A - the script falls back to the ASIN for those.
How do I filter deals by department?
Pass category_id with a department slug - the documented example is electronics; the top-level IDs in the category explorer are the candidates for other departments. Without it you get the marketplace's whole deal collection - lightning deals and the deal of the day alike. An unknown category_id is not rejected; it is passed to Amazon's deals filter as it is, so a typo simply returns fewer or no deals. count defaults to 30 and stops at 50; a higher value returns HTTP 400 Count Max: 50 after the credit is charged. geo_location (a postcode) is best-effort, as on search.
Deals ending within two hours, from the shell
curl -sG "https://sellermagnet-api.com/api/amazon-deals" \
--data-urlencode "marketplaceId=A1PA6795UKMFR9" \
--data-urlencode "category_id=electronics" \
--data-urlencode "count=50" \
--data-urlencode "api_key=$SELLERMAGNET_API_KEY" \
| jq --arg now "$(date -u +%Y-%m-%dT%H:%M:%S)" \
--arg limit "$(date -u -v+2H +%Y-%m-%dT%H:%M:%S 2>/dev/null || date -u -d '+2 hours' +%Y-%m-%dT%H:%M:%S)" \
-r '.data.deals[] | select(.dealEndTime != "N/A" and .dealEndTime > $now and .dealEndTime <= $limit)
| [.asin, .dealPrice.priceTotal, .savings_percentage, .dealEndTime] | @tsv'
What does an empty deals list mean?
An empty deals array with success: true means the deals page carried a deal collection with nothing in it for that filter - a niche department at a quiet hour - and it is billed like any other answer. A page that could not be read at all is not turned into an empty list: it returns HTTP 502 or 503, which the error-handling guide covers. Poll every 10 to 15 minutes: deal windows are hours long, and a lightning deal that appears and sells out inside one interval is not worth a credit a minute.

To judge a deal, pair it with the product's recorded prices: the price history endpoint shows whether the deal price really is a low, and the bestsellers endpoint tells you how the product sells in its category.
Frequently Asked Questions
Is there an API for Amazon deals?
Yes. /api/amazon-deals returns up to 50 current deals of a marketplace as JSON, with deal price, list price, savings, start and end time and a lightning-deal flag.
Can I filter deals by category?
Yes. Pass category_id with a department slug such as electronics. Without it the whole deal collection of the marketplace is returned.
How do I know when a deal ends?
dealEndTime is an ISO 8601 timestamp in UTC. Subtract the current UTC time to get the remaining window; N/A means the page showed no end time.
Can basisPrice be missing?
Yes. It is null when the deal page shows no list price; savings_percentage then comes only from the deal badge, or is null too.
Does Amazon have an official deals API?
The Selling Partner API has none. The Product Advertising API exposes some deal pricing but needs an approved Associates account; this endpoint reads the public deals page instead.
Does an empty deals list cost a credit?
Yes. It is a real answer: the page held no deals for that filter. Read errors are returned as 5xx instead.
Bottom line: one credit per poll, deduplicate by dealId, parse the times as UTC and skip entries whose price could not be read. The deals endpoint page shows the full response, and a free account includes 500 credits - five days of 15-minute polling.