# Amazon Variants Scraper: Map the Whole Product Family (`amazonscrapers/amazon-variants-scraper`) Actor

Given one Amazon product, get every sibling it has: each size, each colour, each pack count, with the ASIN of each and what varies between them. 19 marketplaces, and it works where prices do not.

- **URL**: https://apify.com/amazonscrapers/amazon-variants-scraper.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 variant families

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 variants scraper: map the whole product family, sizes and colours

Give it one product. Get back every sibling it has: each size, each colour, each
pack count, with the ASIN of each and, crucially, what varies between them.

An Amazon listing is rarely one product. A shirt is fourteen ASINs pretending to
be one page, and which one you land on depends on which link you followed. If you
are tracking a price, comparing against a competitor, or building a catalogue,
working on one ASIN when there are fourteen means you are working on a fraction
of the thing and do not know it.

Seventeen fields per product, nineteen marketplaces, and no dependence on the buy
box, so this Actor works where the price ones return nothing.

### What each row carries

**The family.** `variantAsins` is every sibling ASIN. `variantCount` is how many.
`parentAsin` is the listing they all hang from, when Amazon exposes it.

**What actually varies.** `variantAttributes` names the dimensions of the family:
`["size", "color"]`, or just `["style"]`, or nothing at all. This is the field
that tells you whether you are looking at a size run or a colour run, and it is
the one most tools do not return.

**Enough to identify the one you have.** `colour`, `material`, `unitCount`, plus
`title` and `brand`. When a family has fourteen members these are how you tell
which row is which.

**Enough to act on it.** `price`, `currency` and `inStock` for the ASIN you asked
about, plus `image`.

### How the family is read, and why that took two sources

Amazon puts the variant data in two different places depending on the page, and
an earlier version of this engine looked in only one of them.

It searched for element names like `variation_color_name`, which some pages
carry. Other pages keep the same information inside a block of page state, and
those came back with no variants at all. The fix was to read both, and it took
the variant attribute coverage from one product in six to five in six.

That is why `variantAttributes` is a field here rather than a footnote: getting
it required knowing about the second source, and it is the difference between
"this product has 6 siblings" and "this product comes in 6 sizes".

### Every field it returns

Seventeen per product. Deliberately narrow: this Actor answers one question and
gives you enough context to act on the answer.

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

#### Stock and delivery

| Field | What it holds |
| --- | --- |
| `inStock` | True, false, or null when Amazon does not say |

#### Identity and catalogue

| Field | What it holds |
| --- | --- |
| `asin` | The product id |
| `parentAsin` | The parent listing, for variant families |
| `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 |

#### Description, media and variants

| Field | What it holds |
| --- | --- |
| `image` | Main image at the largest size Amazon offers |
| `variantAsins` | Sibling products in the variant family **(this is what you are charged for)** |
| `variantCount` | How many variants there are |
| `variantAttributes` | What varies, such as size and colour |

#### Specifications, pulled apart

| Field | What it holds |
| --- | --- |
| `colour` | Colour |
| `material` | Material |
| `unitCount` | How many units in the pack |

### What it charges for

This Actor charges on `variantAsins`, and on nothing else.

A product with no siblings is a real and useful answer, but it is not the answer
this Actor sells. If a listing turns out to be a single standalone product, the
row still arrives with the price, the colour and the rest, and you are not
charged for it.

That means running it across a mixed list is safe: you pay for the families and
not for the singletons. Measured across a sixty product run on 26 August 2026,
fifty-two of fifty-eight delivered products carried a family, so roughly one row
in ten arrived free.

### A real row

```
asin                B00FXNABPI
parentAsin          null
title               Amazon Basics Slim Velvet Non-Slip Suit Hangers
brand               Amazon Basics
variantCount        20
variantAsins        20 ASINs
variantAttributes   ["size", "color"]
colour              Black
material            Velvet
unitCount           30
price               8.95
currency            GBP
inStock             true
image               https://m.media-amazon.com/images/I/...
scrapedAt           2026-08-26T09:14:22.031Z
```

Twenty siblings varying by size and colour. No parent ASIN on that listing, which
is common: Amazon does not always expose it, and its absence does not mean the
family is not there.

### What to do with the sibling ASINs

`variantAsins` gives you the list. To get full data on each sibling, feed that
list back in as input, either to this Actor or to one of its relatives. Two runs,
and you have the whole family with prices.

This Actor deliberately does not expand the family for you. Doing so would mean
deciding on your behalf how many siblings are worth fetching and charging you for
that decision. On a catalogue of a thousand parent listings with an average of
six siblings, that decision is the difference between a thousand charged rows and
six thousand. Handing you the list and letting you choose is cheaper and clearer.

The usual pattern is two steps: run this Actor on your list, collect the union of
all `variantAsins`, then run the price tracker or the stock checker on that
union.

### 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 category link**, if you want to find which products in a
category are families and which are standalone. That is a useful question on its
own: a category full of fourteen-variant listings is a different competitive
picture from one full of single products.

### Settings worth knowing about

| Setting | What it does |
| --- | --- |
| `marketplace` | One of nineteen. Variant families differ per marketplace: the same parent listing can carry six sizes in the United States and three in Germany. |
| `proxyCountry` | Leave empty and it follows the marketplace. |
| `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. Variant data does not come from the buy box, so pushing to sixteen costs less here than on the price Actors. |
| `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` | Leave it off. The variant block sits in the page state rather than at the top, so hanging up early costs you the field you came for. |

