# Amazon Scraper API - full documentation corpus > Single-file concatenation of every page at https://amazonscraperapi.com/docs. > For the manifest (URL list) only, see https://amazonscraperapi.com/docs/llms.txt. > For an individual page as Markdown, append `.md` to the URL. --- # Getting started _Source: https://amazonscraperapi.com/docs/getting-started_ # Getting started You'll have a working Amazon scrape in under 2 minutes. ## 1. Create an API key 1. Sign in at [app.amazonscraperapi.com](https://app.amazonscraperapi.com). 2. Open **API keys** in the left sidebar. 3. Click **Create key**. Name it (e.g. `local-dev`). Copy the value - it's only shown once. Your key looks like `asa_live_xxxxxxxxxxxxxxxxxxxxxxxx`. Treat it like a password. ## 2. First request - cURL ```bash curl "https://api.amazonscraperapi.com/api/v1/amazon/product?query=B09HN3Q81F&domain=com" \ -H "Authorization: Bearer asa_live_YOUR_KEY" ``` You get back JSON: ```json { "asin": "B09HN3Q81F", "title": "LEGEND COOKWARE 5-Ply Stainless Steel Cookware Set", "price": 299.99, "currency": "USD", "rating": 4.7, "reviews_count": 2481, "images": ["https://m.media-amazon.com/images/I/71..."], "bullet_points": "5-PLY STAINLESS STEEL ... \n ...", "category": [...], "buybox": [...] } ``` Average response time: **~2.6 s median**, **~6 s p95**. ## 3. Same thing in Python ```python import requests r = requests.get( "https://api.amazonscraperapi.com/api/v1/amazon/product", headers={"Authorization": "Bearer asa_live_YOUR_KEY"}, params={"query": "B09HN3Q81F", "domain": "com"}, ) r.raise_for_status() product = r.json() print(product["title"], "-", product["price"], product["currency"]) ``` ## 4. Same thing in Node.js ```javascript const res = await fetch( "https://api.amazonscraperapi.com/api/v1/amazon/product?query=B09HN3Q81F&domain=com", { headers: { Authorization: "Bearer asa_live_YOUR_KEY" } } ); const product = await res.json(); console.log(product.title, "-", product.price, product.currency); ``` ## What costs a credit? **Only successful (HTTP 2xx) responses.** Errors, Amazon challenge pages, and timeouts never touch your balance. See [pricing](https://amazonscraperapi.com/pricing) and the [billing FAQ](https://amazonscraperapi.com/pricing#faq) for the full policy. ## Common errors | HTTP | Body contains | What it means | What to do | |---|---|---|---| | 400 | `invalid_params` | ASIN or domain malformed | Check your query - ASIN is 10 alphanumeric chars, domain is one of the 20 supported TLDs | | 401 | `unauthorized` | API key missing / wrong | Verify the `Authorization: Bearer โ€ฆ` header | | 429 | `rate_limited` | Too many requests from your key | Back off; default rate is generous but not unlimited | | 501 | `not_implemented` | You passed a param that's on the roadmap but not live (e.g. `render_js=true`) | Remove the param; we won't charge for 501 | | 502 | `target_unreachable` OR `extraction_failed` | Amazon blocked us on all retries, or served a page we couldn't parse | Retry in ~30 s; usually transient | ## Related - [Endpoint reference - product](/docs/endpoints/product) - [Endpoint reference - search](/docs/endpoints/search) - [Async batch + webhooks](/docs/endpoints/batch) - [Country + content language](/docs/guides/country-and-language) --- # Changelog _Source: https://amazonscraperapi.com/docs/changelog_ # Changelog Breaking API changes are announced 14 days in advance via email + this page. In-flight requests are always settled under the old contract. --- ## 2026-04-21 **Dashboard** - `/app/settings/billing`: PAYG top-up calculator now updates the "Credits you'll get" and "Basic scrapes" fields live as you type. - Loading skeletons added to dashboard home, billing, usage, and API keys routes - no more blank flash on first navigation. - Sidebar "Documentation" link now points at the new `/docs` site. **Docs** - Launched `/docs` on the marketing site. 11 pages to start: Getting started, 3 endpoints (product / search / batch), 6 guides (auth, errors, rate limits, SDKs, billing, country & language), and this changelog. - Added `/docs/llms.txt`, `/docs/llms-full.txt`, and an `.md` variant for every doc URL so LLMs / coding agents can ingest the full corpus cheaply. **SDKs** - `amazon-scraper-api-sdk` (Node) v0.1.4 - README updated with 5-credit pricing, removed claims about "no credit system". - `amazonscraperapi-sdk` (Python) v0.1.4 - same. - `amazon-scraper-api-sdk-go` v0.1.4 - same. --- ## 2026-04-16 **API** - Credit rebase: 1 basic scrape is now priced at **5 credits** (was 1). Plan allowances and PAYG packages scaled x5 accordingly - your dollar-cost-per-scrape is unchanged. The "1 credit = 1 scrape" pre-launch shorthand only applied to internal testing. - Headers prefix changed from `Spb-*` to `Asa-*`. Old prefix will keep returning values for 90 days, then be removed. **Dashboard** - Live credit balance now read from authoritative ledger on every page load - no more stale numbers from cached mirrors. - Monthly usage graph restyled with visible bars even on zero-credit days. --- ## 2026-04-10 **API** - Shipped production success-rate improvements for Amazon's latest A/B layout. Measured SR jumped from ~87% to ~97% on a 30-query mixed international set. - Parser hardening: gift-card and subscription-plan Amazon product templates now extract correctly instead of hitting `extraction_failed`. --- ## 2026-04-01 **API** - `render_js` and `screenshot` query params reserved. Passing either returns `501 not_implemented` today; the real implementation is on the roadmap for Q3. - New response header `Asa-Attempts` reports how many internal retries we used to fetch your page. **Billing** - Non-2xx responses are now *guaranteed* free - no edge case where a partial 502 is charged. See [Billing policy](/docs/guides/billing). --- ## Earlier Older entries predate the public launch and aren't preserved here. If you need historical info (old behaviour of a specific endpoint), email and we'll pull it from internal records. ## Related - [Getting started](/docs/getting-started) - [Billing policy](/docs/guides/billing) --- # Product endpoint _Source: https://amazonscraperapi.com/docs/endpoints/product_ # `GET /v1/amazon/product` Returns structured data for a single Amazon product page. ## Request ```http GET /api/v1/amazon/product?query=B09HN3Q81F&domain=com Authorization: Bearer asa_live_YOUR_KEY ``` ### Query parameters | Param | Type | Required | Default | Description | |---|---|---|---|---| | `query` | string | โœ… yes | - | 10-character ASIN (or 10-char ISBN for books) | | `domain` | enum | - | `com` | Amazon marketplace TLD: `com`, `co.uk`, `de`, `fr`, `it`, `es`, `nl`, `pl`, `se`, `ca`, `com.mx`, `com.br`, `com.au`, `co.jp`, `sg`, `in`, `com.tr`, `ae`, `sa`, `eg` | | `language` | string | - | marketplace default | Content language as `xx_YY` (e.g. `en_US`, `de_DE`, `es_ES`). See [supported languages per marketplace](/docs/guides/country-and-language). | | `add_html` | boolean | - | `false` | Attach the raw HTML of the Amazon page under `html` in the response | | `render_js` | boolean | - | - | ๐Ÿ”œ **Coming soon** - returns 501 today | | `screenshot` | boolean | - | - | ๐Ÿ”œ **Coming soon** - returns 501 today | ### Response (200) ```json { "asin": "B09HN3Q81F", "asin_in_url": "B09HN3Q81F", "parent_asin": null, "url": "https://www.amazon.com/dp/B09HN3Q81F", "page": 1, "page_type": "Product", "title": "LEGEND COOKWARE 5-Ply Stainless Steel Cookware Set", "product_name": "LEGEND COOKWARE 5-Ply Stainless Steel Cookware Set", "description": "Premium 5-ply construction distributes heat evenly โ€ฆ", "bullet_points": "5-PLY STAINLESS STEEL COOKWARE HEATS EVENLY\\nโ€ฆ", "brand": "LEGEND COOKWARE", "manufacturer": "LEGEND COOKWARE", "store_url": "https://www.amazon.com/stores/LEGEND+COOKWARE", "price": 299.99, "price_buybox": 299.99, "price_strikethrough": 399.99, "currency": "USD", "pricing_count": 3, "stock": "In Stock", "is_prime": true, "max_quantity": 10, "delivery": [ { "date": { "by": "Tuesday, April 25" }, "type": "FREE Delivery" } ], "featured_merchant": { "name": "LEGEND COOKWARE Store", "seller_id": "A1EXAMPLE", "is_amazon_fulfilled": true }, "buybox": [ { "price": 299.99, "seller_name": "LEGEND COOKWARE Store", "seller_id": "A1EXAMPLE", "stock": "In Stock", "returns": "Free returns within 30 days" } ], "category": [ { "ladder": [ { "name": "Home & Kitchen", "url": "โ€ฆ" }, { "name": "Kitchen & Dining", "url": "โ€ฆ" }, { "name": "Cookware", "url": "โ€ฆ" } ]} ], "rating": 4.7, "reviews_count": 2481, "rating_stars_distribution": [ { "rating": 5, "percentage": 72 }, { "rating": 4, "percentage": 18 }, { "rating": 3, "percentage": 6 }, { "rating": 2, "percentage": 2 }, { "rating": 1, "percentage": 2 } ], "reviews": [ { "id": "R1โ€ฆ", "author": "J. Smith", "title": "Excellent build quality", "content": "These pans heat evenly and clean up beautifully.", "rating": 5, "timestamp": "Reviewed in the United States on April 12, 2026", "is_verified": true, "helpful_count": 12 } /* ~8 more */ ], "answered_questions_count": 23, "variations": [ { "asin": "B09HN3Q81F", "dimensions": { "color": "Silver", "size": "10 pc" }, "selected": true }, { "asin": "B09HN3Q81G", "dimensions": { "color": "Copper", "size": "10 pc" }, "selected": false } ], "images": [ "https://m.media-amazon.com/images/I/71โ€ฆ", "https://m.media-amazon.com/images/I/81โ€ฆ" ], "has_videos": true, "product_details": { "Brand": "LEGEND COOKWARE", "Material": "Stainless Steel" }, "product_dimensions": "17.1 x 13.1 x 12.6 inches", "sales_rank": [ { "ladder": [{ "name": "Home & Kitchen", "url": "โ€ฆ" }], "rank": 342 } ] } ``` ### Response headers - `Asa-Cost` - credits spent on this request (`5` for a standard product scrape, `0` for any non-2xx response) - `Asa-Resolved-Url` - the final Amazon URL after any redirects - `Asa-Source-Status` - Amazon's raw HTTP status (may differ from our HTTP status) - `Asa-Attempts` - how many internal attempts it took us to get this result - `Asa-Extractor-Version` - e.g. `amazon@1.0.0` ## Errors | HTTP | Body | Reason | |---|---|---| | 400 | `{"error":"invalid_params"}` | ASIN not 10 alphanumeric chars, domain not in enum | | 401 | `{"error":"unauthorized"}` | Missing / bad API key | | 429 | `{"error":"rate_limited"}` | Too many requests from your key | | 501 | `{"error":"not_implemented","params":["render_js"]}` | You passed a roadmap param | | 502 | `{"error":"target_unreachable"}` | Amazon blocked every internal retry we made | | 502 | `{"error":"extraction_failed"}` | Amazon served something we couldn't parse | | 502 | `{"error":"asin_mismatch"}` | Requested ASIN redirected to a different product | | 502 | `{"error":"generic_gallery_page"}` | Amazon served a placeholder gallery (delisted product) | ## Related - [Search endpoint](/docs/endpoints/search) - [Async batch endpoint](/docs/endpoints/batch) - [Country + content language guide](/docs/guides/country-and-language) - [Billing policy - only-2xx billing](/docs/guides/billing) --- # Search endpoint _Source: https://amazonscraperapi.com/docs/endpoints/search_ # `GET /v1/amazon/search` Returns Amazon search-results listings for a keyword query. ## Request ```http GET /api/v1/amazon/search?query=wireless+headphones&domain=com&sort_by=best_match Authorization: Bearer asa_live_YOUR_KEY ``` ### Query parameters | Param | Type | Required | Default | Description | |---|---|---|---|---| | `query` | string | โœ… yes | - | Search keywords | | `domain` | enum | - | `com` | Amazon marketplace TLD | | `sort_by` | enum | - | `best_match` | `best_match` ยท `price_asc` ยท `price_desc` ยท `avg_customer_review` ยท `newest` | | `start_page` | int | - | `1` | Page to start from (1-10) | | `pages` | int | - | `1` | Number of consecutive pages to fetch (1-10) - each counts as a request | ### Response (200) ```json { "page": 1, "products": [ { "asin": "B0BDHWDR12", "title": "Sony WH-1000XM5 Wireless Noise-Cancelling Headphones", "url": "https://www.amazon.com/dp/B0BDHWDR12", "price": 348.00, "price_strikethrough": 399.99, "currency": "USD", "rating": 4.5, "reviews_count": 18420, "image": "https://m.media-amazon.com/images/I/71โ€ฆ", "is_prime": true, "is_amazons_choice": false, "is_sponsored": false, "best_seller": true, "organic_position": 1, "sponsored_position": null, "shipping_information": "FREE delivery", "manufacturer": "Sony", "is_video": false } ], "html": null } ``` ## Common errors Same as the product endpoint. `502 target_unreachable` on international marketplaces is more common than on `.com` - see our [country routing guide](/docs/guides/country-and-language). ## Related - [Product endpoint](/docs/endpoints/product) - [Batch a hundred search queries](/docs/endpoints/batch) --- # Batch endpoint (async) _Source: https://amazonscraperapi.com/docs/endpoints/batch_ # `POST /v1/amazon/batch` - Async batch scraping Submit a list of ASINs or search queries in one HTTP call. The worker processes them in the background and either POSTs the results to a webhook URL you provide or lets you poll for status. ## When to use this - You need to scrape 100+ products and don't want to manage request orchestration yourself - You want fire-and-forget semantics with a webhook callback - You're building a price-monitoring job that runs periodically across your catalogue For a handful of ASINs, use the plain product or search endpoints - batch has ~60 s of overhead before processing begins. ## Request ```http POST /api/v1/amazon/batch Authorization: Bearer asa_live_YOUR_KEY Content-Type: application/json ``` ```json { "endpoint": "amazon.product", "items": [ { "query": "B09HN3Q81F", "domain": "com" }, { "query": "B08D6T6BKS", "domain": "com" }, { "query": "B092RCLKHN", "domain": "com", "language": "es_US" } ], "webhook_url": "https://your.server/webhooks/amazon-scrapes" } ``` ### Body parameters | Param | Type | Required | Description | |---|---|---|---| | `endpoint` | `"amazon.product"` OR `"amazon.search"` | โœ… yes | Which endpoint to call for each item | | `items` | array (1-1000) | โœ… yes | Each item = same shape as the query string params you'd pass to the sync endpoint | | `webhook_url` | string (https) | - | If set, we POST the completed results here when all items are done. Must be a public URL. | ## Response (201) ```json { "id": "8a1f3b76-2e4c-4a7f-b9e2-1c9d3e5f7a8b", "status": "pending", "total_count": 3, "created_at": "2026-04-20T14:32:01.000Z", "webhook_signature_secret": "whsec_abcd1234...", "poll_url": "/api/v1/amazon/batch/8a1f3b76-2e4c-4a7f-b9e2-1c9d3e5f7a8b" } ``` **Save `webhook_signature_secret` now.** It's only returned once - use it to verify webhook POSTs are genuine. ## Processing Our worker runs every 60 s and processes up to 100 items per run across all pending batches. A 1,000-item batch typically completes within 10-12 minutes. Each item is dispatched through the same pipeline as the sync endpoints (same auth, same billing, same retries). **Only successful (2xx) items are billed.** ## Polling status ```http GET /api/v1/amazon/batch/8a1f3b76-2e4c-4a7f-b9e2-1c9d3e5f7a8b Authorization: Bearer asa_live_YOUR_KEY ``` ```json { "id": "8a1f3b76-...", "status": "running", // "pending" | "running" | "complete" | "failed" "endpoint": "amazon.product", "total_count": 3, "processed_count": 2, "success_count": 2, "failure_count": 0, "credits_charged": 2, "started_at": "2026-04-20T14:32:42.000Z", "completed_at": null, "webhook_url": "https://your.server/webhooks/amazon-scrapes", "webhook_delivered_at": null, "results": [ { "input": { "query": "B09HN3Q81F", ... }, "status": "ok", "http_status": 200, "data": { /* full product JSON */ }, "duration_ms": 2851, "credits_charged": 1 }, { "input": { "query": "B08D6T6BKS", ... }, "status": "ok", "http_status": 200, "data": { /* full product JSON */ }, "duration_ms": 3120, "credits_charged": 1 } ] } ``` ## Webhook payload Once all items complete, we POST to your `webhook_url`: ```http POST https://your.server/webhooks/amazon-scrapes Content-Type: application/json X-ASA-Batch-Id: 8a1f3b76-... X-ASA-Event: batch.completed X-ASA-Signature: sha256= ``` Body is identical to the `GET /batch/{id}` response. ### Verifying the signature (Node.js) ```javascript import crypto from "node:crypto"; function verifyWebhook(req, secret) { const sig = req.headers["x-asa-signature"]; // "sha256=" const expected = `sha256=${crypto.createHmac("sha256", secret).update(req.rawBody).digest("hex")}`; return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); } ``` ### Verifying in Python ```python import hmac, hashlib def verify_webhook(signature_header: str, raw_body: bytes, secret: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(signature_header, expected) ``` ## Retry policy If your webhook returns non-2xx, we retry up to 5 times with backoff (1min โ†’ 2min โ†’ 5min โ†’ 15min โ†’ 1h), then stop. Check `webhook_delivery_status` and `webhook_last_error` on the batch row. ## Billing Each item is billed independently per the normal rules. If 800 of 1,000 items succeed, you pay $0.40 (800 ร— $0.0005). Failed items cost nothing. ## Limits - 1,000 items per batch - No limit on concurrent batches per account - Worker processes ~100 items / minute globally - sustained throughput for a single batch is ~6,000 items/hour ## Related - [Product endpoint](/docs/endpoints/product) - [Search endpoint](/docs/endpoints/search) - [Billing policy](/docs/guides/billing) --- # Authentication _Source: https://amazonscraperapi.com/docs/guides/authentication_ # Authentication Every request to `https://api.amazonscraperapi.com` must carry your API key as a Bearer token: ```http GET /api/v1/amazon/product?query=B09HN3Q81F Authorization: Bearer asa_live_YOUR_KEY_HERE ``` Requests without a valid key return `401 Unauthorized`. Unauthorized requests are free - they never touch your credit balance. ## Key format All keys start with a prefix so you can tell them apart at a glance: | Prefix | Meaning | |---|---| | `asa_live_` | Production traffic. This is what you want 99% of the time. | | `asa_test_` | Sandbox keys (roadmap). Return deterministic fixtures, never touch Amazon, never bill. | The 12 characters after the prefix (e.g. `asa_live_Tq0x4k9Pd8Mn`) form the shortprefix displayed in the dashboard and in our logs - enough to identify a key without revealing the full secret. ## Where to get a key Sign up at [app.amazonscraperapi.com](https://app.amazonscraperapi.com) (Google sign-in or magic-link email; no card required for the free tier). Go to **Settings > API keys** and click **Generate new key**. The full key is shown exactly once at creation time - save it to your secrets manager right then. ## Rotating keys 1. Generate a second key in the dashboard. 2. Deploy the new key to your app. 3. Revoke the old key. Revocation takes effect within ~30 seconds globally. Revoked keys return `401 Unauthorized` with `{"error":"revoked"}`. ## Multiple keys, workspace-wide You can have up to 20 active keys per workspace. Usage is aggregated across all of them - your plan's monthly credit allowance is per workspace, not per key. The **Usage** tab lets you filter by key so you can tell which service burned through what. ## Key hygiene (important) - **Never embed a key in client-side code** - browsers, mobile apps, public repos are all one-way streets. Proxy requests through your own server. - **Store keys in a secrets manager.** Not in `.env` files checked into git. Not in Slack screenshots. - **Use a separate key per environment** (`asa_live_...` for prod, another for staging). When a key leaks, you revoke only the affected environment. - **Rotate on a schedule** - quarterly is a reasonable cadence. If a key ever appears in logs, git history, or a paste bin, rotate immediately. ## IP allow-lists (roadmap) Restricting a key to a list of egress IPs will be available on Pro and Custom plans. Write to if you need it sooner - we can enable it manually. ## What happens to usage when a key is revoked Historical usage for a revoked key stays in your dashboard for 90 days under **Usage**. The key itself can no longer authenticate, so no new charges are possible against it. ## Related - [Getting started](/docs/getting-started) - make your first request - [Error codes](/docs/guides/errors) - full list of `401` reasons - [Rate limits & concurrency](/docs/guides/rate-limits) - per-key ceilings --- # Error codes & retry semantics _Source: https://amazonscraperapi.com/docs/guides/errors_ # 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 with the same shape: ```json { "error": "string_code", "message": "human-readable explanation", "docs_url": "https://amazonscraperapi.com/docs/guides/errors#string_code", "request_id": "req_9f2c8a1b3e4d" } ``` The `request_id` is what we'll ask for if you email support. `docs_url` deep-links to the row in this table. ## 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 `Authorization` header, malformed Bearer token, 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. | | `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: ```javascript 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) apply this policy automatically. You only need to bake it in yourself if you're calling the raw HTTP API. ## Debugging a failed request Every response carries `request_id` in the body. If you email with that ID, 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](/docs/guides/authentication) - key format + rotation - [Rate limits & concurrency](/docs/guides/rate-limits) - per-plan ceilings - [Billing policy](/docs/guides/billing) - only-2xx rule in detail --- # Rate limits & concurrency _Source: https://amazonscraperapi.com/docs/guides/rate-limits_ # Rate limits & concurrency Two ceilings apply to every API key: 1. **Concurrency** - how many requests can be in-flight at the same moment. 2. **Sustained throughput** - how many requests per second, averaged over a short window, you can send before we start returning `429`. Both are generous enough that most customers never hit them, and both scale with your plan. ## Per-plan ceilings | Plan | Concurrent in-flight | Sustained RPS | Burst ceiling | |---|---|---|---| | Free | 10 | 2 | 10 for 3s | | Vibe ($19/mo) | 30 | 10 | 40 for 3s | | Pro ($79/mo) | 50 | 25 | 60 for 3s | | Custom ($100-$2,000/mo) | 100 - 500+ | 50 - 500 | 2x sustained for 3s | "Burst ceiling" means you can briefly exceed the sustained RPS by that much before we start pushing back. Once the 3s window closes, you're back to the sustained figure. ## What you see when you hit a limit A `429` response with a standard `Retry-After` header: ```http HTTP/1.1 429 Too Many Requests Retry-After: 2 Content-Type: application/json { "error": "rate_limited", "message": "Exceeded sustained RPS (25) for key asa_live_Tq0x...", "docs_url": "https://amazonscraperapi.com/docs/guides/rate-limits", "request_id": "req_9f2c..." } ``` **Always respect `Retry-After`.** All of our official SDKs (Node / Python / Go / CLI) do this automatically. If you're calling raw HTTP, the pattern is: ```javascript if (res.status === 429) { await sleep(Number(res.headers.get("Retry-After") || 1) * 1000); return retry(); } ``` ## Checking your current utilisation Every successful response carries two informational headers: - `Asa-Concurrency: 3/20` - you have 3 requests in-flight out of 20 allowed. - `Asa-Rps: 8/10` - you're sending 8 RPS on average in the current window against a 10 RPS ceiling. Plot these in your own observability stack and you'll see headroom before you run out of it. ## Concurrency, explained If your ceiling is 30, you can have exactly 30 requests open to our API at once. Open a 31st and it queues client-side (in our SDKs) or gets a `429` immediately (raw HTTP). Long-running requests - for example a Batch poll that takes 8 minutes - consume a concurrency slot the whole time. Rule of thumb: **Concurrency = Target RPS x Average request duration (seconds)**. At 10 RPS with a 3 s average, you need 30 concurrent slots. That's exactly what the Vibe plan ships, and Pro's 50 covers a 17 RPS sustained workload. ## Batch endpoint Batch submissions themselves are cheap - one `POST /v1/amazon/batch` with 1,000 items consumes one concurrency slot for the duration of the POST (a few seconds). The *items* inside the batch are processed by our worker under a separate internal concurrency budget that doesn't count against yours. ## How to get more - **Upgrade.** Each paid tier bumps both ceilings by 2-4x. - **Custom plan.** If you're already on Pro and hitting 50 concurrent, the Custom tier ladder goes up to 1,000 concurrent for $2,000/mo. - **Short-term boosts.** Email with the numbers you need and how long. If it fits the pool budget, we'll flip it on without requiring a plan change. ## Preventing bursts in your own code Even when you have the headroom, a polite client is a resilient client. The Node SDK exposes a built-in token-bucket limiter: ```javascript import { AmazonScraperAPI } from "amazon-scraper-api-sdk"; const asa = new AmazonScraperAPI("asa_live_โ€ฆ", { maxConcurrency: 30, // defaults to your plan's ceiling if omitted maxRps: 10, }); // Now you can fire-and-forget 10,000 ASINs; the SDK paces them. const results = await Promise.all( asins.map(a => asa.product({ query: a, domain: "com" })) ); ``` Equivalent code in Python, Go, and the CLI ships the same limiter. ## Related - [Authentication](/docs/guides/authentication) - [Error codes](/docs/guides/errors) - [SDK quick-starts](/docs/guides/sdks) --- # SDK quick-starts _Source: https://amazonscraperapi.com/docs/guides/sdks_ # SDK quick-starts We publish four official SDKs. Each wraps the HTTP API with typed helpers, built-in retry/backoff on 408/429/5xx, and a token-bucket rate limiter that respects your plan's ceiling. Pick the one that matches your stack and skip the raw `fetch` code. | Language | Package | Install | |---|---|---| | Node.js / TypeScript | [`amazon-scraper-api-sdk`](https://www.npmjs.com/package/amazon-scraper-api-sdk) | `npm install amazon-scraper-api-sdk` | | Python | [`amazonscraperapi-sdk`](https://pypi.org/project/amazonscraperapi-sdk/) | `pip install amazonscraperapi-sdk` | | Go | [`github.com/ChocoData-com/amazon-scraper-api-sdk-go`](https://pkg.go.dev/github.com/ChocoData-com/amazon-scraper-api-sdk-go) | `go get github.com/ChocoData-com/amazon-scraper-api-sdk-go` | | CLI (any language) | [`amazonscraperapi-cli`](https://www.npmjs.com/package/amazonscraperapi-cli) | `npm install -g amazonscraperapi-cli` | ## Node / TypeScript ```bash npm install amazon-scraper-api-sdk ``` ```typescript import { AmazonScraperAPI } from "amazon-scraper-api-sdk"; const asa = new AmazonScraperAPI("asa_live_YOUR_KEY"); const product = await asa.product({ query: "B09HN3Q81F", domain: "com", }); console.log(product.title, product.price); ``` Typed responses, auto-pagination on Search, webhook-signature verification on Batch. Works in Node 18+ and any ESM/CJS setup. See the [npm README](https://www.npmjs.com/package/amazon-scraper-api-sdk) for the full reference. ## Python ```bash pip install amazonscraperapi-sdk ``` ```python from amazonscraperapi import AmazonScraperAPI asa = AmazonScraperAPI(api_key="asa_live_YOUR_KEY") product = asa.product(query="B09HN3Q81F", domain="com") print(product["title"], product["price"]) ``` Works on Python 3.9+. Sync and async variants ship side by side (`asa.product` vs `await asa.product_async`). Type hints via `TypedDict`. ## Go ```bash go get github.com/ChocoData-com/amazon-scraper-api-sdk-go ``` ```go package main import ( "context" "fmt" "log" amz "github.com/ChocoData-com/amazon-scraper-api-sdk-go" ) func main() { client := amz.New("asa_live_YOUR_KEY") data, err := client.Product(context.Background(), amz.ProductParams{ Query: "B09HN3Q81F", Domain: "com", }) if err != nil { log.Fatal(err) } fmt.Println(data.Title, data.Price) } ``` Context-aware cancellation, idiomatic Go error wrapping, zero external deps beyond `net/http`. ## CLI The CLI is handy for quick one-off scrapes from a terminal or shell script. ```bash npm install -g amazonscraperapi-cli asa login asa_live_YOUR_KEY # stored in ~/.asa/config # Single product asa amazon product --query B09HN3Q81F --domain com # Search asa amazon search --query "laptop" --domain com --pages 2 # Pipe a list of ASINs from a file cat asins.txt | asa amazon product --domain com --json-per-line > out.ndjson ``` The CLI outputs JSON by default, or NDJSON (one row per line) with `--json-per-line` - easy to pipe into `jq`, `awk`, or a warehouse loader. ## Raw HTTP (no SDK) For stacks we don't publish an SDK for yet (Java, Ruby, PHP, C#), the API is a plain `GET` with a Bearer token. Every endpoint page has a `cURL` snippet you can port. Examples: - [Product endpoint](/docs/endpoints/product) - [Search endpoint](/docs/endpoints/search) - [Async Batch endpoint](/docs/endpoints/batch) ## What the SDKs do that raw HTTP doesn't - **Exponential backoff with jitter** on 408 / 429 / 5xx. - **Client-side rate limiter** so you don't have to think about your plan's ceiling. - **Webhook-signature verification** (Batch endpoint) in one line. - **Typed response shapes** - autocomplete the ~55 product fields in your editor. - **Sensible defaults** - `Accept-Encoding: gzip`, `User-Agent` identifying the SDK + version, a 30s request timeout you can override per call. If you ever find the SDK doing something unexpected, pass `debug: true` at construction and you'll get every HTTP call printed to stderr with timings. ## Related - [Authentication](/docs/guides/authentication) - API key format and rotation - [Rate limits & concurrency](/docs/guides/rate-limits) - what the built-in limiter is protecting you from - [Error codes](/docs/guides/errors) - what the SDK's retry policy is reacting to --- # Country, marketplace, and content language _Source: https://amazonscraperapi.com/docs/guides/country-and-language_ # Country, marketplace, and content language Three distinct concepts - don't conflate them: | Concept | What you control | Example | |---|---|---| | **Marketplace (TLD)** | `domain` param | `com` โ†’ amazon.com, `de` โ†’ amazon.de | | **Content language** | `language` param | `en_US`, `de_DE`, `es_MX` | | **Proxy egress country** | automatic, not exposed | amazon.de โ†’ DE residential IP | ## Proxy egress is automatic You don't configure proxy egress. When you hit `?domain=de` we automatically route through a residential IP in Germany. When you hit `?domain=co.jp` we route through Japan. This matching reduces Amazon's anti-bot score and gives you the real desktop-locale HTML. There was briefly a `country` parameter that let customers force a specific egress IP. We removed it - obvious mismatches (e.g. scraping amazon.de through a US IP) trigger Amazon's anti-bot disproportionately and add complexity without customer value. ## Content language override Amazon serves some marketplaces in multiple languages. Pass `?language=xx_YY` to override: ```http GET /v1/amazon/product?query=B09HN3Q81F&domain=com&language=es_US ``` This maps to Amazon's `?language=es_US` URL parameter. Amazon then serves the Spanish US-marketplace content - prices in USD, shipping from US warehouses, but titles/descriptions/bullets translated to Spanish. ## Supported languages per marketplace (empirically tested) | Marketplace | Default | Other languages that work | |---|---|---| | `com` (US) | `en_US` | `de_DE`, `es_ES`, `es_US`, `zh_CN`, `pt_BR`, `ar_AE`, `en_GB` | | `co.uk` (UK) | `en_GB` | `de_DE`, `fr_FR`, `pl_PL`, `ro_RO`, `en_US` | | `de` (Germany) | `de_DE` | `en_GB`, `en_US`, `nl_NL`, `pl_PL`, `tr_TR`, `cs_CZ`, `fr_FR` | | `fr` (France) | `fr_FR` | `en_GB`, `de_DE`, `nl_NL`, `ar_AE` | | `it` (Italy) | `it_IT` | `en_GB`, `en_US`, `de_DE` | | `es` (Spain) | `es_ES` | `en_GB`, `en_US`, `pt_BR`, `pt_PT`, `ca_ES` | | `nl` (Netherlands) | `nl_NL` | `en_GB`, `de_DE` | | `pl` (Poland) | `pl_PL` | (monolingual) | | `se` (Sweden) | `sv_SE` | `en_GB` | | `ca` (Canada) | `en_CA` | `en_US`, `en_GB`, `fr_CA`, `fr_FR`, `zh_CN` | | `com.mx` (Mexico) | `es_MX` | `es_ES`, `en_US` | | `com.br` (Brazil) | `pt_BR` | `en_US` | | `com.au` (Australia) | `en_AU` | `en_US`, `en_GB`, `zh_CN` | | `co.jp` (Japan) | `ja_JP` | `en_US`, `en_GB`, `zh_CN`, `ko_KR` | | `sg` (Singapore) | `en_SG` | `en_US`, `en_GB`, `zh_CN` | | `in` (India) | `en_IN` | `en_US`, `hi_IN` | | `com.tr` (Turkey) | `tr_TR` | `en_US`, `en_GB` | | `ae` (UAE) | `en_AE` | `ar_AE`, `en_US` | | `sa` (Saudi Arabia) | `ar_SA` | `en_US`, `en_GB` | | `eg` (Egypt) | `ar_EG` | `en_US`, `en_GB` | If you pass a language that the marketplace doesn't support, Amazon silently falls back to the marketplace's default. No error from us. ## What doesn't change with `language` The `language` parameter only affects **presentation** (titles, descriptions, bullets, UI text). It doesn't affect: - **Price** - always shown in the marketplace's native currency - **Availability / stock** - physical availability is per-marketplace, not per-language - **Shipping** - ships from the marketplace's region - **Reviews** - Amazon may show reviews from multiple sources; language flag doesn't filter them If you need Spanish-language product data *from* the Mexican marketplace, use `domain=com.mx` (which defaults to `es_MX`). If you want Spanish-language data *from* the US marketplace (available to US-Spanish speakers), use `domain=com&language=es_US`. ## Related - [Product endpoint](/docs/endpoints/product) - [Search endpoint](/docs/endpoints/search) --- # Billing policy _Source: https://amazonscraperapi.com/docs/guides/billing_ # Billing policy **Only successful (HTTP 2xx) requests cost credits.** Everything else is free. ## The rule set | Outcome | Bill? | |---|---| | Auth failure (401) | โŒ Free | | Bad params (400) | โŒ Free | | Rate limit from us (429) | โŒ Free | | Roadmap param (501 `render_js`/`screenshot`) | โŒ Free | | Amazon timeout or network failure | โŒ Free | | Amazon 503 / challenge page detected | โŒ Free | | Amazon 302 to login or 404 | โŒ Free | | Amazon served something, extractor found nothing (502 `extraction_failed`) | โŒ Free | | ASIN mismatch - requested B1, got B2 (502 `asin_mismatch`) | โŒ Free | | Partial product (title + buybox present, some optional fields missing) | โœ… Billed | | Full product | โœ… Billed | ## Why so generous? Because you shouldn't pay for our failures. When Amazon blocks a request, we absorb the infrastructure cost and return a clean error to you. You get what you paid for or you pay nothing. Our current success rate is ~97 %, and the cost of the failed 3 % is a rounding error on our margins. The simpler rule - "only 2xx costs credits" - is worth more to us in customer trust than the pennies we'd save by billing borderline cases. ## Credit cost per request A basic Amazon product or search request costs **5 credits**. When JS-rendered calls ship, those will cost 15 credits each. The credit unit is just the internal accounting unit - customer-facing pricing is still **$0.90 per 1,000 basic requests** on PAYG, and cheaper on subscriptions. ## The one edge case worth flagging If a product returns valid fields but some optional ones are missing (e.g. `price: null` for a book with multiple format editions, `variations: []` for a single-variant product), you're billed. The response IS the product - nothing is missing because the extractor failed; it's missing because Amazon didn't publish it. A successful 2xx with real data costs 5 credits. ## Retries Our orchestrator handles retries internally - you send one request, we do the work of getting it through. **You're billed at most once per request**, only if the final result is a successful 2xx. The number of internal attempts used is surfaced in the `Asa-Attempts` response header for your own debugging. ## Batch billing Each item in a batch is billed independently. 800/1000 successes at 5 credits each = 4,000 credits charged. Failures in the batch are free. The failure reason is in each item's `error` field. ## Checking your usage Every request, successful or not, appears in your dashboard at `/app/usage`. Failed requests display `$0` for Cost and a clear reason code. Use them to debug, not worry. ## Versioning Any change to this billing policy is announced 14 days in advance via email and reflected in both this page and the [pricing FAQ](https://amazonscraperapi.com/pricing#faq) simultaneously. In-flight requests are settled under the old policy.