Error codes & retry semantics
Complete list of error codes the API returns, when to retry, and how to back off. Only 2xx responses are billed.
Error codes
Every non-2xx response is free. That’s the contract - if we didn’t give you a clean product, you didn’t pay for it.
All errors return a JSON body (Content-Type: application/json) with an error code from the table below, and usually a human-readable message:
{
"error": "target_unreachable",
"message": "Amazon blocked every retry. This is typically transient on hot ASINs, so try again in 60s or more. You were not charged.",
"retryable": true
}
Some codes add extra fields (for example retryable, attempts or last_block_reason on a 502). Every response, success or error, carries an X-Request-Id header. That ID is what we’ll ask for if you email support.
Was the error really from us?
Every response from our API carries an X-Request-Id header and a JSON body. If you see a 5xx with neither, for example a plain-text body such as error code: 502, it was generated by a proxy, CDN or worker platform between you and us, not by the API. In that case the request may still have reached us: send us the time and ASIN and we’ll look it up.
Error table
| HTTP | error code | When it happens | Retry? |
|---|---|---|---|
400 | invalid_params | ASIN not 10 alphanumeric chars, unsupported domain value, language not in the enum, missing query. | No - fix the request. |
401 | unauthorized | Missing or malformed api_key, or unknown key. | No - get a valid key. |
401 | revoked | Key was rotated out. | No - use your current key. |
402 | insufficient_credits | You’re out of credits and have no active auto-top-up. | Yes, after topping up. |
403 | forbidden_region | Egress to the requested country isn’t available on your plan. | No - upgrade or drop the country param. |
404 | not_found | You hit an unknown path. Check the endpoint URL. | No. |
404 | product_not_found | Amazon returned 404 for the ASIN: it’s delisted, the ID is malformed, or it never existed on this marketplace. The response includes retryable: false. | No - drop the ASIN from your set, or try a different marketplace. |
408 | upstream_timeout | Amazon took > 45s to respond. | Yes - retry up to 3 times with 2s backoff. |
422 | blocked_by_amazon | Amazon served a challenge page or 503. We already retried internally. | Yes, but with a 60s+ delay. |
429 | rate_limited | You exceeded your per-key QPS or concurrency ceiling. Check the Retry-After header. | Yes - respect Retry-After. |
500 | internal_error | Unhandled exception on our side. Always get logged. | Yes, after ~5s. |
501 | not_implemented | You passed a roadmap param (render_js, screenshot). | No - wait for the feature. |
502 | target_unreachable | Amazon blocked every internal retry. | Yes, with a 60s+ delay. |
502 | extraction_failed | Amazon served HTML that our extractor couldn’t parse. Usually a new A/B layout; we auto-file a ticket. | Yes, typically works on retry. |
502 | asin_mismatch | You asked for B1, Amazon redirected to B2 (usually a new-edition swap). | No - the source ASIN is what’s stale. |
502 | generic_gallery_page | Amazon served a placeholder shelf (the product is delisted). | No - remove the ASIN from your set. |
503 | capacity | We’re rate-limiting you to protect the shared pool. Rare. | Yes, after Retry-After. |
Retry semantics - the defaults we recommend
Use exponential backoff with jitter for anything marked “Yes” above. Here’s a canonical loop you can port to any language:
async function scrapeWithRetry(fetchFn, maxAttempts = 4) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const res = await fetchFn();
if (res.ok) return res.json();
// Don't retry permanent errors.
if ([400, 401, 402, 403, 404, 501].includes(res.status)) {
throw new Error(`permanent: ${res.status}`);
}
if (attempt === maxAttempts) throw new Error(`gave up after ${attempt}`);
// Respect our Retry-After if we sent one.
const retryAfter = Number(res.headers.get("Retry-After")) * 1000;
const backoff = retryAfter || Math.min(2 ** attempt * 1000, 30_000);
await new Promise(r => setTimeout(r, backoff + Math.random() * 1000));
}
}
Our official SDKs (Node, Python, Go, CLI) do not retry on their own: wrap your calls in a loop like this one whether you use an SDK or the raw HTTP API. For 502 target_unreachable, wait at least 60 seconds before retrying the same ASIN.
Debugging a failed request
Every response carries an X-Request-Id header. If you email [email protected] with that ID (or, if you didn’t keep it, the UTC time and ASIN), we can pull the full internal trace - upstream status, attempts, latency per attempt, extractor output. Keep it in your own logs for at least 7 days so we don’t both have to guess later.
Related
- Authentication - key format + rotation
- Rate limits & concurrency - per-plan ceilings
- Billing policy - only-2xx rule in detail