# Amazon Stock Checker: Availability, Delivery & Scarcity (`amazonscrapers/amazon-stock-checker`) Actor

Check whether Amazon products are buyable right now. In stock or not, Amazon's own availability wording, only-N-left warnings, promised delivery date and how many other sellers are on the listing. 19 marketplaces. You only pay for rows with a stock answer.

- **URL**: https://apify.com/amazonscrapers/amazon-stock-checker.md
- **Developed by:** [Amazon Scrapers](https://apify.com/amazonscrapers) (community)
- **Categories:** E-commerce, Automation
- **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 stock answers

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 stock checker: availability, delivery date and scarcity, by ASIN

Give it a list of products. Get back whether each one can be bought right now,
what Amazon promises for delivery, how close it is to running out, and how many
other sellers are on the listing.

This is the smallest question you can ask about a product and the one that goes
stale fastest. A price you read yesterday is probably still roughly right. A
stock status you read yesterday is a guess.

Fifteen fields per product, nineteen marketplaces, and a timestamp on every row
so repeated runs become an availability history rather than a snapshot you have
to remember to keep.

### Read this before your first run

One thing decides whether this Actor is useful to you, and it takes five minutes
to find out: whether Amazon shows a buy box to visitors of your marketplace.

The availability block sits inside the buy box. Where Amazon leaves the buy box
out, the stock answer usually goes with it, and this Actor will honestly return
nothing rather than guess. Measured 2026-08-24, share of products where the buy
box arrived:

| 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 |

Rows without an answer arrive anyway and cost you nothing, so running twenty
products on your own marketplace before you commit is free. Do that first.

### What it answers

**Can it be bought.** `inStock` is true, false, or empty when Amazon does not
say. That third state matters more here than anywhere else: some page variants
carry no availability line at all, and reporting those as out of stock would be a
confident lie about a product that is probably fine.

**In Amazon's own words.** `availability` is the sentence Amazon prints, in the
shop's own language. "In Stock", "Auf Lager", "Derzeit nicht verfügbar". Useful
when you want to know why, not just whether.

**How nearly gone.** `stockLeft` catches "Only 3 left in stock", which Amazon
prints when a listing is running down. Empty on most products, which is itself
the answer: there is no scarcity signal on that page.

**When it would arrive.** `deliveryInfo` is the date Amazon promises, and
`isPrime` whether it comes through Prime. A product in stock with a three week
delivery is a different fact from one arriving Saturday, and for anyone reselling
it is the more important of the two.

**Who else has it.** `otherSellersCount` and `hasUsedOffer`. If the main listing
is out but four other sellers are on it, the product is not gone, it has moved.

**What it costs while you are looking.** `price` and `currency`, from Amazon's
own buy box data. Not the point of this Actor, but you get it anyway, and stock
without price is half an answer.

### Every field it returns

Fifteen per product. No field is here to pad the count: each one either answers
the availability question or identifies the row.

#### Price and offer

| Field | What it holds |
| --- | --- |
| `price` | Current price from Amazon's own buy box data |
| `currency` | Three-letter code, from the page itself |
| `hasUsedOffer` | Whether a used copy is offered |
| `otherSellersCount` | How many other sellers offer it |

#### Stock and delivery

| Field | What it holds |
| --- | --- |
| `inStock` | True, false, or null when Amazon does not say **(this is what you are charged for)** |
| `availability` | Amazon's own availability sentence |
| `stockLeft` | Only three left in stock, when shown |
| `deliveryInfo` | The delivery date Amazon promises |
| `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
inStock            true
availability       In Stock
stockLeft          null
deliveryInfo       Saturday, August 29
isPrime            true
otherSellersCount  2
hasUsedOffer       false
price              29.99
currency           USD
marketplace        com
scrapedAt          2026-08-26T09:14:22.031Z
```

No scarcity warning on that product today, two other sellers on the listing, and
Prime delivery on Saturday. The empty `stockLeft` is an answer, not a gap.

### What it charges for

This Actor charges on the stock answer, not on the price.

That is deliberate and it is unusual. A definite "no, this is out of stock" is
exactly what you came for and it is worth the same as a yes. Most scrapers on
this Store bill per row delivered, so a run against fifty discontinued products
costs the same as a run against fifty available ones while telling you less.

Here, if the page carries no availability information at all, the row still
arrives with everything else and you are not charged for it. On a mixed list, or
on a marketplace where the buy box is scarce, that is the difference between a
bill and a surprise.

### Reading the empty values honestly

`inStock` empty is not the same as false. Empty means Amazon sent a page with no
availability block. False means Amazon said so, in whatever language.

This distinction cost a real defect to learn, and it is worth telling because it
shows what this Actor refuses to do. An earlier version of the engine read
Amazon's mobile page as a second source, because that page seemed to solve a
price problem. It reported five popular products as unavailable when it simply
could not work out where to deliver them, and it dropped the specification count
from twenty to eight while it was at it. Those five rows counted as delivered and
would have been billed.

The mobile page was removed and the rule became: one address per product, always
the canonical one, and a confident wrong answer the customer pays for is worse
than an empty field they do not.

### 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`, something with quotes or a trailing full stop around it.
Duplicates are removed across everything you give it, so a product listed twice
is fetched once and billed once.

**A search term.** `standing desk converter` becomes an Amazon search, and every
result gets checked.

**A category or bestseller link.** Paste
`https://www.amazon.com/gp/bestsellers/electronics` and it walks the list.

**A filtered search URL.** Build the search in your own browser with the filters
you want, paste the address, and this Actor continues from there. Reproducing
Amazon's filter syntax by hand is not worth anyone's afternoon.

You can mix all four in one run.

### Settings worth knowing about

| Setting | What it does |
| --- | --- |
| `marketplace` | One of nineteen, from `com` to `co.jp`. The currency and the language follow it. |
| `proxyCountry` | Leave empty and it follows the marketplace. Setting it by hand is only useful when you want availability as it appears to a shopper in a specific country. |
| `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. Amazon stops earlier on most searches anyway. |
| `lanes` | How many products are read at once, each on its own exit address. Eight by default. Sixteen roughly halves the run time and costs a few answers, because Amazon strips the buy box sooner when you push. |
| `maxAttempts` | Attempts per product, each from a different address. Twelve by default. Lowering it makes runs cheaper and loses products. |
| `residentialFallback` | Off by default. Retries through residential addresses after half the attempts have failed. Measured 2026-08-26: turning it on cost roughly six times as much and returned two more products out of sixty. Leave it off unless a marketplace is refusing everything. |
| `economical` | Hangs up on each page once the availability block has arrived. Off by default; measured across three pairs of runs it saves nothing. |

### How it works, in plain terms

Amazon refuses roughly six requests in ten to an address it has not seen before.
Any scraper that asks once and gives up returns a third of what you asked for and
calls it a day.

This one keeps a squad of exit addresses and learns which ones Amazon is
currently tolerating. Every product gets up to twelve attempts, each from a
different address, and the same address is never used twice in a row. Addresses
that work get remembered for the rest of the run and reused first. Addresses that
get refused repeatedly are dropped.

Between attempts it waits, and the wait adapts: when Amazon starts refusing more,
it slows down instead of pushing harder, because pushing harder is how a whole
run gets locked out.

Anything still refused at the end of the first pass goes into a second pass at
half speed, using only the addresses that already proved themselves. That second
pass is where the difference between ninety percent and a hundred percent lives.

### Reading the run log

Every run ends with a summary that tells you exactly what happened:

```
Done: 19 charged (+1 delivered without a price, free), 0 refused by Amazon, 0 other. 1267 KB per charged result, 186 attempts in total.
Exit addresses: 147 used, 3 proven good, 12 burned.
Attempts by outcome: {"page did not carry the field this Actor needs":39,"refused by Amazon":122,"http_500":2,"ok":19,"network error":4}
```

`charged` is what you pay for. The number in brackets arrived and cost you
nothing. `refused by Amazon` is what never came back after twelve attempts, and
if that number is not zero on amazon.com something is unusual that hour.
`attempts in total` divided by products tells you how hard Amazon was pushing
back.

If a run ever delivers rows and charges for none of them, the log says so in
capitals at the bottom. That is a configuration problem rather than a scraping
one, and it is better read in a log than found on an invoice.

### Reliability, measured

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

```
run 1   12 of 12 delivered, 12 with a stock answer
run 2   12 of 12 delivered, 12 with a stock answer
run 3   12 of 12 delivered, 12 with a stock answer
```

And one run of sixty products the same day: 60 delivered,
58 with a stock answer, in 9.6 minutes.

Worth reading out of that: nothing was lost at either size. 36 of 36 on the
short runs and 60 of 60 on the long one. That is the shape you want before you
schedule something, because a scraper that is reliable only on small batches is
not reliable, it is lucky.

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.

### What you can build with it

**A back-in-stock watcher.** Run daily on a fixed list, write into one dataset,
and compare the last two rows per ASIN. The comparison stays in your code, so you
can change the rule without changing the check.

**A supplier reliability report.** Track how often each brand's products go out
of stock over a month. `availability` in Amazon's own words tells you whether it
is a supply problem or a delisting.

**A pre-purchase sweep.** Before committing to a bulk order, check that the
competing listings are actually buyable. A category where half the listings are
out is a different opportunity from one where none are.

**A delivery-promise comparison.** `deliveryInfo` and `isPrime` across a category
show which sellers are winning on speed rather than on price.

**A scarcity signal.** `stockLeft` on a watchlist is the closest public thing to
a sell-through rate. It is rare, and when it appears it is worth acting on.

### Working it into your stack

Every run writes to a dataset you can download as CSV, JSON, Excel or XML, or
pull through the API. Point repeated runs at one named dataset and the rows
accumulate into a history, sortable by `asin` and `scrapedAt`.

Schedules live in the Schedules tab and take a cron expression. Webhooks fire on
run completion, so the comparison and the alerting can live wherever your other
code lives.

The output is a flat table: strings, numbers and booleans, with no nested
structures to unpack. It loads into a spreadsheet without cleaning.

### Marketplaces

Nineteen, from amazon.com to amazon.co.jp, and availability is read in the shop's
own language rather than translated afterwards. A German "Derzeit nicht
verfügbar" comes back as `inStock: false`, not as an empty field, and that is a
real difference: an empty field would have made every German product look
unknown.

The one caveat is the buy box, and it is at the top of this page because it
decides whether this Actor works for you at all.

Worth knowing separately: `currency` reports what Amazon actually quoted, not
what the marketplace usually uses. amazon.ca quotes US dollars to a visitor it
places in the United States, and this Actor reports that rather than silently
relabelling it.

### Pricing

Four dollars per thousand products with a stock answer, plus three cents to start
a run.

Checking a hundred products costs forty-three cents: a hundred times four tenths
of a cent, plus the three cent start fee. You are not charged
for a product where Amazon gave no availability information, or for one that
never arrived. Platform usage is included in that price, so there is no separate
compute bill and no proxy bill.

Watching fifty products daily costs twenty-three cents a day: fifty times four
tenths of a cent, plus the three cent start fee.

### The rest of this family

This Actor is one of eleven built on the same engine. If you need something next
to a stock answer, one of these is probably a better fit than stretching this
one:

- **Amazon Price Tracker** for price history on the same list of ASINs.
- **Amazon Offer Checker** when the question is how many sellers are on the
  listing and on what terms.
- **Amazon Product Scraper** when you want all sixty-four fields per product.
- **Amazon Bestsellers Scraper** and **Amazon Search Results Scraper** to
  establish which products to watch before you start watching them.

They share the retry engine, the pricing model and the rule about never guessing
a price, so what you learn on one carries to the others.

### Questions people ask

**How often should I run it.** Depends on how fast your stock moves. Daily is
common. For products you are actively selling against, several times a day is
reasonable and still cheap.

**Why is stockLeft usually empty.** Amazon only prints "Only N left" when a
listing is genuinely running down. An empty value means there is no such warning,
which usually means supply is fine.

**Why is inStock sometimes empty on amazon.de.** Because Amazon sent a page
without a buy box, which it does on several marketplaces when it cannot place the
visitor in a delivery country. Empty means we do not know, and you are not
charged for it.

**Can I get an alert when something goes out of stock.** Not from this Actor
directly. Put a webhook on the run and compare against your last result.

**Does it tell me who the other sellers are.** It tells you how many, not who.
Seller names can identify a person, and this Actor does not collect personal
data.

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

**Can I run it on a schedule.** Yes, from the Schedules tab. Every row carries
`scrapedAt`, so repeated runs into one dataset become a record of how
availability moved.

**What happens if Amazon blocks everything.** The run still ends successfully
with whatever came back, and the log says how many were refused. It does not fail
the run for you, because a failed run on this Store looks like a broken Actor
even when Amazon was simply having a bad hour.

### What it does not collect

No review text, no reviewer names, no customer questions, no seller names. Those
are personal data, and scraping them 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.

This Actor does not return the rating or the review count either. Not for
privacy reasons, simply because they are not part of a stock answer; the product
scraper in this family returns both.

### Legal note

This Actor reads public pages and takes product facts: availability, delivery
promises, offer counts, price. 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, and if you are in Europe, keep in mind that combining public
data into a profile of a person is a different activity from tracking stock.

# 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: buyable or not, how soon, how nearly gone. Rows where Amazon gave no availability 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-stock-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-stock-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-stock-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,amazonscrapers/amazon-stock-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/eZqY5PIEJvCWRdG7o/builds/XtLltSWAa2DRMKQDp/openapi.json
