# Amazon Offer Checker: How Contested Is This Listing (`amazonscrapers/amazon-offer-checker`) Actor

See how crowded an Amazon listing is: how many other sellers are on it, whether a used offer exists, whether a subscription price sits alongside, and how fast it ships. Never guesses a price. 19 marketplaces. It does not name sellers, by design.

- **URL**: https://apify.com/amazonscrapers/amazon-offer-checker.md
- **Developed by:** [Amazon Scrapers](https://apify.com/amazonscrapers) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 products with a prices

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Amazon offer checker: seller count, used offers, Prime and return terms

Give it a list of products. Get back how crowded each listing is: how many other
sellers are on it, whether a used copy is offered, whether a subscription price
sits alongside, how fast it ships and on what return terms.

Twenty fields per product, nineteen marketplaces, and a price that comes from
Amazon's own buy box data rather than from anywhere else on the page, which on a
competition question matters more than it does anywhere else.

### What this does not do, and why the name says so

It does not tell you who holds the buy box.

Answering that needs the seller's identity, and this Actor does not collect
seller names. A seller name is frequently a person's name, and scraping personal
data out of a public website is almost always a GDPR problem in Europe regardless
of how public the page is. The Dutch data protection authority calls it "almost
always" a violation and says a purely commercial interest does not count as a
legitimate one. That line is the same across every Actor in this family.

There are tools on this Store called buy box checkers that cannot name the buy
box winner either. Calling this one an offer checker is the honest version of the
same product.

What you do get is the shape of the competition, which is most of what the
question is usually for.

### The seller count, and how nearly it went missing

`otherSellersCount` is the headline field here, and for a while it was empty on
every single product.

Not because Amazon stopped printing it. Because Amazon changed where the number
sits. The old wording was "6 new from $12.00", with the count before the word.
The current wording is "New & Used (6) from", with the count in brackets after
it. This Actor was looking only for the old shape, so it returned nothing on
forty-three consecutive rows while the number sat there in plain sight.

Found and fixed on 26 August 2026 by probing the page for what it actually said
instead of assuming. Measured immediately after on eight American products: seven
of eight carried a count.

It is worth telling because it is the failure mode of every scraper on this
Store, and because it explains why this Actor now ships a field probe that
reports whether the marker a field depends on is present on the page. A field
that quietly goes to zero is worse than one that fails loudly.

### Every field it returns

Twenty per product, weighted towards the offer rather than the product.

#### Price and offer

| Field | What it holds |
| --- | --- |
| `price` | Current price from Amazon's own buy box data **(this is what you are charged for)** |
| `listPrice` | The struck-through price, when Amazon shows one |
| `discountPercent` | How much is off as a percentage |
| `currency` | Three-letter code, from the page itself |
| `priceSource` | Where the price came from, so you can audit it |
| `hasSubscribeAndSave` | Whether a subscription price exists |
| `hasUsedOffer` | Whether a used copy is offered |
| `isRefurbished` | Renewed and refurbished listings |
| `otherSellersCount` | How many other sellers offer it |

#### Stock and delivery

| Field | What it holds |
| --- | --- |
| `inStock` | True, false, or null when Amazon does not say |
| `availability` | Amazon's own availability sentence |
| `deliveryInfo` | The delivery date Amazon promises |
| `returnPolicy` | The return terms in full |
| `isPrime` | Prime delivery on this offer |

#### Identity and catalogue

| Field | What it holds |
| --- | --- |
| `asin` | The product id |
| `title` | Full product title, shortened only past 1000 characters |
| `brand` | Brand name |
| `marketplace` | Which shop this row came from |
| `url` | The page this was read from |
| `scrapedAt` | Timestamp of the read |

### A real row

```
asin                 B0DXXYS4BJ
title                Roku Streaming Stick HD with Voice Remote
brand                Roku
price                29.99
currency             USD
priceSource          buybox-data
listPrice            39.99
discountPercent      25
otherSellersCount    2
hasUsedOffer         false
hasSubscribeAndSave  false
isRefurbished        false
isPrime              true
deliveryInfo         Saturday, August 29
returnPolicy         Eligible for Return, Refund or Replacement within 30 days
inStock              true
availability         In Stock
scrapedAt            2026-08-26T09:14:22.031Z
```

Two other sellers on that listing, no used copy, twenty-five percent off, and the
price came from Amazon's own buy box data rather than from anywhere else on the
page.

### Why priceSource matters more here

On a competition question a wrong price is worse than no price, because you would
reprice against a number nobody is charging.

Amazon puts up to nine price-shaped numbers on a product page: the subscribe
option, a used offer, a struck-through list price, a per-unit price, a bundle,
and a Prime upsell block that shows two different amounts at once. A price
scraped out of the markup could be any of them.

Measured on this engine: markup-derived prices were correct zero times out of six,
buy box data nine out of nine. One concrete case: a product selling for $16.99
came back as $11.70 from the markup, because the cheapest visible number belonged
to a used offer three blocks down. On this Actor that would be doubly wrong,
since `hasUsedOffer` is a separate field precisely so you can see the used offer
without it contaminating the price.

So buy box data is the only source, and `priceSource` records it on every row.

**You are not charged for a row without a price.**

### When you would use it

Before listing against a product, to see how many sellers you would be joining.

While repricing, to check whether a listing has quietly gained competitors. A
listing that went from two sellers to nine in a month is a different proposition
from the same listing at the same price.

When a price looks wrong, to see whether a used offer or a subscription price is
what you are actually looking at.

Before promising a delivery date, since `isPrime` and `deliveryInfo` are the
competitive position on anything commoditised.

### What to put in

**ASINs or product URLs.** Paste them in any shape: a bare `B08N5WRWNW`, a full
URL with tracking parameters, a spreadsheet cell that still says
`ASIN: B08N5WRWNW`. Duplicates are removed across everything you give it.

**A search term or a category link**, if you want to survey competition across a
category rather than a known list. That is the more interesting use: the spread
of `otherSellersCount` across a category tells you where the crowd is.

### Settings worth knowing about

| Setting | What it does |
| --- | --- |
| `marketplace` | One of nineteen. The offer block depends on the buy box, so read the marketplace table below before committing. |
| `proxyCountry` | Leave empty and it follows the marketplace. Worth setting by hand when you want the offer picture a shopper in a specific country sees. |
| `maxItems` | Hard ceiling on delivered products. Zero means no ceiling. |
| `maxItemsPerList` | How many products to take from each search term or category link. |
| `maxListPages` | How deep to walk each list. |
| `lanes` | How many products are read at once. Eight by default. Sixteen roughly halves the run time and costs a few offer blocks, because Amazon strips the buy box sooner when you push. On this Actor that is the wrong trade. |
| `maxAttempts` | Attempts per product, each from a different address. Twelve by default. |
| `residentialFallback` | Off by default. Measured 2026-08-26: on it cost roughly six times as much and returned two more products out of sixty. |
| `economical` | Hangs up once the offer block has arrived. Off by default; measured, it saves nothing. |

### How it works, in plain terms

Amazon refuses roughly six requests in ten to an address it has not seen before.
Every product gets up to twelve attempts, each from a different exit address, and
the same address is never used twice in a row. Addresses that work are remembered
for the rest of the run and tried first. When refusals climb the runner slows
down rather than pushing harder.

Anything still refused at the end goes into a second pass at half speed, using
only the addresses that already proved themselves.

The offer block lives inside the buy box, which is the part of the page Amazon
guards hardest. That is why this Actor is one of the more expensive of the eleven
to run, and why the marketplace table below matters more here than elsewhere.

### Reading the run log

```
Done: 16 charged (+3 delivered without a price, free), 1 refused by Amazon, 0 other. 946 KB per charged result, 179 attempts in total.
Exit addresses: 130 used, 1 proven good, 14 burned.
Attempts by outcome: {"refused by Amazon":105,"page did not carry the field this Actor needs":51,"ok":16,"http_500":2,"proxy error":3,"delivered, product unavailable":1,"timed out":1}
```

`charged` is products that came back with a price, which is the marker for the
offer block having arrived. The bracketed number came back complete without one
and cost you nothing.

If a run ever delivers rows and charges for none of them, the log says so in
capitals at the bottom.

### Reliability, measured

Three consecutive runs of twelve products on 26 August 2026, at the default
settings:

```
run 1   12 of 12 delivered, 9 with an offer block
run 2   10 of 12 delivered, 7 with an offer block
run 3   10 of 12 delivered, 8 with an offer block
```

And one run of sixty products the same day: 60 delivered,
53 with an offer block, in 9.4 minutes.

Worth reading out of that: the longer run has the better delivery rate,
100 percent against 89, and that is not luck. The engine spends the first
products of a run working out which exit addresses Amazon is tolerating today, so
a short run pays the whole warm-up over fewer products. If delivery matters more
to you than turnaround, batch your work into larger runs.

The second is the residential switch, and the honest version is less dramatic
than it sounds. Measured 26 August on the same sixty products, twice: with the
switch off, fifty-one came back with the field this Actor charges on, for $0.0205
of platform usage. With it on, fifty-three came back, for $0.1192. Two more
products for roughly six times the cost.

That is why it is off by default. It is in your input form, so if a marketplace
is refusing almost everything you can turn it on and pay for the difference.

### Marketplaces

Nineteen, from amazon.com to amazon.co.jp, read in their own language.

This job depends on the buy box more than most, and the buy box does not arrive
everywhere. Measured on 2026-08-24:

| Marketplace | Products priced |
| --- | --- |
| amazon.com | 57 of 60 |
| amazon.ca | 5 of 5 |
| amazon.co.uk | 5 of 6 |
| amazon.fr | 4 of 5 |
| amazon.it | 1 of 5 |
| amazon.es | 1 of 5 |
| amazon.de | 0 of 8 |
| amazon.com.au | 0 of 5 |

On the marketplaces at the bottom of that table this Actor has little to tell
you, and it will not pretend otherwise: those rows arrive without a price and
without a charge. Run a small job on your marketplace before building on it.

Worth knowing separately: the seller count wording differs by marketplace.
amazon.co.uk prints "New & Used (6) from" and amazon.com sometimes prints nothing
at all on the same product. Both shapes are read, and an absent count means
absent rather than zero.

### Pricing

Four dollars per thousand products with an offer block, plus three cents to start
a run.

Checking a hundred listings costs forty-three cents: a hundred times four tenths
of a cent, plus the three cent start fee. You are not charged
for rows without a price or for products that never arrived, and platform usage
is included.

### The rest of this family

Eleven Actors on one engine. Around an offer checker:

- **Amazon Price Tracker** when you want the price line over time rather than the
  competitive picture today.
- **Amazon Stock Checker** when availability is the question and you want to be
  billed on the stock answer.
- **Amazon Variants Scraper** when the listing you are competing on turns out to
  be a family of fourteen.
- **Amazon Product Scraper** for all sixty-four fields.

### Questions people ask

**Why is otherSellersCount sometimes empty.** Because Amazon only prints the
count on listings that have other sellers, and not on every page variant even
then. Empty means no such line on that page, not zero sellers. If it is empty on
everything, check the marketplace table above.

**Can I find out who the other sellers are.** Not with this Actor, and not with
any Actor in this family. Seller names are personal data by this project's
standard.

**Can I use this for repricing.** As an input, yes: it tells you the current
price, how many competitors, whether a used offer is in play and whether Prime
is. The repricing decision stays yours.

**What does hasSubscribeAndSave actually tell me.** That a lower recurring price
sits next to the one you are reading. On consumables that is often the real
competitive price, and it is invisible if you only look at `price`.

**How often should I run it.** Competition changes more slowly than price. Daily
is plenty for most listings, weekly for stable categories.

**How many products can it check.** No built-in ceiling. Sixty products took
9.4 minutes on the measured run, so a hundred is around 16.

### What it does not collect

No review text, no reviewer names, no customer questions, no seller names. This
Actor does not return ratings either: it answers what is on offer and on what
terms, and the rating belongs to the product rather than to the offer. The
product scraper and the niche research Actor in this family return it.

### Legal note

This Actor reads public pages and takes product facts: prices, offer counts,
delivery terms, return terms, availability. Business information about an item,
not information about a person.

You are responsible for what you do with the output. Check Amazon's terms for
your own use case.

# Actor input Schema

## `productUrls` (type: `array`):

Amazon product URLs or bare ASINs, one per line. Leave empty if you are using Search instead. Duplicates are removed so you are never charged twice.

## `search` (type: `array`):

Anything that lists products: a plain search term ("tower fan"), an Amazon search URL, a category link, or a bestsellers page. Each one is crawled and every product found is scraped. Use this when you do not know the ASINs up front.

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

Which Amazon site to read. The currency of the price follows the marketplace and the proxy country.

## `proxyCountry` (type: `string`):

Which country the requests should appear to come from. Leave empty and it follows the marketplace: amazon.de is read from Germany, amazon.co.uk from the UK. This matters more than it looks -- Amazon shows different prices, currencies and even availability depending on where you appear to be.

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

Hard ceiling on delivered products. 0 means no ceiling: the Actor keeps going until the lists run out or your limits stop it.

## `maxItemsPerList` (type: `integer`):

Products to take from each search term or category link. Up to 10000.

## `proxyConfiguration` (type: `object`):

Datacenter proxies were enough in testing: 30 out of 30 products returned with zero blocks. They are the cheaper option and the default. Switch to residential if you start seeing blocked pages at higher volume. Running without any proxy stops after roughly 85-120 products, because Amazon closes the door on a single IP address for over an hour.

## `lanes` (type: `integer`):

How many products to read at the same time. Each lane uses its own exit address, so more lanes never means asking one address more often. Eight is the tested default and the most accurate. Sixteen finishes the same sixty products in about half the time, but measured on 2026-08-24 it returned six more products without a price, because Amazon strips the buy box sooner under pressure. Raise it when speed matters more than the last few prices.

## `maxAttempts` (type: `integer`):

How often to retry a product Amazon refuses, each time from a different exit IP. A refusal is a property of one address, not of the product, and a refused attempt costs about 4 KB. Twelve tries is cheap insurance against a run that comes back short.

## `residentialFallback` (type: `boolean`):

After half the attempts have failed, retry through residential addresses instead of datacenter ones. Measured 2026-08-26: residential transfer was 90 percent of the bill on a sixty product run ($0.150 of $0.167) while a run that never touched it delivered all sixty anyway. Leave it off unless you are losing products; turn it on when a marketplace refuses everything.

## `economical` (type: `boolean`):

Hang up on each product page once the price is in, instead of reading it to the end. Roughly three times less traffic and about three times faster. Measured cost: over 30 products this lost 2 ratings, 4 review counts and 1 brand, because Amazon sometimes puts the rating below the price. Prices were identical either way. Leave this off unless you only need prices and are paying per gigabyte.

## `maxListPages` (type: `integer`):

Result pages to walk per list. Up to 100. Amazon itself stops earlier on most searches.

## `minDelayMs` (type: `integer`):

Pause between products. Measured 2026-08-23: at 1-2 seconds this returned 10 of 15 products; at 8-15 seconds it returned 12 of 15 on the same list. Pace turned out to matter more than anything else. The market leader waits about 14 seconds per product.

## `maxDelayMs` (type: `integer`):

Upper bound for the pause between products. The actual pause is random between the minimum and this value, which looks less mechanical than a fixed interval.

## `retryPauseMs` (type: `integer`):

How long to wait before trying a refused product again. Kept short because the retry already switches to a different exit IP, and waiting longer only makes the run more expensive.

## Actor input object example

```json
{
  "productUrls": [
    "B09B8V1LZ3",
    "https://www.amazon.com/dp/B0DXXYS4BJ"
  ],
  "search": [],
  "marketplace": "com",
  "maxItems": 0,
  "maxItemsPerList": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "lanes": 8,
  "maxAttempts": 12,
  "residentialFallback": false,
  "economical": false,
  "maxListPages": 5,
  "minDelayMs": 8000,
  "maxDelayMs": 15000,
  "retryPauseMs": 300
}
```

# Actor output Schema

## `products` (type: `string`):

One row per product showing how contested the listing is. Rows without a price are delivered and never charged for.

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

Counts per outcome, exit addresses used, bytes per charged result.

# 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 = {
    "productUrls": [
        "B09B8V1LZ3",
        "https://www.amazon.com/dp/B0DXXYS4BJ"
    ],
    "search": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("amazonscrapers/amazon-offer-checker").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 = {
    "productUrls": [
        "B09B8V1LZ3",
        "https://www.amazon.com/dp/B0DXXYS4BJ",
    ],
    "search": [],
}

# Run the Actor and wait for it to finish
run = client.actor("amazonscrapers/amazon-offer-checker").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 '{
  "productUrls": [
    "B09B8V1LZ3",
    "https://www.amazon.com/dp/B0DXXYS4BJ"
  ],
  "search": []
}' |
apify call amazonscrapers/amazon-offer-checker --silent --output-dataset

```

## MCP server setup

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

```

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/WhN1EThdGnNN5MK8P/builds/XoOCiFgHd4r5F2LL5/openapi.json