### 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 instead of pushing harder, because pushing harder is how a whole run gets
locked out.

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

The retry rule follows the variant family rather than the price, so a page that
handed over twenty sibling ASINs on the first attempt is not asked again just
because Amazon left the buy box out.

### Reading the run log

```
Done: 16 charged (+3 delivered without a price, free), 1 refused by Amazon, 0 other. 559 KB per charged result, 109 attempts in total.
Exit addresses: 74 used, 1 proven good, 11 burned.
Attempts by outcome: {"refused by Amazon":71,"ok":16,"http_500":2,"page did not carry the field this Actor needs":19,"delivered, product unavailable":1}
```

`charged` is products that came back with at least one sibling. The bracketed
number are the standalone products, delivered and free.

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 a variant family
run 2   12 of 12 delivered, 9 with a variant family
run 3   11 of 12 delivered, 9 with a variant family
```

And one run of sixty products the same day: 58 delivered,
52 with a variant family, in 7.5 minutes.

Worth reading out of that: the longer run has the better delivery rate,
97 percent against 97, 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.

### What you can build with it

**A complete price watch.** Expand every parent listing into its siblings before
you start tracking, so you are not watching the black one while the competition
discounts the blue.

**A catalogue import.** One row per family gives you the structure; a second run
on the sibling list gives you the detail.

**A size-run audit.** `variantAttributes` across a category tells you whether
competitors offer three sizes or nine, which is a stocking decision as much as a
listing decision.

**A cannibalisation check.** When your own listing sits in a family with a
competitor's, `variantCount` and `variantAsins` show you exactly who you are
sharing a page with.

**A gap finder.** A family of six colours where two are permanently out of stock
is a gap, and the stock checker in this family will tell you which two.

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

One shape note: `variantAsins` and `variantAttributes` are lists, so a strict flat
export will stringify them. If your destination wants one row per sibling rather
than one row per parent, expand them on the way in; the API makes that a few
lines.

Schedules take a cron expression. Variant families change when a seller adds a
colour, which is rarely, so weekly is usually plenty.

### Marketplaces

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

Variant data does not depend on the buy box, so this Actor works on marketplaces
where prices do not. Measured on amazon.de, where no product returned a price:
variant data came back normally. The `price` column will be empty there, the
family will not.

Worth knowing: families are per marketplace. The same parent listing can carry
six sizes on amazon.com and three on amazon.de, and that difference is real
rather than a scraping artefact.

### Pricing

Four dollars per thousand products with a variant family, plus three cents to
start a run.

Mapping 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
standalone products or for products that never arrived, and platform usage is
included.

Because the charge is on the family rather than on the row, a catalogue that
turns out to be mostly single products costs you very little to find that out.

### The rest of this family

Eleven Actors on one engine. The natural next steps after mapping a family:

- **Amazon Price Tracker** on the sibling list, so you watch every colour rather
  than one.
- **Amazon Stock Checker** to see which siblings are actually buyable.
- **Amazon Product Specs** when each sibling needs its own catalogue row.
- **Amazon Product Scraper** for all sixty-four fields on the siblings that
  matter.

### Questions people ask

**Why is variantAttributes sometimes empty when variantAsins is not.** Because
Amazon lists the siblings without naming what distinguishes them. It happens on
older listings. You still get the ASINs.

**Why is parentAsin often empty.** Amazon does not expose it on every page. When
it is there you get it; when it is not, `variantAsins` still gives you the
family.

**Does the list include out of stock siblings.** Yes. Amazon lists them and so
does this Actor. Whether a sibling is buyable is a separate question, and the
stock checker in this family answers it.

**Is the ASIN I asked about in variantAsins.** Yes, the list is the whole family
including the one you asked for. Deduplicate on your side if you are building a
union.

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

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

### Legal note

This Actor reads public pages and takes product facts: which products belong
together and what distinguishes them. Business information about items, not
information about people.

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": [
    "B00FXNABPI",
    "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 with its whole variant family. Standalone products 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": [
        "B00FXNABPI",
        "https://www.amazon.com/dp/B0DXXYS4BJ"
    ],
    "search": []
};

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

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

```

## MCP server setup

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

```

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/Tg73icOKJmfFTate0/builds/wEoACTe48MK6lauXl/openapi.json
