# Amazon Offers (`s-r/amazon-offers`) Actor

Give it EAN/GTIN codes or ASINs and get the Amazon marketplace offers for each: seller name, price, shipping, total price, condition and availability, plus offer count and currency. One row per identifier, across 20 marketplaces from Amazon.nl to Amazon.co.jp.

- **URL**: https://apify.com/s-r/amazon-offers.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Business
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 amazon-offers

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

## Amazon Offers Scraper - All Sellers by EAN or ASIN

Give this Actor a list of EAN/GTIN codes or ASINs and it returns the Amazon
marketplace offers for each one: seller name, price, shipping, total price,
condition and availability. Twenty marketplaces, from Amazon.nl to Amazon.co.jp.

It reads a cache that ShoppingScraper keeps warm, rather than hitting Amazon
live on every call. That has one consequence worth understanding before you run
it, and it is the difference between this Actor and most of its neighbours.

### The first lookup is free

Every identifier comes back in one of three states:

| status | what it means | charged |
|---|---|---|
| `cached` | offers are ready | yes |
| `scraped_no_offers` | the scrape ran and Amazon genuinely had no offers there | yes |
| `queued_for_scrape` | nothing known yet, a scrape has just been queued | **no** |

An identifier nobody has asked for before is scraped on demand, which takes
about five minutes. The run waits for that (up to `wait_for_scrape_seconds`,
ten minutes by default) and returns the offers in the same run, so a cold
cache normally costs you a few minutes, not a second run. A row only stays
`queued_for_scrape` when the scrape had not landed by the time the wait ran
out.

A `queued_for_scrape` row costs you nothing, and that is enforced by where it
is written rather than by a promise. Charged rows go to the **default dataset**,
which is what Apify bills per item. Queued rows never touch it: they are listed
in a `QUEUED_IDENTIFIERS` record in the key-value store, together with the list
to retry, and the run summary says how many are still open. Run the Actor again
for those identifiers a few minutes later.

`scraped_no_offers` is charged on purpose: "nobody sells this on Amazon.nl" is a
real answer about the market, and it took a real scrape to establish.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `identifiers` | array | **required** | EANs, ASINs, or a mix. An EAN is resolved to an ASIN upstream; a 10-character ASIN is scraped directly. |
| `country` | string | `nl` | Marketplace. Note the endpoint's codes: the UK is `uk`, not `gb`. |
| `max_age_hours` | integer | `24` | How old a cached result may be and still count as a hit. |
| `wait_for_scrape_seconds` | integer | `600` | How long the run polls for identifiers that had to be scraped first. `0` returns immediately. |
| `force` | boolean | `false` | Demand data newer than the TTL and trigger an immediate re-scrape. |
| `ttl_hours` | integer | `6` | Only used with `force`. |
| `concurrency` | integer | `10` | Identifiers looked up at once. |

**Markets:** nl, de, fr, it, es, be, uk, us, ca, mx, br, jp, au, in, sg, ae, tr,
sa, se, pl.

### Output

One row per identifier, so the output joins straight back onto your input list
through `requested_identifier`, even for rows that are still queued.

```json
{
  "channel": "amazon",
  "ean": "5702017814674",
  "gl": "nl",
  "requested_identifier": "5702017814674",
  "found": true,
  "status": "cached",
  "billed": true,
  "offers_count": 49,
  "currency": "EUR",
  "offers": [
    {
      "sellerName": "Amazon RetourDeals",
      "price": "33.02",
      "shippingPrice": "0.00",
      "totalPrice": "33.02",
      "condition": "used",
      "availability": "InStock"
    }
  ]
}
```

### What this does not do

It does not return a product description, a rating or a review count. The
offers endpoint carries seller and price data, and this Actor passes through
what is there rather than inventing the rest.

`already_queued` on a queued row is global: it is true when anyone at all has a
scrape running for that combination, not just you. It is reported as
information, never as a billing signal.

### FAQ

**Why did my run return no offers?**
Either the identifier had not been scraped on Amazon in that country yet and
the scrape did not land inside the wait (the row says `queued_for_scrape`, it
was free, run again in a few minutes), or the lookup itself failed. A failed
lookup is never dressed up as "no offers": it is listed in the `errors` record,
and when every lookup fails the run is marked failed with the reason.

**Can I mix EANs and ASINs?**
Yes, in the same list. An ASIN row comes back with the ASIN in both the `asin`
and the `ean` field, which is how the underlying endpoint caches it.

**Why is the UK `uk` and not `gb`?**
Because that is the code the marketplace endpoint accepts. `gb` returns an
error listing the supported markets.

# Actor input Schema

## `identifiers` (type: `array`):

One or more identifiers. An EAN or GTIN is resolved to an ASIN upstream; a 10-character ASIN is scraped directly. You can mix both in one run.

## `country` (type: `string`):

Which marketplace to read. Note the endpoint's own codes: the United Kingdom is 'uk', not 'gb'.

## `max_age_hours` (type: `integer`):

How old a cached result may be and still count as a hit. Older than this and a fresh scrape is queued.

## `wait_for_scrape_seconds` (type: `integer`):

An identifier nobody has asked for before is scraped on demand, which takes about five minutes. The run polls for that long before giving up on it. Set 0 to return immediately and collect queued identifiers on a later run.

## `force` (type: `boolean`):

Demand data no older than the TTL below. Fires an immediate re-scrape when the cache is staler, and returns the stale row flagged as refreshing so you can poll for the fresh copy.

## `ttl_hours` (type: `integer`):

Only used with Force fresh data. Ignored otherwise.

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

How many identifiers to look up at once.

## Actor input object example

```json
{
  "identifiers": [
    "5702017814674",
    "B0DHSFBRPB"
  ],
  "country": "nl",
  "max_age_hours": 24,
  "wait_for_scrape_seconds": 600,
  "force": false,
  "ttl_hours": 6,
  "concurrency": 10
}
```

# Actor output Schema

## `offers` (type: `string`):

One row per identifier, with its offers.

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

Counts of billable versus free queued rows.

## `errors` (type: `string`):

Failures with a code and a redacted message.

# 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 = {
    "identifiers": [
        "5702017814674",
        "B0DHSFBRPB"
    ],
    "country": "nl",
    "max_age_hours": 24,
    "wait_for_scrape_seconds": 600,
    "ttl_hours": 6,
    "concurrency": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/amazon-offers").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 = {
    "identifiers": [
        "5702017814674",
        "B0DHSFBRPB",
    ],
    "country": "nl",
    "max_age_hours": 24,
    "wait_for_scrape_seconds": 600,
    "ttl_hours": 6,
    "concurrency": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/amazon-offers").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 '{
  "identifiers": [
    "5702017814674",
    "B0DHSFBRPB"
  ],
  "country": "nl",
  "max_age_hours": 24,
  "wait_for_scrape_seconds": 600,
  "ttl_hours": 6,
  "concurrency": 10
}' |
apify call s-r/amazon-offers --silent --output-dataset

```

## MCP server setup

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

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/8EH4vxaPx0whSLWLg/builds/K0dTZZ6aEPrpVxZzu/openapi.json
