# Amazon Sponsored Products: Ad Placements by keyword & SOV (`amazonscrapers/amazon-sponsored-products-tracker`) Actor

See who is advertising on any Amazon search term. One row per slot on the results page: position, sponsored or organic, ASIN, title, price, rating and review count. Work out share of voice per keyword. 19 marketplaces. You only pay for slots that came back readable.

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

## Pricing

from $1.00 / 1,000 search placements

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 sponsored products tracker: who is advertising on your keywords

Give it a search term. Get back every slot on the results page in the order
Amazon showed them, with the advertisements marked.

Position one, sponsored, someone else's ASIN. Position two, organic, yours.
That is the whole product: the ranking with the ads marked inside it, which is
what share of voice actually is and what you cannot get from any Amazon report
about your own account.

This is the only Actor in this family that never opens a product page. Everything
it returns was already on the search page, which is why it is fast and why the
price per row is a tenth of what the others charge.

### Read this before your first run

**Search pages get refused in waves, and one setting fixes it.** Measured on 30
August 2026, during a stretch of several hours when Amazon refused every search
page with a two kilobyte stub:

| Setting | Result |
| --- | --- |
| Datacenter, 12 retries | nothing, every attempt refused |
| Datacenter, 20 retries, six second pauses | nothing, every attempt refused |
| Residential on the later attempts | 60 placements, 15 sponsored, 8 attempts |

That is why `residentialFallback` is on by default here, unlike the rest of this
family. It costs about seven cents per thousand slots instead of four, which
against the price of a slot is nothing, and it is the difference between a run
with data and a run with none.

Turn it off if you want the cheapest possible run and do not mind an empty one
now and then. The run log always says which happened.

**Amazon does not always serve the same search page.** Measured the same day, the
same search term fetched five times within an hour:

| Results on the page | Advertisements found |
| --- | --- |
| 60 | 15 |
| 60 | 15 |
| 60 | 14 |
| 48 | 3 |
| 48 | 1 |

Both versions answer normally. Neither carries an error, a captcha or any other
sign that something is missing. The thin one simply has most of the advertising
left out.

This Actor detects that. When a page comes back carrying far fewer results than
Amazon's own result count implies, it treats it as a refusal and fetches it again
from a different address, up to `maxAttempts` times. If it still cannot get a
full page after all of those, it delivers what it has and says so in the run log,
in plain words, rather than handing you a table that looks complete.

Read that line in the log before you trust a low sponsored count. A keyword with
two advertisements and a keyword where Amazon gave us a thin page look identical
in a spreadsheet, and only the log tells them apart.

**It charges for every slot, sponsored or not.** Share of voice is a fraction.
Fifteen of sixty is a different number than fifteen of twenty, and you cannot
work out either one from the sponsored rows alone. The organic rows are half of
what you are buying.

### Every field it returns

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

#### Demand and rank

| Field | What it holds |
| --- | --- |
| `isBestSeller` | The orange best seller badge |
| `isAmazonChoice` | The Amazon's Choice badge |
| `rating` | Average stars, read in every shop language |
| `reviewCount` | How many ratings, as a number |

#### Identity and catalogue

| Field | What it holds |
| --- | --- |
| `asin` | The product id **(this is what you are charged for)** |
| `title` | Full product title, shortened only past 1000 characters |
| `marketplace` | Which shop this row came from |
| `url` | The page this was read from |
| `scrapedAt` | Timestamp of the read |

#### Placement

| Field | What it holds |
| --- | --- |
| `keyword` | The search term this slot was found on |
| `page` | Which results page the slot was on |
| `position` | Where the slot sat on that page, counting from one |
| `isSponsored` | Whether Amazon marked this slot as an advertisement |

### What a row looks like

```
"keyword": "standing desk converter",
"page": 1,
"position": 1,
"isSponsored": true,
"asin": "B0864RSM5S",
"title": "VERSADESK PowerPro 36 Inch Electric Standing Desk Converter",
"price": 349,
"currency": "USD",
"rating": 4.4,
"reviewCount": 502
```

Position is where the slot sat on that page, counting from one. Page is which
results page it came from. Together they give you the true rank across a
multi-page run.

### The same product can appear twice, and that is the point

