kestrel actors docs

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

EventWhat it isPriceCharged for
productProduct row$0.004One 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.

FieldTypeDefaultWhat it does
asinsarray[]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.
domainstring"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.
inStockOnlybooleanfalseKeep 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.
maxPricenumber00 = 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.
sessionsinteger4How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16.
perIpnumber0.5Pace for each session. Amazon tolerates about 0.5/s per IP; faster causes captchas, not speed. Range 0.1-2.
proxyConfigurationobject{"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.

FieldTypeWhat it holds
typestringRow type: product or status.
asinstringAmazon's product id.
domainstringMarketplace domain suffix, e.g. com, co.uk, de.
marketplacestringMarketplace country name.
urlstringCanonical product URL on that marketplace.
titlestringProduct title as printed.
brandstringBrand from the byline ('Visit the … Store' and 'Brand:' prefixes removed).
pricenumberBuybox 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_displaystringThe buybox price exactly as printed, e.g. '$54.99'.
currencystringCurrency 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_pricenumberThe struck-through list price when Amazon shows one.
list_price_displaystringList price exactly as printed.
availabilitystringThe availability line: 'In Stock', 'Only 3 left in stock', 'Currently unavailable.' and so on, in the marketplace's language.
in_stockbooleantrue/false from the availability wording, else true when a buybox price exists, else null (unknown).
primebooleanWhether the Prime badge is shown on the offer.
ratingnumberOverall star rating (1-5).
ratings_countintegerHow many ratings the product has in total.
imagestringMain image URL (highest resolution the page offers).
imagesarrayEvery gallery image URL, main image first.
featuresarrayThe bullet points under the title.
descriptionstringThe product description block as plain text (up to 2,000 characters), null when Amazon renders none.
breadcrumbsarrayCategory path from the breadcrumb bar, top level first. Null on pages without a breadcrumb bar (most amazon.com pages).
categorystringThe last breadcrumb, i.e. the product's own category.
detailsobjectThe 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.
bsrarrayBest 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_topintegerThe first Best Sellers Rank number (the top-level category), null when not shown.
date_first_availablestring'Date First Available' from the details table, as printed.
sold_bystringThe seller line from the buybox, e.g. 'Amazon.com' or 'Amazon Resale and Fulfilled by Amazon'.
variations_countintegerHow many variations (colours, sizes, models) the listing carries, null when the page has no variation picker.
5_star_pctintegerShare of ratings that are 5 stars, when the histogram is shown.
4_star_pctintegerShare of 4-star ratings.
3_star_pctintegerShare of 3-star ratings.
2_star_pctintegerShare of 2-star ratings.
1_star_pctintegerShare of 1-star ratings.
statusstringStatus rows: ok, filtered, not_found, blocked, limit or error.
targetstringStatus rows: the ASIN or URL this job was given.
productsintegerStatus rows: 1 when the product row was delivered, else 0.
filteredintegerStatus rows: 1 when inStockOnly or maxPrice dropped the product, never charged.
duplicatesintegerStatus rows: 1 when another input already covered this product.
errorstringStatus rows: why the product was not delivered, null when it was.
fetched_atstringUTC 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:

  • 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 blocked in 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_found after 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 proxyConfiguration and the run routes through the marketplace's own country, which is what the verified co.uk run used.
  • Language follows the marketplace. availability and details labels come back in the site's language; in_stock normalises the common wordings, details keys 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

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