Amazon Product API
POST /v1/products returns Amazon (US) buy-box and product data for up to 2,000 ASINs per call (10,000 at pulse), in four tiers that trade detail for throughput, plus delivery promises as an add-on.
POST /v1/products returns Amazon (US) buy-box and product data for up to 2,000 ASINs per call (10,000 at pulse), in four tiers that trade detail for throughput, plus delivery promises as an add-on.
Overview
Send a list of ASINs and a tier; get back one record per ASIN with its buy-box price, stock and fulfilment, and as much product detail as the tier includes. Up to 2,000 ASINs per call (10,000 at pulse), Amazon US (ATVPDKIKX0DER) only. Every ASIN you send comes back exactly once, with a status you can act on, and failed lookups are refunded.
POST https://api.scrapely.dev/v1/products
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"asins": string[],
"tier": "pulse" | "lite" | "standard" | "full",
"include"?: ["delivery"]
}Always 200: A well-formed request is answered
200even if every ASIN came backunavailable. Branch on each result'sstatus, not on the HTTP status. You pay only forokandnot_found.
Quick example
cURL
curl -X POST "https://api.scrapely.dev/v1/products" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"asins":["0060094664","B0009KNLV6","BZZZZZZZZZ"],"tier":"lite"}'Python
import requests
r = requests.post(
"https://api.scrapely.dev/v1/products",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"asins": ["0060094664","B0009KNLV6","BZZZZZZZZZ"], "tier": "lite"},
timeout=300,
)
for p in r.json()["results"]:
print(p["asin"], p["status"], (p.get("buybox") or {}).get("price"))Node.js
const res = await fetch('https://api.scrapely.dev/v1/products', {
method: 'POST',
headers: { Authorization: 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({"asins":["0060094664","B0009KNLV6","BZZZZZZZZZ"],"tier":"lite"}),
signal: AbortSignal.timeout(300_000),
});
const { results, stats } = await res.json();
for (const p of results) console.log(p.asin, p.status, p.buybox?.price);200 OK
{
"tier": "lite",
"results": [
{
"asin": "0060094664",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 4.74,
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Amazon.com",
"isFba": true
}
},
{
"asin": "B0009KNLV6",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 12.99,
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Lumen Direct",
"isFba": false
}
},
{
"asin": "BZZZZZZZZZ",
"status": "not_found",
"fetchedAt": "2026-10-15T18:04:12.077Z"
}
],
"stats": {
"requested": 3,
"ok": 2,
"partial": 0,
"notFound": 1,
"unavailable": 0,
"elapsedMs": 1310
}
}Headers
X-Request-Id: req_8b1e…
X-Scrapely-Credits: 3
X-Scrapely-Balance: 48211
X-Scrapely-Duration-Ms: 1310BZZZZZZZZZ does not exist, so it comes back not_found. That is a definitive answer and is charged like ok.
Tiers
The tier decides which fields you get back, how many ASINs one call takes and how long it runs. Pick the cheapest tier that has the fields you need.
Tier | Credits per ASIN | ASINs per call | Typical call time | Use it for |
|---|---|---|---|---|
| 0.25 | 10,000 | 10–20 s for 10,000 ASINs | Sweeping a whole catalogue for change, as often as you like. |
| 1 | 2,000 | 20–35 s for 2,000 ASINs | Recurring sweeps to detect change. |
| 3 | 2,000 | 25–45 s for 2,000 ASINs | Repricing an ASIN whose price or stock changed. |
| 10 | 2,000 | about a minute for 2,000 ASINs | The first scrape of a new ASIN: building a listing. |
Call times are typical ranges for a full-size call and vary through the day; smaller calls finish proportionally faster.
Fields per tier
Field | pulse | lite | standard | full |
|---|---|---|---|---|
| yes | yes | yes | yes |
| yes | — | yes | yes |
| — | yes | yes | yes |
| — | yes | yes | yes |
| — | yes | yes | yes |
| — | — | yes | yes |
| — | — | yes | yes |
| — | — | yes | yes |
| — | — | yes | yes |
| — | — | yes | yes |
| — | — | yes | yes |
| — | — | — | yes |
| — | — | PARENT_ASIN only | also via variations |
| — | — | — | yes |
| — | — | — | yes |
| — | — | — | yes |
| — | — | — | yes |
| — | — | — | yes |
| — | — | — | yes |
| add-on | add-on | add-on | add-on |
Warning: No tier returns sellers other than the buy box, or feature bullets. Delivery dates are the delivery add-on.
Pulse: change detection
pulse returns one compact summary per ASIN, the new-offer count and the lowest new price, and nothing else. That makes it the cheapest tier, 0.25 credits per ASIN, and the biggest: calls of up to 10,000 ASINs (10–20 s for 10,000 ASINs). It returns status and offers.newCount / offers.newMinPrice only: no buy box, no seller, no stock state, no title.
{
"tier": "pulse",
"results": [
{
"asin": "0060094664",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"offers": {
"newCount": 38,
"newMinPrice": 3.98
}
},
{
"asin": "B0009KNLV6",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"offers": {
"newCount": 4,
"newMinPrice": 12.49
}
},
{
"asin": "BZZZZZZZZZ",
"status": "not_found",
"fetchedAt": "2026-10-15T18:04:12.077Z"
}
],
"stats": {
"requested": 3,
"ok": 2,
"partial": 0,
"notFound": 1,
"unavailable": 0,
"elapsedMs": 1180
}
}Not a buy-box price:
newMinPriceis the lowest NEW price from any seller. Measured on 1,316 bestsellers, it equalled the buy-box price for 92% of them; the other 8% (mostly books where Amazon sits above a cheaper third party) have a pricier buy box. Use pulse to detect that something moved, never to reprice from.
Sweep with pulse, fetch standard on change. Store offers.newCount and offers.newMinPrice per ASIN. On every cycle:
Sweep everything with
pulse(10,000 per call).Send to
standardthe ASINs whosenewCountornewMinPricechanged, thepartialones, and the ASINs whose laststandardfetch showed a buy box above the lowest new price (pulse cannot see those move).Delist on
not_found: it is just as reliable at pulse as on the other tiers.
pulse = products(catalogue, "pulse") # 0.25 credits per ASIN
changed = [a for a, p in pulse.items()
if p["status"] == "partial"
or (p["status"] == "ok" and p.get("offers") != stored[a]["offers"])
or stored[a]["buybox_above_lowest"]]
for asin, p in products(changed, "standard").items():
if p["status"] == "ok":
reprice(asin, p["buybox"])
lowest = p["offers"].get("newMinPrice") or 0
stored[asin]["buybox_above_lowest"] = p["buybox"].get("price", 0) > lowestDelivery add-on
Add "include": ["delivery"] to any tier to get the buy-box offer's delivery promise for an anonymous US shopper: the standard date (or date range), the "fastest" option when there is one, Prime eligibility and where it ships from. It costs +3 credits per ASIN, charged only for ASINs whose delivery comes back. Delivery is looked up for every ok and partial ASIN after the tier's data, so a call with delivery is capped at 2,000 ASINs and takes noticeably longer.
Request
{
"asins": [
"0060094664",
"B0009KNLV6",
"BZZZZZZZZZ"
],
"tier": "standard",
"include": [
"delivery"
]
}200 OK
{
"tier": "standard",
"include": [
"delivery"
],
"results": [
{
"asin": "0060094664",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 4.74,
"priceDisplay": "$4.74",
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Amazon.com",
"isFba": true,
"availabilityMessage": "In Stock",
"condition": "NEW",
"merchant": {
"id": "ATVPDKIKX0DER",
"name": "Amazon.com"
},
"offerListingId": "Xh7+qT2b/kLm9wQ=="
},
"title": "The Lion, the Witch and the Wardrobe",
"description": [
"Four adventurers step through a wardrobe door and into the land of Narnia.",
"Book two in the Chronicles of Narnia."
],
"offers": {
"newCount": 38,
"newMinPrice": 3.98,
"usedCount": 112,
"usedMinPrice": 1.07
},
"isParent": false,
"delivery": {
"standard": {
"earliest": "2026-10-19T07:00:00Z",
"latest": "2026-10-19T07:00:00Z",
"free": true,
"message": "FREE delivery Monday, October 19"
},
"fastest": {
"earliest": "2026-10-16T07:00:00Z",
"latest": "2026-10-16T07:00:00Z",
"message": "Or fastest delivery Tomorrow, October 16. Order within 5 hrs 12 mins"
},
"isPrime": true,
"shipsFrom": "Amazon.com"
}
},
{
"asin": "B0009KNLV6",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 12.99,
"priceDisplay": "$12.99",
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Lumen Direct",
"isFba": false,
"availabilityMessage": "Only 3 left in stock - order soon.",
"condition": "NEW",
"merchant": {
"id": "A3QF8VQX2L7MZN",
"name": "Lumen Direct"
},
"offerListingId": "pQ4/z9Lr0sT="
},
"title": "Lumen 6-Outlet Surge Protector, 4 ft",
"description": [
"Six outlets, 900 joules, 4 ft cord."
],
"offers": {
"newCount": 4,
"newMinPrice": 12.49,
"usedCount": 0
},
"isParent": false,
"delivery": {
"standard": {
"earliest": "2026-10-20T07:00:00Z",
"latest": "2026-10-23T07:00:00Z",
"message": "$5.99 delivery October 20 - 23"
},
"isPrime": false,
"shipsFrom": "Lumen Direct"
}
},
{
"asin": "BZZZZZZZZZ",
"status": "not_found",
"fetchedAt": "2026-10-15T18:04:12.077Z",
"delivery": null,
"deliveryError": "not_fetched"
}
],
"stats": {
"requested": 3,
"ok": 2,
"partial": 0,
"notFound": 1,
"unavailable": 0,
"elapsedMs": 2660,
"delivery": {
"requested": 2,
"ok": 2,
"failed": 0
}
}
}Field | Description |
|---|---|
| The promise shown under the buy box, ISO 8601 as Amazon sends it. Equal for a single-day promise. |
| True for "FREE delivery". Absent when Amazon's text does not say. |
| Amazon's own line, e.g. "FREE delivery Monday, October 19". |
| Same shape: the "Or fastest delivery" option, when Amazon offers one. |
| The buy-box offer is Prime eligible. Absent when Amazon does not say. |
| Where the buy-box offer ships from. Absent when Amazon does not say. |
| Only when |
|
|
Delivery never changes an ASIN's status: a product can be ok with delivery: null. Every null delivery is refunded. Promises are for an anonymous shopper with no address: Amazon's default US estimate, not a specific ZIP code's.
Request
Field | Type | Description |
|---|---|---|
| string[], required | 1 to 2,000 ASINs (10 letters or digits); up to 10,000 at |
| string, required |
|
| string[], optional | Add-ons. |
Send the body as JSON. Authentication is the same as every endpoint: Authorization: Bearer, X-API-Key or ?api_key= (Authentication).
Response
results holds one record per unique ASIN, in the order you sent them, and every ASIN appears exactly once. stats summarises the call.
Field | Description |
|---|---|
| The tier you asked for. |
| Upper-cased. |
|
|
| ISO 8601 time the answer for this ASIN arrived. |
| pulse: |
| Only with |
| Present for |
| Unique ASINs after de-duplication. |
| Counts per status. |
| Only with |
| Wall time of the call. |
pulse
{
"tier": "pulse",
"results": [
{
"asin": "0060094664",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"offers": {
"newCount": 38,
"newMinPrice": 3.98
}
},
{
"asin": "B0009KNLV6",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"offers": {
"newCount": 4,
"newMinPrice": 12.49
}
},
{
"asin": "BZZZZZZZZZ",
"status": "not_found",
"fetchedAt": "2026-10-15T18:04:12.077Z"
}
],
"stats": {
"requested": 3,
"ok": 2,
"partial": 0,
"notFound": 1,
"unavailable": 0,
"elapsedMs": 1180
}
}standard
{
"tier": "standard",
"results": [
{
"asin": "0060094664",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 4.74,
"priceDisplay": "$4.74",
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Amazon.com",
"isFba": true,
"availabilityMessage": "In Stock",
"condition": "NEW",
"merchant": {
"id": "ATVPDKIKX0DER",
"name": "Amazon.com"
},
"offerListingId": "Xh7+qT2b/kLm9wQ=="
},
"title": "The Lion, the Witch and the Wardrobe",
"description": [
"Four adventurers step through a wardrobe door and into the land of Narnia.",
"Book two in the Chronicles of Narnia."
],
"offers": {
"newCount": 38,
"newMinPrice": 3.98,
"usedCount": 112,
"usedMinPrice": 1.07
},
"isParent": false
},
{
"asin": "B0009KNLV6",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 12.99,
"priceDisplay": "$12.99",
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Lumen Direct",
"isFba": false,
"availabilityMessage": "Only 3 left in stock - order soon.",
"condition": "NEW",
"merchant": {
"id": "A3QF8VQX2L7MZN",
"name": "Lumen Direct"
},
"offerListingId": "pQ4/z9Lr0sT="
},
"title": "Lumen 6-Outlet Surge Protector, 4 ft",
"description": [
"Six outlets, 900 joules, 4 ft cord."
],
"offers": {
"newCount": 4,
"newMinPrice": 12.49,
"usedCount": 0
},
"isParent": false
},
{
"asin": "BZZZZZZZZZ",
"status": "not_found",
"fetchedAt": "2026-10-15T18:04:12.077Z"
}
],
"stats": {
"requested": 3,
"ok": 2,
"partial": 0,
"notFound": 1,
"unavailable": 0,
"elapsedMs": 1520
}
}full
{
"tier": "full",
"results": [
{
"asin": "0060094664",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 4.74,
"priceDisplay": "$4.74",
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Amazon.com",
"isFba": true,
"availabilityMessage": "In Stock",
"condition": "NEW",
"merchant": {
"id": "ATVPDKIKX0DER",
"name": "Amazon.com"
},
"offerListingId": "Xh7+qT2b/kLm9wQ=="
},
"title": "The Lion, the Witch and the Wardrobe",
"description": [
"Four adventurers step through a wardrobe door and into the land of Narnia.",
"Book two in the Chronicles of Narnia."
],
"offers": {
"newCount": 38,
"newMinPrice": 3.98,
"usedCount": 112,
"usedMinPrice": 1.07,
"refurbishedCount": 0,
"collectibleCount": 2,
"collectibleMinPrice": 24.5
},
"isParent": false,
"full": {
"listPrice": 8.99,
"images": [
"https://m.media-amazon.com/images/I/81tmrPMBcTL.jpg",
"https://m.media-amazon.com/images/I/41Xq0oK7nFL.jpg"
],
"attributes": {
"Publisher": "HarperCollins",
"Print length": "208 pages"
},
"variationChildren": [
"0064404994",
"B000FC0SIM"
],
"binding": "Paperback",
"reviews": {
"count": "41,872",
"rating": 4.8
}
}
},
{
"asin": "B0009KNLV6",
"status": "ok",
"fetchedAt": "2026-10-15T18:04:11.412Z",
"buybox": {
"price": 12.99,
"priceDisplay": "$12.99",
"inStock": true,
"availabilityType": "IN_STOCK",
"fulfillerName": "Lumen Direct",
"isFba": false,
"availabilityMessage": "Only 3 left in stock - order soon.",
"condition": "NEW",
"merchant": {
"id": "A3QF8VQX2L7MZN",
"name": "Lumen Direct"
},
"offerListingId": "pQ4/z9Lr0sT="
},
"title": "Lumen 6-Outlet Surge Protector, 4 ft",
"description": [
"Six outlets, 900 joules, 4 ft cord."
],
"offers": {
"newCount": 4,
"newMinPrice": 12.49,
"usedCount": 0
},
"isParent": false,
"full": {
"images": [
"https://m.media-amazon.com/images/I/71q8kE2xQVL.jpg"
],
"attributes": {
"Brand": "Lumen"
},
"variationChildren": [],
"binding": "Electronics",
"reviews": {
"count": "1,204",
"rating": 4.6
}
}
},
{
"asin": "BZZZZZZZZZ",
"status": "not_found",
"fetchedAt": "2026-10-15T18:04:12.077Z"
}
],
"stats": {
"requested": 3,
"ok": 2,
"partial": 0,
"notFound": 1,
"unavailable": 0,
"elapsedMs": 1876
}
}Statuses
Status | Meaning | What to do | Billed |
|---|---|---|---|
| Product returned with no errors. | Use it. | Yes |
| Product returned, but some of its fields could not be read. | Do not trust price or stock; re-fetch at a higher tier. | No (refunded) |
| Amazon says the product could not be found. | The ASIN is dead: delist it, do not retry. | Yes |
| We could not get an answer for this ASIN this time. | Retry later. It is not evidence the ASIN is dead. | No (refunded) |
not_found is only ever reported when Amazon itself says the product does not exist. A failed lookup is always unavailable, never not_found, so you can safely delist on not_found.
Credits and billing
Tier | Credits per ASIN | 1,000 ASINs | Largest call | Per ASIN with delivery |
|---|---|---|---|---|
| 0.25 | 250 | 2,500 (10,000 ASINs) | 3.25 |
| 1 | 1,000 | 2,000 (2,000 ASINs) | 4 |
| 3 | 3,000 | 6,000 (2,000 ASINs) | 6 |
| 10 | 10,000 | 20,000 (2,000 ASINs) | 13 |
The call reserves unique ASINs × credits per ASIN (plus the add-on rate with
include), rounded up once per call, before anything is fetched. If your balance cannot cover it you get402 INSUFFICIENT_CREDITSand nothing is charged.partialandunavailableASINs are refunded when the call finishes. You pay forokandnot_found, plus delivery for the ASINs whosedeliverycame back.The final charge is recomputed from what you are billed for and rounded up once, so with pulse's fractional rate a refund never exceeds the charge: 3 pulse ASINs reserve 1 credit, and still cost 1 if one of them is
unavailable.X-Scrapely-Creditsis the final charge after refunds, andX-Scrapely-Balanceyour balance after it.
Example: 800 ASINs at standard reserve 2,400 credits; if 10 come back unavailable, 30 are refunded. 10,000 ASINs at pulse reserve 2,500.
Recommended call strategy
Use each tier for what it is good at. A typical repricing pipeline:
fullonce per new ASIN (10 credits): images, attributes, variations and list price to build the listing. IfisParentis true, list a child fromfull.variationChildreninstead.pulseon every recurring cycle (0.25 credits): compareoffers.newCountandoffers.newMinPricewith what you stored (Pulse). Uselite(1 credit) instead when you need the buy-box price, stock and FBA on every cycle: comparebuybox.price,buybox.inStockandbuybox.isFba.standardonly for the ASINs that changed (3 credits): the merchant, condition and offer listing id you need to act on the change.standardacross every ASIN at a slower cadence (for example every 8th cycle): neither pulse nor lite can see a buy-box seller change at the same price.Handle statuses: delist on
not_found; re-fetchpartialat a higher tier; retryunavailableon the next cycle.
Never write back from lite: Updates to marketplace listings (price, condition, offer id) should use
standardorfulldata. Lite has no merchant, condition or offer listing id.
import requests
API = "https://api.scrapely.dev/v1/products"
H = {"Authorization": "Bearer YOUR_API_KEY"}
def products(asins, tier):
r = requests.post(API, headers=H, json={"asins": asins, "tier": tier}, timeout=300)
r.raise_for_status()
return {p["asin"]: p for p in r.json()["results"]}
sweep = products(tracked_asins, "lite")
changed = [a for a, p in sweep.items()
if p["status"] == "partial"
or (p["status"] == "ok" and p["buybox"] != stored[a]["lite_buybox"])]
dead = [a for a, p in sweep.items() if p["status"] == "not_found"]
for asin, p in products(changed, "standard").items():
if p["status"] == "ok":
reprice(asin, p["buybox"]) # price, merchant, condition, offerListingIdLimits and timing
Limit | Value |
|---|---|
ASINs per call | 2,000; 10,000 at |
With | 2,000 ASINs per call at any tier; the call takes noticeably longer |
Concurrency | One slot of your account's concurrency per call, shared with scrapes and jobs |
Typical call time | pulse: 10–20 s for 10,000 ASINs; lite: 20–35 s for 2,000 ASINs; standard: 25–45 s for 2,000 ASINs; full: about a minute for 2,000 ASINs |
Client timeout | Allow 300 s client-side. |
Seconds for a few hundred ASINs at any tier; a minute or so for 2,000 at full. If a call is about to run out of time, the ASINs not yet fetched come back unavailable (and are refunded) rather than the whole call failing. For the lowest latency, send smaller calls in parallel within your concurrency limit.
Errors
HTTP | Code | When |
|---|---|---|
400 |
|
|
400 |
| Unknown |
401 |
| Missing or invalid API key. |
402 |
| Your balance cannot cover the reservation (unique ASINs × credits per ASIN, plus add-ons). Nothing was charged. |
429 |
| All your concurrency slots are busy, or too many calls in a short window. Honour |
A failed lookup is never an HTTP error: it shows up per ASIN as unavailable. See Errors for the shared error shape.