kestrel actors › Ecommerce
Amazon Product Scraper — Details, Prices & Availability by ASIN
An amazon product scraper that turns any ASIN or product URL into one clean row of amazon product data: title, brand, the buybox price and the struck-through list price, currency, the availability line and a stock flag, rating and rating count, the main image and the whole gallery, bullet points, description, category, the product-details tables, seller and Best Sellers Rank when the page shows it. It is an amazon price scraper, an amazon asin lookup and an amazon stock checker in one call, across 20 marketplaces — the amazon scraper the rest of the kestrel suite feeds on.
Run Amazon Product Scraper on Apify →
Amazon product scraper pricing: what one row costs
| Event | What it is | Price | Charged for |
|---|---|---|---|
product | Product row | $0.004 | One product's details from its public page: title, brand, buybox price and list price, currency, availability, stock flag, rating, rating count, images, bullet points, description, category, details table, seller and Best Sellers Rank when shown. Products dropped by inStockOnly/maxPrice, unknown ASINs and blocked pages are free. |
$0.004 per product row. Status rows, filtered products, duplicates, unknown ASINs and blocked pages are free. A daily check over 500 ASINs is $2 a day; the same check with inStockOnly on a catalogue where half the listings are dead is $1. Platform usage (compute and proxy) is billed separately at Apify's standard rates and is small — a product page is one request.
Amazon product scraper input reference
Every field of Amazon Product Scraper, straight from the actor's input schema.
| Field | Type | Default | What it does |
|---|---|---|---|
asins | array | [] | Amazon ASINs (B0CHWRXH8B) or product URLs from any marketplace (https://www.amazon.de/dp/B09B8V1LZ3). A URL's marketplace is used automatically; bare ASINs use the marketplace below. Each ASIN is fetched and billed once per run. |
domain | string | "com" | Amazon marketplace for bare ASINs: com, co.uk, de, fr, it, es, ca, com.au, co.jp, in, com.mx, com.br, nl, se, pl, sa, ae, sg, com.tr, com.be. |
inStockOnly | boolean | false | Keep only products whose page says they are in stock. Unavailable and unknown-stock products are dropped before billing, so a stock check across a catalogue costs nothing for the dead listings. |
maxPrice | number | 0 | 0 = keep every product. N = keep only products whose buybox price is at or below N in the marketplace's currency; products without a visible price are dropped too. Filtered products are never charged. Range 0-10000000. |
sessions | integer | 4 | How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16. |
perIp | number | 0.5 | Pace for each session. Amazon tolerates about 0.5/s per IP; faster causes captchas, not speed. Range 0.1-2. |
proxyConfiguration | object | {"useApifyProxy": true} | Apify Proxy is required: Amazon captchas datacentre traffic that is not rotated; the run detects a captcha and rotates IP. Datacentre proxies are enough for amazon.com. For other marketplaces choose RESIDENTIAL — it is routed through the marketplace's own country automatically, which is what makes the buybox price and stock line appear. |
Example input
{
"asins": ["B0CHWRXH8B", "B09B8V1LZ3", "B0BSHF7WHW"],
"domain": "com"
}
{
"asins": ["B0CHWRXH8B", "B09B8V1LZ3", "B0BSHF7WHW"],
"inStockOnly": true,
"maxPrice": 100
}
Amazon product scraper output fields
Two row types share one dataset: product (one product's details from its public page, charged) and status (per ASIN or URL given, free).
Dataset views in the Apify Console: Overview, Products, Prices, Content, Run status.
| Field | Type | What it holds |
|---|---|---|
type | string | Row type: product or status. |
asin | string | Amazon's product id. |
domain | string | Marketplace domain suffix, e.g. com, co.uk, de. |
marketplace | string | Marketplace country name. |
url | string | Canonical product URL on that marketplace. |
title | string | Product title as printed. |
brand | string | Brand from the byline ('Visit the … Store' and 'Brand:' prefixes removed). |
price | number | Buybox price as a number. Null when the page shows no buying option (unavailable products, or a marketplace that hides the offer from the caller's location). |
price_display | string | The buybox price exactly as printed, e.g. '$54.99'. |
currency | string | Currency read from the printed price, falling back to the marketplace's currency. Amazon prices in the caller's location's currency, so this can differ from the marketplace. |
list_price | number | The struck-through list price when Amazon shows one. |
list_price_display | string | List price exactly as printed. |
availability | string | The availability line: 'In Stock', 'Only 3 left in stock', 'Currently unavailable.' and so on, in the marketplace's language. |
in_stock | boolean | true/false from the availability wording, else true when a buybox price exists, else null (unknown). |
prime | boolean | Whether the Prime badge is shown on the offer. |
rating | number | Overall star rating (1-5). |
ratings_count | integer | How many ratings the product has in total. |
image | string | Main image URL (highest resolution the page offers). |
images | array | Every gallery image URL, main image first. |
features | array | The bullet points under the title. |
description | string | The product description block as plain text (up to 2,000 characters), null when Amazon renders none. |
breadcrumbs | array | Category path from the breadcrumb bar, top level first. Null on pages without a breadcrumb bar (most amazon.com pages). |
category | string | The last breadcrumb, i.e. the product's own category. |
details | object | The Product information / Technical details / Product overview tables as {label: value}: dimensions, weight, manufacturer, model number, colour, date first available and whatever else the page lists. |
bsr | array | Best Sellers Rank entries [{rank, category}] when the page renders them. Amazon serves the rank to some sessions only, so this is null on many pages. |
bsr_top | integer | The first Best Sellers Rank number (the top-level category), null when not shown. |
date_first_available | string | 'Date First Available' from the details table, as printed. |
sold_by | string | The seller line from the buybox, e.g. 'Amazon.com' or 'Amazon Resale and Fulfilled by Amazon'. |
variations_count | integer | How many variations (colours, sizes, models) the listing carries, null when the page has no variation picker. |
5_star_pct | integer | Share of ratings that are 5 stars, when the histogram is shown. |
4_star_pct | integer | Share of 4-star ratings. |
3_star_pct | integer | Share of 3-star ratings. |
2_star_pct | integer | Share of 2-star ratings. |
1_star_pct | integer | Share of 1-star ratings. |
status | string | Status rows: ok, filtered, not_found, blocked, limit or error. |
target | string | Status rows: the ASIN or URL this job was given. |
products | integer | Status rows: 1 when the product row was delivered, else 0. |
filtered | integer | Status rows: 1 when inStockOnly or maxPrice dropped the product, never charged. |
duplicates | integer | Status rows: 1 when another input already covered this product. |
error | string | Status rows: why the product was not delivered, null when it was. |
fetched_at | string | UTC timestamp when the row was fetched. |
Example row from a real run
Taken from the actor's README — this is the shape your dataset holds.
{
"type": "product",
"asin": "B09B8V1LZ3",
"domain": "com",
"marketplace": "US",
"url": "https://www.amazon.com/dp/B09B8V1LZ3?th=1&psc=1",
"title": "Amazon Echo Dot (newest model), Vibrant sounding Alexa speaker, Great for bedrooms, dining rooms and offices, Charcoal",
"brand": "Amazon",
"price": 54.99,
"price_display": "$54.99",
"currency": "USD",
"list_price": 79.99,
"list_price_display": "$79.99",
"availability": "In Stock",
"in_stock": true,
"prime": false,
"rating": 4.7,
"ratings_count": 198808,
"image": "https://m.media-amazon.com/images/I/61J2sQtBYDL._AC_SL1000_.jpg",
"images": ["https://m.media-amazon.com/images/I/61J2sQtBYDL._AC_SL1000_.jpg", "https://m.media-amazon.com/images/I/71xoR4A6q-L._AC_SL1500_.jpg"],
"features": ["Our most popular smart speaker with a fabric design. It is our most compact smart speaker that fits perfectly into small spaces."],
"description": null,
"breadcrumbs": null,
"category": null,
"details": null,
"bsr": null,
"bsr_top": null,
"date_first_available": null,
"sold_by": null,
"variations_count": 3,
"5_star_pct": 82,
"4_star_pct": 11,
"3_star_pct": 4,
"2_star_pct": 1,
"1_star_pct": 2,
"fetched_at": "2026-08-29T10:31:04+00:00"
}
{ "type": "status", "target": "B0BSHF7WHW", "asin": "B0BSHF7WHW", "domain": "com", "status": "filtered", "products": 0, "filtered": 1, "duplicates": 0, "error": null, "fetched_at": "2026-08-29T10:31:05+00:00" }
Amazon product scraper in Python, JavaScript and curl
Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/amazon-product-scraper").call(run_input={
"asins": ["B0CHWRXH8B", "B09B8V1LZ3"],
"domain": "com",
"inStockOnly": True,
})
rows = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items() if r["type"] == "product"]
for r in rows:
print(r["asin"], r["price_display"], r["availability"], r["rating"], r["ratings_count"])
JavaScript (Node)
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/amazon-product-scraper').call({
asins: ['B09B8V1LZ3', 'https://www.amazon.co.uk/dp/B09B8V1LZ3'],
proxyConfiguration: { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'] },
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.table(items.filter(r => r.type === 'product').map(r => ({ asin: r.asin, marketplace: r.marketplace, price: r.price_display, inStock: r.in_stock })));
curl
curl -X POST "https://api.apify.com/v2/acts/kestrel~amazon-product-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"asins": ["B0CHWRXH8B"], "domain": "com"}'
The same three calls are runnable files in the examples repository: examples/amazon-product-scraper.py|.js|.sh.
Limits and honest notes
Scope — read this before you buy
This reads the public product page as an anonymous visitor sees it. That is exactly the amazon product information a shopper gets, and no more:
- Price is the buybox offer. Pages with no buying option (discontinued products, or an offer the marketplace hides from the caller's location) have a null price, and the row is still delivered with everything else filled in — use
inStockOnlyormaxPriceif you do not want to pay for those. - Best Sellers Rank is rendered for some sessions and not others. When it is not on the page,
bsris null. If you need ranks for a whole category, the Amazon Best Sellers Scraper reads the chart itself and fills every rank. - Reviews are not here. The product row carries
rating,ratings_countand the star histogram; the review text belongs to the Amazon Reviews Scraper. - Variations are counted, not expanded. Give each variation's ASIN if you need each one priced.
- One request per product. Ten ASINs is ten page fetches; a thousand is a thousand, paced at about one every two seconds per session, so four sessions do a thousand products in roughly eight minutes.
- Captchas happen. The site challenges repeat visitors from the same IP. The client detects the captcha page, rotates to a fresh IP and retries up to eight times; a product that is still blocked after that is reported
blockedin its status row and never billed. - Cheap proxies first, residential only when the site pushes back. A run starts on the proxies you chose — datacentre is the cheapest — and rotates to a fresh IP on a captcha or a 503. When the same page is refused twice in a row that is the IP range being blocked, not the page, so the run stops trusting that pool: its sessions re-open on a residential proxy and stay there for the rest of the run. Nothing to configure, no run has to fail to teach it, and a run that is never refused never leaves the cheap pool — so a run costs the least that still succeeds. The run's SUMMARY reports it as
http.escalated. Residential sessions keep the marketplace routing, so the buybox stays the one a local shopper sees. - A 404 is an answer. An unknown ASIN is reported
not_foundafter one attempt, not after five rotations. - The buybox can hide. For marketplaces outside the US, a datacentre egress sometimes gets a page with no offer. Choose RESIDENTIAL in
proxyConfigurationand the run routes through the marketplace's own country, which is what the verifiedco.ukrun used. - Language follows the marketplace.
availabilityanddetailslabels come back in the site's language;in_stocknormalises the common wordings,detailskeys do not.
FAQ
Does it need an Amazon API key or login?
No. There is no key and no account. The Product Advertising API needs an approved associate account and rate-limits by sales; this reads the page instead.
Can I look up an ASIN and get the price only?
Yes. Every row has price, price_display, list_price and currency; the Prices view shows nothing else. That is the amazon asin lookup most people want.
How do I track prices over time?
Schedule the run (daily is typical), keep the rows, and group by asin. fetched_at timestamps every row; price versus list_price separates real cuts from list-price theatre.
Does it work for marketplaces other than amazon.com?
Yes — 20 of them, by URL or by domain. Use RESIDENTIAL proxies for marketplaces outside the US so the buybox renders; the run picks the right country for you.
What happens with a product that is out of stock?
The row is delivered with availability (e.g. Currently unavailable.), in_stock: false and a null price — unless inStockOnly is on, in which case it is dropped free of charge.
Can I get the product images?
Yes: image is the main image at the highest resolution the page offers, images is the full gallery.
Why is Best Sellers Rank null?
Because the page did not render it for that session. It is never invented. The Best Sellers Scraper is the reliable source for ranks.
Can I feed it from a keyword search or a chart?
That is the intended flow: the Amazon Search Scraper and the Best Sellers Scraper both emit asin columns; paste them into asins here for full details, then into the Reviews Scraper for the review text.
Is there a free tier?
Apify's free plan includes monthly platform credit; the per-row price applies on top. Filtered rows, duplicates and unknown ASINs never cost anything.
How to use Amazon Product Scraper
Related scrapers
Run Amazon Product Scraper
The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/amazon-product-scraper