An advertiser who also ranks organically holds two slots on one page. This Actor
reports both, because collapsing them would destroy exactly the measurement you
came for. On the page these numbers were measured from, one ASIN held position 1
as an advertisement and position 10 organically.

If you want unique products instead of slots, group by `asin` on your side. The
Actor will not do it for you, because it cannot know which of your two questions
you are asking.

### How the advertisement marker works

Amazon marks a sponsored slot in several ways at once, and this Actor looks for
the ones that do not depend on language: the click-tracking link, the sponsored
label class, and the advertisement container class. That matters because the
visible word is "Sponsored" on amazon.com, "Gesponsert" on amazon.de and
something else again on the other seventeen marketplaces.

Worth saying plainly: the marker that most guides on the internet name,
`sp-sponsored-result`, appeared on neither of the real pages this was built
against. What is in here came from captured pages, not from documentation.

### What to put in

Search terms, plain, one per line:

```
standing desk converter
sit stand desk riser
monitor arm dual
```

Category and search links work too, if you would rather track a browse page than
a keyword:

```
https://www.amazon.com/s?k=standing+desk+converter
https://www.amazon.com/gp/bestsellers/office-products
```

`maxListPages` decides how deep to go. Page one is where the money is; pages two
and three are where you find who is bidding cheaply.

### Settings worth knowing about

| Setting | What it does |
| --- | --- |
| `search` | The keywords or category links to track. |
| `marketplace` | Which Amazon. Nineteen available. |
| `maxItemsPerList` | How many slots to take per keyword, across all pages. |
| `maxListPages` | How many results pages to walk per keyword. |
| `proxyConfiguration` | Which proxy to use. Datacenter by default. |
| `proxyCountry` | Which country to exit from. Empty means follow the marketplace. |
| `residentialFallback` | Switch the later attempts to residential addresses. **On by default for this Actor**, and it is the setting that decides whether you get data at all when Amazon is refusing search pages. See below. |
| `maxAttempts` | How many times to retry a page Amazon refuses or serves thin. |
| `minDelayMs` `maxDelayMs` `retryPauseMs` | The pacing between pages. The defaults are deliberately unhurried. |

### How it works, in plain terms

It fetches the search results page, reads every result slot on it in order, and
records what was there. No product pages are opened, so a keyword costs one page
fetch and returns sixty rows instead of sixty fetches returning sixty rows.

Each slot is checked for the advertisement markers. Title, price, star rating and
review count are read from the search page itself, which is where Amazon already
prints them.

If Amazon refuses the page, or serves a thin one, it tries again from a different
exit address. Both cases are counted separately in the run log so you can see
which happened.

### Reading the run log

Every run ends with three lines from a real run of this Actor:

```
Done: 60 charged, 0 refused by Amazon, 0 other. 113 KB per charged result, 10 attempts in total.
Exit addresses: 10 used, 1 proven good, 0 burned.
Attempts by outcome: {"refused by Amazon":7,"served fewer results than Amazon claims to have":2,"ok":1}
```

Two extra lines are worth watching for. One says a page came back thin, meaning
Amazon served fewer results than it claims to have, and you are seeing fewer
advertisements than were really there. The other says a page never arrived at all
after every retry, in which case that keyword is simply missing from the run and
you were not charged for it.

### What you can build with it

**Share of voice per keyword.** Count your ASINs among the sponsored slots,
divide by the total sponsored slots. Run daily and you have the trend line that
agencies charge a monthly fee for.

**Competitor ad watch.** Track the keywords you bid on and see who else shows up,
at which positions, and on which days. A competitor who suddenly appears at
position one on twelve of your keywords has just raised their budget.

**Keyword discovery through the back door.** Track a competitor's ASIN across a
hundred keywords and you learn which terms they are willing to pay for, which is
a better signal about commercial intent than any keyword volume tool.

**Organic versus paid ranking.** The same ASIN in both a sponsored and an organic
slot tells you whether their ranking is bought or earned.

**Ad load by category.** Fifteen sponsored slots out of sixty is a crowded
keyword. Two out of sixty is a cheap one. Run a list of candidate keywords before
you plan a campaign.

### Working it into your stack

