# Amazon Product & Search Scraper (`fanndev/amazon-product-search-scraper`) Actor

Scrape Amazon search results by keyword or search URL: ASIN, title, price, list price and discount, rating, exact review count, bestseller and Amazon's Choice badges, units bought last month, and image URL. Built for price-distribution analysis and competitor catalogue monitoring.

- **URL**: https://apify.com/fanndev/amazon-product-search-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/fanndev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

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 Product & Search Scraper

Scrape Amazon search results by keyword or search URL — ASIN, title, price,
list price and discount, rating, **exact** review count, bestseller and
Amazon's Choice badges, units bought last month, and image.

Built for price-distribution analysis and competitor catalogue monitoring.

No login, no API key. HTTP-only — no browser.

### What you get

```json
{
  "position": 1,
  "page": 1,
  "asin": "B01LP0U5X0",
  "title": "Amazon Basics Extra Thick Exercise Yoga Mat with Carrying Strap",
  "price": 24.18,
  "currency": "$",
  "listPrice": null,
  "discountPercent": null,
  "rating": 4.6,
  "reviewCount": 70384,
  "boughtInPastMonth": 10000,
  "isSponsored": false,
  "isBestSeller": true,
  "isAmazonsChoice": false,
  "imageUrl": "https://m.media-amazon.com/images/I/...",
  "productUrl": "https://www.amazon.com/dp/B01LP0U5X0"
}
```

Field coverage on a typical 48-result page:

| Field | Coverage |
| --- | --- |
| asin, title, rating, reviewCount, imageUrl | 48/48 |
| boughtInPastMonth | 45/48 |
| price | 44/48 |

Prices are missing on multi-variant tiles that show "See options" instead of
a figure. That is a real state, not a parse failure, so it stays `null`.

**Review counts are exact.** The visible text on Amazon is abbreviated
(`70.3K`); this actor reads the aria-label instead and reports `70384`. A
test asserts large counts are not suspiciously round.

### Category URLs: read this first

A URL only works if it contains a **`k=` keyword**. Measured:

| URL form | Results |
| --- | --- |
| `/s?k=yoga+mat` | 48 |
| `/s?k=yoga+mat&rh=n%3A3407731` (category filter) | 24 |
| `/s?k=yoga+mat&s=price-asc-rank` (sorted) | 48 |
| `/s?k=yoga+mat&rh=p_36%3A1000-3000` (price filter) | 60 |
| **`/s?rh=n%3A3407731`** (node only) | **0** |
| **`/s?node=3407731`**, **`/b?node=…`** | **0** |

Amazon serves node-only category URLs as a *browse* layout with no search
results at all for logged-out clients. The actor rejects those up front with
an explanation telling you to add a keyword, rather than running and quietly
returning nothing.

So: any filter, sort or category refinement works — just keep a keyword in
the URL.

### Input

```json
{
  "searchTerms": [
    "yoga mat",
    "https://www.amazon.com/s?k=coffee+maker&s=price-asc-rank"
  ],
  "marketplace": "com",
  "maxPages": 3,
  "maxItemsPerTerm": 0,
  "includeSponsored": true
}
```

| Field | Description |
| --- | --- |
| `searchTerms` | Keywords, or Amazon search URLs containing `k=` |
| `marketplace` | com, co.uk, de, fr, it, es, ca, com.au, co.jp, in |
| `maxPages` | Up to 7 — Amazon stops offering a next page around there |
| `maxItemsPerTerm` | Hard cap per term; 0 for no cap |
| `includeSponsored` | Off keeps only organic results, for a cleaner price distribution |
| `delaySeconds` | Pacing — Amazon serves CAPTCHAs to traffic it dislikes |
| `proxyConfiguration` | Residential **strongly recommended** |

ASINs are de-duplicated across pages, so a product Amazon repeats on page 2
is not double-counted in your price statistics. `position` is the global
rank across pages; `positionOnPage` restarts each page.

