Amazon Data API error codes
Standard HTTP status codes with a machine-readable code and a message
written for whoever is reading the log at 3am.
Error shape
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded: 15 requests/second on the Growth plan.",
"status": 429
}
}
Validation failures use FastAPI's detail array instead, so each
problem is tied to a specific field:
{
"detail": [
{
"loc": ["query", "page"],
"msg": "Input should be greater than or equal to 1",
"type": "greater_than_equal",
"input": "0"
}
]
}
Full reference
| Status | Code | Cause | Retry? |
|---|---|---|---|
| 400 | bad_request | The request was malformed — usually a query string that could not be parsed. | No — fix the request |
| 401 | unauthorized | Missing API key. Send it as `x-api-key` or `Authorization: Bearer <key>`. | No — fix the request |
| 403 | forbidden | The key is valid but revoked, or your plan does not include this endpoint. | No — fix the request |
| 404 | not_found | Unknown endpoint path. Check the API reference for the exact spelling. | No — fix the request |
| 422 | validation_error | A parameter failed validation. The `detail` array names the offending field. | No — fix the request |
| 429 | rate_limited | You exceeded your plan's per-second rate limit, or your monthly credits are exhausted. | Yes, with backoff |
| 500 | internal_error | Something broke on our side. These are logged and alerted on. | Yes, with backoff |
| 502 | upstream_error | Amazon returned an unusable response. Safe to retry with backoff. | Yes, with backoff |
| 503 | endpoint_disabled | This endpoint has been taken offline for maintenance. Nothing is charged; check the status page. | Yes, with backoff |
| 504 | upstream_timeout | The upstream request exceeded the time budget. Safe to retry. | Yes, with backoff |
The three you will actually see
429 — rate limited
You exceeded your plan's per-second ceiling. The response carries
Retry-After: 1. Rejected requests cost nothing, so a tight retry loop
is not expensive — just make sure it backs off rather than hammering.
429 — quota exhausted
Different code, same status. Your monthly credits are gone and your plan has no overage allowance. The message includes your reset date. This one will not resolve on retry — upgrade, or wait for the period to roll.
if response.status_code == 429:
code = response.json()["error"]["code"]
if code == "rate_limited":
time.sleep(float(response.headers.get("Retry-After", 1)))
return retry()
if code == "quota_exhausted":
alert_ops("AmazonCrawler credits exhausted") # retrying won't help
raise QuotaError(response.json()["error"]["message"])
502 / 504 — upstream trouble
Amazon returned something unusable or took too long. These are transient and uncharged. Retry with exponential backoff — three attempts clears the overwhelming majority.
What you are never charged for
- 5xx errors — anything that failed on our side or upstream.
- 429s — requests the rate limiter rejected before doing any work.
- 422s — validation caught before we reach Amazon.
- 401 / 403 / 404 — rejected at the edge.
Only a request that reached upstream and came back with a body consumes credits. Check
X-Credits-Cost on any response if you want to verify it.
A retry policy that behaves
import random, time, requests
RETRYABLE = {429, 500, 502, 503, 504}
def call(path, params, *, tries=5):
for attempt in range(tries):
r = session.get(f"https://amazoncrawler.com/v1{path}", params=params, timeout=30)
if r.ok:
return r.json()
if r.status_code == 429 and r.json()["error"]["code"] == "quota_exhausted":
raise QuotaError(r.json()["error"]["message"])
if r.status_code not in RETRYABLE:
r.raise_for_status()
# exponential backoff with jitter, honouring Retry-After
wait = float(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait + random.uniform(0, 0.3))
raise RuntimeError(f"{path}: exhausted {tries} attempts")