Set a schedule and let it run at a fixed hour each morning, so day-to-day
comparisons are like for like. Ad placements move throughout the day, and a run
at nine and a run at five are not the same measurement.

Pull the dataset from the API:

```
https://api.apify.com/v2/datasets/<datasetId>/items?clean=true&format=csv
```

Every field is flat, so it drops straight into a pivot table. Group by `keyword`
and `isSponsored` and you have share of voice without writing any code.

### Marketplaces

Nineteen, from amazon.com to amazon.co.jp. The advertisement markers this Actor
uses are the language-independent ones, so a German or Japanese results page is
read the same way as an American one.

Ad load itself varies a lot by marketplace and by category, and that is real
rather than a limitation: a keyword with no advertisers has no sponsored slots to
find.

### Pricing

A tenth of a cent per slot, plus three cents to start a run.

One keyword at five pages is about three hundred slots, so thirty cents. Twenty
keywords at one page each is about twelve hundred slots, so a dollar twenty.
Tracking ten keywords daily at one page costs about eighteen cents a day.

The price per row is low because the work per row is low: one page fetch of about
half a megabyte carries sixty rows, where the product scrapers in this family read
half a megabyte per row. You are charged per slot delivered. Slots that could not
be read are not delivered and not charged for, and a page that never arrived costs
you nothing beyond the start fee.

### The rest of this family

This Actor is one of thirteen on the same engine.

- **Amazon Search Results Scraper** when you want the full sixty-four fields for
  every product on a search page rather than the slots themselves.
- **Amazon Listing Monitor** to watch what changes on the ASINs you find here.
- **Amazon Bestsellers Scraper** for category rankings rather than keyword ones.
- **Amazon Product Scraper** for everything about a single product.

They share the retry engine and the pricing model, so what you learn on one
carries to the others.

### Questions people ask

**Why do I get fewer advertisements than I see in my own browser.** Almost always
because Amazon served a thin page. Check the run log for the line about thin
pages, and make sure `residentialFallback` is on. A signed-in browser on a residential
connection is the most ad-heavy view of Amazon there is, and it is not a fair
comparison.

**Does it tell me what the advertiser paid.** No. Amazon does not publish bids or
costs anywhere on the page. What you get is presence and position, which is what
share of voice is built from.

**Can I track my own campaigns with it.** For placement and position, yes. For
spend, impressions and clicks, use Amazon's own advertising reports for your
account; this reads what a shopper sees.

**Does it open product pages.** No. That is deliberate and it is why this costs a
tenth of what the product scrapers cost.

### What it does not collect

No review text, no reviewer names, no customer questions, no seller names. Review
text and reviewer names are personal data, and scraping personal data is almost
always a problem under European privacy law regardless of intent. The star rating
and the review count are aggregate numbers and those you do get.

You can see which ASIN holds a sponsored slot. You cannot see which person or
company is behind it, because Amazon does not print that on the search page and
this Actor does not go looking.

### Legal note

This Actor reads pages that Amazon serves publicly, at a deliberately unhurried
pace. It is not affiliated with, endorsed by, or connected to Amazon.com, Inc.
What you may do with the data it returns depends on where you are and what you
use it for, and that is your call to make.

# Actor input Schema

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

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

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

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

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

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

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

## `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
{
  "search": [
    "standing desk converter"
  ],
  "marketplace": "com",
  "maxItemsPerList": 50,
  "maxListPages": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxAttempts": 12,
  "residentialFallback": true,
  "minDelayMs": 8000,
  "maxDelayMs": 15000,
  "retryPauseMs": 300
}
```

# Actor output Schema

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

One row per slot on the search results page, in the order Amazon showed them, with advertisements marked. Slots that could not be read are not 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 = {
    "search": [
        "standing desk converter"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("amazonscrapers/amazon-sponsored-products-tracker").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 = { "search": ["standing desk converter"] }

# Run the Actor and wait for it to finish
run = client.actor("amazonscrapers/amazon-sponsored-products-tracker").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 '{
  "search": [
    "standing desk converter"
  ]
}' |
apify call amazonscrapers/amazon-sponsored-products-tracker --silent --output-dataset

```

## MCP server setup

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

```

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/G6RD8B8JhWS8DaxHY/builds/EhaWUGePAesgvxdyj/openapi.json