### Two fields that need a proxy in the right country

**`isPrime` and `deliveryInfo` follow the exit IP, not the marketplace.**
From an Indonesian IP, amazon.com showed **zero** Prime badges across 48
results and rendered "Ships to Indonesia" with international delivery fees
instead. Nothing was broken — Amazon simply does not offer Prime to that
destination.

Every row carries `shipsTo` so you can tell. If it is not the marketplace's
own country, treat `isPrime: false` as *unknown*, not as *not Prime*, and
rerun with a residential proxy in that country.

**Prices are pinned to the marketplace currency** regardless of exit IP,
via the cookie Amazon honours for this. Without it an Indonesian exit
returns `IDR 336,939.57` for a $19.99 item, which would silently corrupt
price analysis. `currency` is on every row, so never assume USD.

### About `brandGuess`

Amazon's current search results contain **no brand element** — no brand
span, no `data-brand` attribute, no brand JSON. Verified across three
queries. The `a-size-base-plus` class that older scrapers read for brand is
the *title* element on today's markup.

So `brandGuess` is **derived from the leading words of the title**, and it
is named that way on purpose. It gets `Gaiam` and `BLACK+DECKER` right, and
it will get `Amazon` where the brand is really `Amazon Basics`. Use it for
rough grouping; do not treat it as authoritative. There is no field here
claiming to be a scraped brand, because there is no such data to scrape.

### Known limits

- **~7 pages per term.** Amazon stops serving a next-page link; the actor
  stops with it rather than burning requests on filler pages.
- **No brand**, for the reason above.
- **Prime and delivery are geo-dependent** — see above.
- Sponsored placements are auctioned per impression, so the sponsored subset
  of a page differs between runs. `isSponsored` reflects that impression.
- CAPTCHAs are reported as `CAPTCHA_ESCALATION` rows, never solved.

See [CRAWLING\_METHOD.md](CRAWLING_METHOD.md) for the fingerprint matrix, the
category-URL measurements and the parsing pitfalls.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords, or full Amazon search URLs. A URL must contain a k= keyword — Amazon serves node-only category URLs (rh=n%3A..., node=..., /b?...) as a browse page with no results for logged-out clients. Filters and sorts work fine alongside a keyword, e.g. /s?k=yoga+mat\&rh=n%3A3407731\&s=price-asc-rank

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

Amazon domain to search. Prices are pinned to this marketplace's currency regardless of where the run exits from.

## `maxPages` (type: `integer`):

Amazon stops serving a next-page link after about 7 pages on a broad query; the actor stops on its own when a term runs out, so a high value costs nothing extra.

## `maxItemsPerTerm` (type: `integer`):

Hard cap on products emitted per search term. 0 means no cap.

## `includeSponsored` (type: `boolean`):

Sponsored placements are flagged with isSponsored either way. Turn this off to keep only organic results for a cleaner price distribution.

## `delaySeconds` (type: `integer`):

Pacing between requests. Amazon serves CAPTCHAs to traffic it dislikes.

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

Residential proxy strongly recommended. It also determines the delivery country, which is what makes Prime and delivery fields meaningful.

## Actor input object example

```json
{
  "searchTerms": [
    "yoga mat",
    "coffee maker"
  ],
  "marketplace": "com",
  "maxPages": 1,
  "maxItemsPerTerm": 0,
  "includeSponsored": true,
  "delaySeconds": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per product found on the search results pages.

# 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 = {
    "searchTerms": [
        "yoga mat",
        "coffee maker"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/amazon-product-search-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 = { "searchTerms": [
        "yoga mat",
        "coffee maker",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fanndev/amazon-product-search-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 '{
  "searchTerms": [
    "yoga mat",
    "coffee maker"
  ]
}' |
apify call fanndev/amazon-product-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fanndev/amazon-product-search-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/T4asA75PcjhLoRtlz/builds/ys6cfNcXkidvdo1Gw/openapi.json
