# Amazon Product Scraper (`usestring/amazon-products`) Actor

Give it ASINs or Amazon product URLs and get one row per product: a numeric buy-box price, ISO currency, list price, star rating, review count, the availability line, brand and main image. Each of the seven marketplaces is fetched from an exit in its own country, so prices come back local.

- **URL**: https://apify.com/usestring/amazon-products.md
- **Developed by:** [String](https://apify.com/usestring) (community)
- **Categories:** E-commerce, Business
- **Features:** Limited permissions
- **Stats:** 4 total users, 1 monthly users, 62.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Amazon Product Scraper do?

You hand it ASINs or Amazon product URLs, it fetches one product detail page each, and it returns
one row per product: a **numeric** `price` with the ISO `currency` beside it, plus `listPrice`,
`rating`, `reviewCount`, the page's `availability` line, `brand`, the main image and the `asin` read
back off the page itself.

It is the lookup end of our three Amazon Actors, and the distinction matters when you are choosing
between them. This one needs you to already know the products — it does no discovery at all. If you
are starting from a keyword, run [Amazon Search Scraper](https://apify.com/usestring/amazon-search)
first. If you are starting from a category, run [Amazon Bestsellers
Scraper](https://apify.com/usestring/amazon-bestsellers). Both hand back ASINs, and those ASINs come
straight in here.

- Seven marketplaces: US, UK, Germany, France, Spain, Japan and Australia.
- A pasted URL names its own marketplace, so one run can mix storefronts freely.
- The price is read from the add-to-cart form's hidden inputs, where Amazon states the exact decimal
  amount, an ISO currency code and the formatted string together.
- Each marketplace is fetched from an exit inside its own country, which is what makes a local price
  exist at all.
- Up to 500 ASINs per run, fetched with configurable parallelism.

No Amazon account, API key or cookies are involved — this is the page a logged-out visitor sees.
One row per ASIN, at a median of about 2.4 seconds per page in our own measurements.

### What data does it extract?

| Field | Type | Description |
| --- | --- | --- |
| `asin` | string | Amazon's ten-character product ID, read back from the page's own hidden ASIN input |
| `title` | string | Product title, with Amazon's padding of newlines and non-breaking spaces collapsed |
| `brand` | string | From the byline. `Visit the Apple Store` becomes `Apple`, and a labelled `Marque : Blink` becomes `Blink` |
| `price` | number | The buy-box price as a number: `19.99`, never `"$19.99"` |
| `priceText` | string | The same price formatted the way the storefront printed it |
| `listPrice` | number | The struck-through list price, where the page shows one |
| `currency` | string | ISO code — `USD`, `GBP`, `EUR`, `JPY`, `AUD` — from the cart form, or inferred from the symbol when that form is absent |
| `rating` | number | Stars out of 5. A locale decimal comma is handled, so `4,6 sur 5` on amazon.fr is `4.6` |
| `reviewCount` | number | Rating count as an integer: `278974`, not `"278,974"` |
| `availability` | string | The page's own availability line, verbatim: `In Stock`, `Only 3 left in stock`, `Currently unavailable` |
| `imageUrl` | string | The main product image at the size the page served |
| `marketplace` | string | `us`, `uk`, `de`, `fr`, `es`, `jp` or `au` |
| `sourceUrl` | string | The `/dp/` URL that was fetched |
| `collectedAt` | string | ISO timestamp stamped once per run |

Any field the page does not state comes back `null` rather than as an empty string or a zero, so a
missing buy box is distinguishable from a free item.

#### How the page is read

An Amazon detail page carries no page-wide state blob, so each field is taken from the one element
that states it rather than from whichever rendered string happens to be nearby.

The add-to-cart form is the strongest source: its hidden inputs carry the amount as a machine
decimal, the currency as an ISO code and the display string, all three agreeing by construction.
When that form is absent the embedded buying-options blob stands in, and the rendered price string
is the last resort. Being a display string, that last one has to be read back through the
marketplace's own digit grouping — `1.234,56` on amazon.de and `1,234.56` on amazon.com are the same
amount, and a yen price of `1,980` has no decimal part at all.

### Geography, currency and why the exit country is pinned

Each marketplace is fetched from an exit in its own country. This is not a cosmetic choice of
currency symbol; it decides whether there is a price on the page to read:

- Fetched from a US exit, amazon.fr, amazon.co.jp and amazon.com.au answer with an offer-less
  page — the buy box is simply not there, so `price`, `priceText` and `currency` would all be null.
- amazon.co.uk and amazon.de do answer, but quote a **converted USD price**, which silently poisons
  any price comparison you build on top of it.

Pinning the exit removes both failure modes: `price` is the local buy-box price and `currency` is
the local ISO code. Titles follow the storefront, so amazon.de returns German titles and amazon.co.jp
Japanese ones, alongside a `brand` read from that page's own byline wording.

One consequence worth knowing: the same ASIN in seven marketplaces gives you seven rows with seven
currencies, and you convert them yourself. Nothing here silently normalizes to USD.

### Why scrape Amazon product pages?

- **Repricing.** Numeric `price` and `listPrice` on a schedule is the raw input to any repricing
  rule, and `availability` tells you when a competitor has simply run out.
- **Cross-border price gaps.** The same ASIN across US, UK, DE, FR, ES, JP and AU, each in its own
  currency, exposes arbitrage and MAP breaches that a single-marketplace view cannot show.
- **Feed enrichment.** A catalogue of ASINs comes back with brand, rating, review count and an image
  URL to hang on your own product records.
- **Stock and availability monitoring.** The verbatim availability line distinguishes "In Stock"
  from "Only 3 left" from "Currently unavailable", which a boolean would flatten.
- **Review-volume benchmarking.** `reviewCount` and `rating` together are the cheapest proxy for how
  much traction a competing product actually has.

### How to use it

1. Open the Actor and click **Try for free**.
2. Paste your ASINs into **ASINs or URLs**. Full product URLs are fine in the same list, and `/dp/`,
   `/gp/product/`, `/gp/aw/d/` and `?asin=` forms are all understood.
3. Choose a **Marketplace**. It applies to the bare ASINs; a pasted URL overrides it with its own
   domain.
4. Adjust **Maximum results** and **Concurrency** if you are running a large batch.
5. Start the run, then read the dataset — export as JSON, CSV or Excel, or pull it through the API.
   The `SUMMARY` record in the key-value store lists anything that failed and why.

### Input

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `asins` | array of strings | required | Bare ASINs or full Amazon product URLs, 1 to 500 per run |
| `marketplace` | string | `us` | One of `us`, `uk`, `de`, `fr`, `es`, `jp`, `au`. Applies to bare ASINs only |
| `maxItems` | integer | `1000` | Ceiling on dataset rows, 1 to 50,000 |
| `concurrency` | integer | `5` | Products fetched in parallel, 1 to 10 |

```json
{
  "asins": [
    "B0CHX3QBCH",
    "B08N5WRWNW",
    "https://www.amazon.de/dp/B0BDHWDR12"
  ],
  "marketplace": "us",
  "maxItems": 1000,
  "concurrency": 5
}
```

An entry that is neither an ASIN nor a URL naming one is rejected before it costs a fetch, and shows
up under `failures` with the value you passed. A bare ASIN and the full URL for the same product are
two different entries, so list a product one way or the other rather than both.

### Output

One row per product. This is the shape rather than a captured run:

```json
[
  {
    "asin": "B0CHX3QBCH",
    "title": "Apple AirPods Pro 2 Wireless Earbuds, USB-C Charging Case",
    "brand": "Apple",
    "price": 189.99,
    "priceText": "$189.99",
    "listPrice": 249,
    "currency": "USD",
    "rating": 4.7,
    "reviewCount": 108442,
    "availability": "In Stock",
    "imageUrl": "https://m.media-amazon.com/images/I/61SUj2aKoEL._AC_SL1500_.jpg",
    "marketplace": "us",
    "sourceUrl": "https://www.amazon.com/dp/B0CHX3QBCH",
    "collectedAt": "2026-08-20T09:14:02.881Z"
  },
  {
    "asin": "B0BDHWDR12",
    "title": "SanDisk Ultra microSDXC UHS-I Speicherkarte 128 GB",
    "brand": "SanDisk",
    "price": 12.99,
    "priceText": "12,99 €",
    "listPrice": null,
    "currency": "EUR",
    "rating": 4.7,
    "reviewCount": 41230,
    "availability": "Auf Lager",
    "imageUrl": "https://m.media-amazon.com/images/I/71p-M3sBwkL._AC_SL1500_.jpg",
    "marketplace": "de",
    "sourceUrl": "https://www.amazon.de/dp/B0BDHWDR12",
    "collectedAt": "2026-08-20T09:14:02.881Z"
  }
]
```

### How much does it cost?

Pricing is pay-per-event: you are charged per result row written to the dataset. A row is only
written when it can be charged, so hitting your account spend limit stops the writing rather than
handing you unbilled data. The current per-row figure lives on the Actor's Store page, which is the
one place it cannot go stale.

Runs started from an Apify **free plan** stop at **250 requests and 250 results**, and the run says
so in its status message. Any paid plan runs the full input and the `maxItems` you set.

The limit is there because this Actor fetches through our own scraping infrastructure, which Apify
does not cover for free-plan runs. It binds on requests as well as rows, so a 500-ASIN list on a
free plan cannot spend 500 fetches to return 250 rows.

### Using it with the Apify API

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('usestring/amazon-products').call({
  asins: ['B0CHX3QBCH', 'https://www.amazon.co.uk/dp/B08N5WRWNW'],
  marketplace: 'us',
  maxItems: 200
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) {
  console.log(item.asin, item.marketplace, item.price, item.currency);
}
```

The Python client and the plain REST API work the same way, and a schedule will run the batch for
you on whatever cadence your pricing rules need.

### Limitations

Detail pages only, and only for products you name. There is no review text, no full offer or seller
list, no variant matrix, no Q\&A and no bestseller rank. Discovery is somebody else's job: use the
search Actor for keywords and the bestsellers Actor for category rankings. Marketplaces beyond the
seven listed above, amazon.in included, are not supported. Every value reflects what Amazon showed
at collection time, stamped in `collectedAt`.

### Related actors

- [Amazon Search Scraper](https://apify.com/usestring/amazon-search) — turns a keyword into ranked
  ASINs, which are exactly what this Actor takes as input.
- [Amazon Bestsellers Scraper](https://apify.com/usestring/amazon-bestsellers) — category rankings,
  the other way of discovering ASINs worth looking up here.
- [Walmart Product Scraper](https://apify.com/usestring/walmart-products) — the same job on
  Walmart's catalogue, with the marketplace seller behind the buy box.
- [Home Depot Product Scraper](https://apify.com/usestring/homedepot-products) — price and stock for
  home-improvement SKUs, resolvable to a single store.
- [Etsy Listings Scraper](https://apify.com/usestring/etsy-listings) — the handmade and vintage side
  of the same price research.

### FAQ

**Can I mix ASINs and URLs in one input?**
Yes, in the same list. A URL is read on the marketplace its domain names, so
`https://www.amazon.de/dp/B0BDHWDR12` is fetched on amazon.de whatever `marketplace` is set to. Bare
ASINs use the `marketplace` setting.

**Is the price returned as a number?**
Yes. `price` is numeric, `priceText` keeps the formatted original, and `currency` carries the ISO
code. `listPrice`, `rating` and `reviewCount` are numbers too.

**Which price does it return — buy box, third party, or used?**
The buy box, which is the offer Amazon presents by default. The full offer list behind "Other
sellers on Amazon" is not collected.

**How many rows does one ASIN produce?**
One. One page fetched, one row emitted, so your dataset row count is predictable before you start.

**What happens if an ASIN does not exist on the chosen marketplace?**
That target is recorded under `failures` in the run's `SUMMARY` record rather than emitted as an
empty row, and the rest of the batch still returns. If every target fails, the run itself fails
rather than reporting a successful empty dataset.

**Can I get the same product across several marketplaces in one run?**
Yes — pass the ASIN once per marketplace as a full URL, or run the batch once per marketplace. Each
row carries its own `marketplace` and `currency`, so the comparison is straightforward afterwards.

**Do I need to configure proxies?**
No. Fetching, geography and anti-bot handling are all on our side; the marketplace choice is the
only geographic control you have, and it is enough.

### Feedback

Missing a field, or seeing a page parsed wrong? Open an issue from the Actor's Store page and
include the ASIN and the marketplace — that pair is usually enough to reproduce it in one run.

# Actor input Schema

## `asins` (type: `array`):

Amazon ASINs or full product URLs.

## `marketplace` (type: `string`):

Amazon marketplace to read.

## `maxItems` (type: `integer`):

Global cap on dataset items. Runs started from an Apify free plan stop at 250 requests and 250 results; any paid plan runs the full amount.

## `concurrency` (type: `integer`):

Targets fetched in parallel.

## Actor input object example

```json
{
  "asins": [
    "B0CHX3QBCH"
  ],
  "marketplace": "us",
  "maxItems": 1000,
  "concurrency": 5
}
```

# Actor output Schema

## `results` (type: `string`):

Give it ASINs or Amazon product URLs and get one row per product: a numeric buy-box price, ISO currency, list price, star rating, review count, the availability line, brand and main image. Each of the seven marketplaces is fetched from an exit in its own country, so prices come back local.

## `summary` (type: `string`):

Item count, failure count and every target that failed, with its error.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "asins": [
        "B0CHX3QBCH"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("usestring/amazon-products").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "asins": ["B0CHX3QBCH"] }

# Run the Actor and wait for it to finish
run = client.actor("usestring/amazon-products").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "asins": [
    "B0CHX3QBCH"
  ]
}' |
apify call usestring/amazon-products --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,usestring/amazon-products"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ig7mA5RuOwemsxcEW/builds/CvRsi1abwbvrZ0N4d/openapi.json
