# Amazon Scraper - BSR Alerts, Time-Series & AI (`harvestlab/amazon-scraper`) Actor

The ONLY Amazon scraper with cross-run BSR time-series + real-time webhook alerts (Slack/Zapier/n8n). Prices, ratings, reviews & seller data across 11 marketplaces. AI competitive analysis via 5 LLM providers. $0.005/product — replaces $19-39/mo rank-alert SaaS.

- **URL**: https://apify.com/harvestlab/amazon-scraper.md
- **Developed by:** [Nick](https://apify.com/harvestlab) (community)
- **Categories:** E-commerce, AI, Lead generation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 Scraper - BSR Alerts, Time-Series & AI

Scrape Amazon product listings from search results or direct product URLs. Extract prices, ratings, reviews, images, seller info, Prime eligibility, Best Sellers Rank, and more. Supports 10+ Amazon marketplace domains worldwide including US, UK, Germany, France, Japan, and India.

**New in v2.0 — BSR alert webhooks.** Set `alertWebhookUrl` + `alertRankThreshold` (default 50) and the actor POSTs a compact JSON payload to your URL whenever a tracked ASIN's absolute rank change crosses the threshold. Pipe into Slack, Discord, Zapier, Make, n8n, or your own endpoint — real-time Amazon rank alerts with **no polling required**. Fire-and-forget (5s timeout, 1 retry, webhook errors never fail the run). Pay-per-hit at $0.002/successful webhook.

**v1.9 — Best Sellers Rank time-series tracking.** Flip on `trackBestsellerRank` and every scraped ASIN is annotated with `rank_now`, `rank_previous`, `rank_change`, `rank_peak`, and `rank_movement` ("rising" / "falling" / "steady" / "new"). Pair with Apify's scheduler to build a persistent BSR history per ASIN — no database needed. Cross-run state lives in a named Apify key-value store (one snapshot slot per Amazon domain). Combined with AI analysis, the report switches to a rank-movement narrator that explains WHY products are climbing or falling.

Optionally generate AI-powered competitive pricing analysis with market positioning, price sweet spots, brand comparisons, and actionable insights for sellers, buyers, and market researchers.

### Features

- **Search scraping**: Enter any keyword and scrape product results with automatic pagination across multiple pages
- **Direct URL scraping**: Provide specific Amazon product URLs for detailed extraction including description, features, and seller info
- **10+ Amazon domains**: amazon.com, amazon.co.uk, amazon.de, amazon.fr, amazon.it, amazon.es, amazon.ca, amazon.co.jp, amazon.com.au, amazon.nl, amazon.in — scrape any marketplace with localized pricing and availability
- **Rich product data**: Title, price, original price, rating, review count, brand, Prime status, images, badges, delivery info
- **Detail page extraction**: When scraping direct URLs, get full product descriptions, bullet-point features, availability status, seller info, and category breadcrumbs
- **Multi-language support**: Rating and review count parsing works across English, German, French, Spanish, Italian, Dutch, and Japanese Amazon pages
- **European decimal-comma parsing**: Prices like `19,99 €` or `1.299,50 €` (amazon.de, .fr, .it, .es, .nl) are correctly normalized to floats (19.99, 1299.50)
- **Dynamic currency detection**: The `currency` field reflects the currency actually displayed on the page, not just the domain default — useful when proxy geo differs from the marketplace
- **Best Sellers Rank extraction**: When `fetchDetails: true`, each product emits `bestseller_rank` (integer) and `bestseller_category` — useful for tracking category-level positioning over time
- **BSR time-series tracking (v1.9+)**: `trackBestsellerRank: true` persists each ASIN's rank across runs and emits `rank_now`, `rank_previous`, `rank_change`, `rank_peak`, `rank_movement` deltas on every scraped item, plus a per-run `rank_movement_summary` item with top-5 climbers + top-5 fallers. Cross-run state is scoped per Amazon domain
- **BSR alert webhooks (new in v2.0)**: `alertWebhookUrl` + `alertRankThreshold` (default 50) fire an HTTP POST per ASIN whose `|rank_change| >= threshold`. Payload includes ASIN, title, URL, marketplace, rank_previous/now/change/pct, movement, peak, category, price, and scraped_at. Fire-and-forget with 5s timeout + 1 retry + `X-Actor-Run-Id` header for traceability. Webhook failures never fail the run
- **Hi-res gallery images**: Detail pages emit an `images` list of up to 12 alt-view / swatch images, upscaled to the master asset by stripping CDN size specifiers
- **Anti-bot resilience**: Browser TLS fingerprint impersonation via curl_cffi with residential proxy rotation per request, plus WAF-retry logic on search result pages
- **AI competitive analysis**: Market overview, pricing strategy recommendations, brand positioning, best-value rankings, and opportunity identification
- **Multi-LLM support**: Choose between OpenRouter (recommended — 300+ models), Anthropic (Claude), Google AI (Gemini), OpenAI (GPT), or Ollama (self-hosted) for AI analysis
- **Pay-per-result pricing**: Only pay for products successfully scraped — no upfront costs

### Use Cases

- **E-commerce sellers**: Monitor competitor pricing, track price changes, identify pricing sweet spots
- **Market researchers**: Analyze product categories, brand distribution, and market saturation
- **Dropshippers**: Find profitable products by comparing prices across Amazon marketplaces
- **Brand managers**: Track your products' ratings and reviews alongside competitors
- **Affiliate marketers**: Discover high-rated products with strong review counts for content creation
- **Price comparison tools**: Feed Amazon pricing data into comparison engines or dashboards

### Data Extracted

#### Search Results

Each product from search results includes:

| Field | Description |
|-------|-------------|
| `asin` | Amazon Standard Identification Number |
| `title` | Product title |
| `price` | Current selling price |
| `original_price` | Original/list price (if discounted) |
| `discount_percentage` | Computed discount percent when both prices are present |
| `currency` | Currency code as displayed on the page (USD, EUR, GBP, JPY, etc.) — reflects rendered page, not domain-assumed value |
| `rating` | Average customer rating (out of 5) |
| `reviews_count` | Number of customer reviews |
| `is_prime` | Whether the product is Prime eligible |
| `is_sponsored` | Whether the listing is a sponsored ad |
| `in_stock` | Boolean — whether the item is currently purchasable |
| `image_url` | Main product image URL |
| `product_url` | Direct link to the product page |
| `brand` | Brand or manufacturer name |
| `badge` | Special badges (Best Seller, Amazon's Choice) |
| `delivery_info` | Delivery date information |
| `page_number` | Which search result page the product appeared on |

#### Product Detail Pages

When scraping direct product URLs or enabling `fetchDetails: true` on search, additional fields are extracted:

| Field | Description |
|-------|-------------|
| `description` | Full product description text (cleaned of brand-story navigation chrome) |
| `features` | Bullet-point feature list |
| `availability` | Stock availability status |
| `seller` | Third-party seller name (if not Amazon) |
| `category` | Product category breadcrumb path |
| `images` | List of hi-res gallery image URLs (up to 12, alt views + zoom swatches) |
| `bestseller_rank` | Integer — Amazon's "Best Sellers Rank" position in its category (lowest wins) |
| `bestseller_category` | Category label associated with `bestseller_rank` |

#### BSR Time-Series Fields (v1.9+)

When `trackBestsellerRank: true`, these fields are added to every product:

| Field | Description |
|-------|-------------|
| `rank_now` | Integer — current BSR (same value as `bestseller_rank`, exposed for time-series consumers) |
| `rank_previous` | Integer — BSR from the previous run of this ASIN on this domain (`null` on first run) |
| `rank_change` | Integer — `rank_previous - rank_now`. Positive = product climbed (e.g. +250 means moved from #500 to #250). Negative = product fell |
| `rank_peak` | Integer — best (lowest-number) rank ever observed for this ASIN in the snapshot history |
| `rank_movement` | String — `"new"` (no prior data), `"rising"` (rank improved ≥10%), `"falling"` (rank worsened ≥10%), `"steady"` (<10% change) |
| `rank_previous_scraped_at` | ISO timestamp — when the previous snapshot was taken |
| `hours_since_last_snapshot` | Float — hours between the previous snapshot and this run |

Plus a single `rank_movement_summary` rollup item pushed at the end of each run (when a prior snapshot exists):

```json
{
    "type": "rank_movement_summary",
    "amazon_domain": "amazon.com",
    "search_query": "air fryer",
    "snapshot_at": "2026-04-23T12:00:00+00:00",
    "previous_snapshot_at": "2026-04-22T12:00:00+00:00",
    "climbers_count": 3,
    "fallers_count": 2,
    "top_climbers": [{"asin": "B0C...", "rank_previous": 812, "rank_now": 417, "rank_change": 395, "title": "...", "bestseller_category": "..."}],
    "top_fallers":  [{"asin": "B0D...", "rank_previous": 42,  "rank_now": 210, "rank_change": -168, "title": "...", "bestseller_category": "..."}]
}
````

#### Sample Output

```json
{
    "asin": "B0CHX3QBCH",
    "title": "Apple iPhone 15 Pro, 128GB, Natural Titanium",
    "price": 999.00,
    "original_price": 1099.00,
    "discount_percentage": 9.1,
    "currency": "USD",
    "rating": 4.6,
    "reviews_count": 2431,
    "is_prime": true,
    "is_sponsored": false,
    "in_stock": true,
    "image_url": "https://m.media-amazon.com/images/I/81Os1SBkFpL._AC_SL1500_.jpg",
    "product_url": "https://www.amazon.com/dp/B0CHX3QBCH",
    "brand": "Apple",
    "badge": "Amazon's Choice",
    "delivery_info": "FREE delivery Tomorrow",
    "availability": "In Stock",
    "seller": "Amazon.com",
    "features": ["6.1-inch Super Retina XDR display", "A17 Pro chip", "48MP Main camera", "Titanium design"],
    "category": "Electronics > Cell Phones & Accessories > Unlocked Cell Phones",
    "description": "The iPhone 15 Pro features a titanium design with the A17 Pro chip...",
    "amazon_domain": "amazon.com",
    "search_query": "iphone 15 pro",
    "scraped_at": "2026-04-18T10:15:00Z"
}
```

### Input Configuration

#### Search Parameters

- **Search Query** (`searchQuery`): Keywords to search for (e.g., "wireless headphones", "laptop stand", "organic coffee")
- **Product URLs** (`productUrls`): Direct Amazon product URLs — provide specific ASINs or product pages for detailed extraction. Overrides search query when provided
- **Amazon Domain** (`amazonDomain`): Which Amazon marketplace to scrape. Options: amazon.com (default), amazon.co.uk, amazon.de, amazon.fr, amazon.it, amazon.es, amazon.ca, amazon.co.jp, amazon.com.au, amazon.nl, amazon.in
- **Max Products** (`maxProducts`): Maximum number of products to scrape (1-500, default: 25). Higher values will scrape across multiple search result pages
- **Sort By** (`sortBy`): Sort order for search results — relevance (default), price-asc, price-desc, avg-rating, newest

#### AI Analysis Settings

- **Enable AI Analysis** (`enableAiAnalysis`): Toggle AI-powered competitive analysis on or off (default: off). Requires an LLM API key when enabled
- **LLM Provider** (`llmProvider`): Choose between OpenRouter (recommended — 300+ models), Anthropic (Claude), Google AI (Gemini), OpenAI (GPT), or Ollama (self-hosted)
- **OpenRouter API Key** (`openrouterApiKey`): Your API key from openrouter.ai. Required when using OpenRouter provider
- **Anthropic API Key** (`anthropicApiKey`): Your API key from console.anthropic.com. Required when using Anthropic provider
- **Model Override** (`llmModel`): Optional custom model selection. Leave empty for defaults (Gemini 2.0 Flash for OpenRouter, Claude Sonnet for Anthropic, Gemini for Google AI, GPT-4o Mini for OpenAI, Llama 3.1 for Ollama)

#### Proxy Settings

**For international Amazon domains (`amazon.it`, `amazon.es`, `amazon.co.uk`, `amazon.co.jp`, etc.), RESIDENTIAL proxy is strongly recommended** — Amazon's AWS WAF frequently blocks datacenter IPs on these marketplaces. Datacenter proxies work reliably only for `amazon.com`.

The actor uses browser TLS fingerprint impersonation (curl\_cffi) combined with per-request proxy rotation and automatic WAF-retry on search result pages, but residential IPs substantially improve success rates across all domains.

**amazon.fr WAF limitation**: `amazon.fr` enforces a noticeably stricter AWS WAF than other EU domains (`.de`, `.co.uk`, `.it` all work reliably with generic residential proxies). Expect a higher block rate on search queries even with `apifyProxyCountry: "FR"` — the WAF often returns a JavaScript challenge page (`awsWafCookieDomainList` / `gokuProps`) on the `/s?k=...` endpoint regardless of proxy country. Direct product URL scraping (`/dp/ASIN`) succeeds more consistently than search. Recommendation: retry the run, use direct product URLs where possible, or fall back to another EU marketplace (`amazon.de`, `amazon.co.uk`) for equivalent catalog data.

**amazon.co.jp geo limitation**: When using `amazon.co.jp` with non-Japanese proxies (EU/US residential IPs), Amazon serves a US- or EU-localized version of the page. Prices will appear in USD or EUR — not JPY. For true JPY results, request a Japanese residential IP via `apifyProxyCountry: "JP"` in your proxy configuration.

### AI Competitive Analysis

When enabled, the AI analysis examines all scraped products and generates a structured report including:

- **Market Overview**: Price ranges, competition intensity, market maturity assessment, category saturation
- **Price Analysis**: Sweet spot ranges where most products cluster, premium and budget thresholds, pricing strategy recommendations for new entrants
- **Top 5 Best Value**: Products ranked by a composite value score (price × rating × reviews) with detailed reasoning for each pick
- **Brand Analysis**: Market share distribution across brands, positioning strategies, brand strength indicators
- **Opportunities**: Underserved price points, feature gaps in the market, specific actionable recommendations for sellers and product developers

The analysis is returned as structured JSON alongside the product data, making it easy to integrate into dashboards, reports, or automated workflows.

### Example Usage

#### Search for products

```json
{
    "searchQuery": "wireless headphones",
    "amazonDomain": "amazon.com",
    "maxProducts": 25,
    "sortBy": "relevance",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

#### Scrape specific products

```json
{
    "productUrls": [
        "https://www.amazon.com/dp/B0C8S5Y3KV",
        "https://www.amazon.com/dp/B09XS7JWHH"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

#### Search with AI analysis

```json
{
    "searchQuery": "standing desk",
    "amazonDomain": "amazon.com",
    "maxProducts": 50,
    "enableAiAnalysis": true,
    "llmProvider": "openrouter",
    "openrouterApiKey": "sk-or-...",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

#### Track Best Sellers Rank over time (v1.9+)

Run once per day on a schedule and each ASIN will emit its rank delta vs. the previous run:

```json
{
    "searchQuery": "air fryer",
    "amazonDomain": "amazon.com",
    "maxProducts": 50,
    "trackBestsellerRank": true,
    "enableAiAnalysis": true,
    "llmProvider": "openrouter",
    "openrouterApiKey": "sk-or-...",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US"
    }
}
```

First run: every ASIN emits `rank_movement: "new"` and null deltas (baseline).
Second run onward: every ASIN emits `rank_change`, `rank_movement`, and a historical `rank_peak`. The AI report switches to a rank-movement narrator that lists the top climbers/fallers with plausible drivers.

#### Real-time BSR alerts via webhook (v2.0+)

Combine BSR tracking with a webhook URL to get an HTTP POST the moment any ASIN climbs or falls past your threshold — no polling required. Works with Slack incoming webhooks, Zapier catch hooks, Make custom webhooks, n8n, Discord, or any endpoint that accepts JSON.

```json
{
    "searchQuery": "mechanical keyboard",
    "amazonDomain": "amazon.com",
    "maxProducts": 25,
    "trackBestsellerRank": true,
    "alertWebhookUrl": "https://hooks.zapier.com/hooks/catch/12345/abcde",
    "alertRankThreshold": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US"
    }
}
```

Webhook payload shape (one POST per qualifying ASIN):

```json
{
    "asin": "B0CHX3QBCH",
    "title": "Keychron Q1 Pro QMK/VIA Wireless Mechanical Keyboard",
    "url": "https://www.amazon.com/dp/B0CHX3QBCH",
    "marketplace": "amazon.com",
    "rank_previous": 812,
    "rank_now": 417,
    "rank_change": 395,
    "rank_change_pct": 48.65,
    "rank_movement": "rising",
    "rank_peak": 390,
    "bestseller_category": "Electronics > Computer Keyboards",
    "price": 199.00,
    "currency": "USD",
    "hours_since_last_snapshot": 24.03,
    "scraped_at": "2026-04-23T12:00:00+00:00"
}
```

Each webhook includes an `X-Actor-Run-Id` header so you can correlate alerts with the run in the Apify Console. Only ASINs whose **absolute** `rank_change` is ≥ `alertRankThreshold` fire (both climbers and fallers). Fire-and-forget with a 5-second timeout and 1 retry on failure — the run never fails because of an unreachable webhook. Only successful (2xx) dispatches are billed at `$0.002/alert` via the `alert-dispatched` event. A `bsr_alert_dispatch_summary` audit row is pushed to the dataset showing exactly what fired and what failed.

#### Scrape German Amazon

```json
{
    "searchQuery": "Kopfhörer kabellos",
    "amazonDomain": "amazon.de",
    "maxProducts": 25,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

### Pricing

This actor uses pay-per-event pricing:

| Event | Price | Description |
|-------|-------|-------------|
| Product scraped | $0.005 | Charged for each product successfully scraped with data |
| Rank tracked | $0.01 | Charged for each ASIN annotated with BSR time-series deltas (only when `trackBestsellerRank: true`) |
| Alert dispatched | $0.002 | Charged for each BSR-alert webhook successfully POSTed (2xx response). Failed webhooks are NOT charged |
| AI analysis completed | $0.05 | Charged once when AI competitive analysis is generated |

Example: scraping 100 products with AI analysis costs $0.005 × 100 + $0.05 = $0.55.

Example with BSR tracking: scraping 100 products, of which 60 have a valid BSR, with AI rank-movement analysis, costs $0.005 × 100 + $0.01 × 60 + $0.05 = $1.15.

Example with BSR alerts: a 25-product daily watchlist where on average 3 ASINs/day cross the threshold and fire webhooks costs $0.005 × 25 + $0.01 × 25 + $0.002 × 3 = $0.381/day ≈ $11.43/month — vs. $19-39/month flat for RankBlaze / Keepa rank alerts, and you keep the raw catalog data.

### Technical Details

- Uses `curl_cffi` with Chrome 120 TLS fingerprint impersonation to bypass Amazon's bot detection
- Creates a fresh HTTP session with new proxy IP for each request, maximizing anonymity
- Polite crawling with 2-4 second randomized delays between requests
- Exponential backoff with jitter on 403/429/503 errors — up to 4 retries per page
- AWS WAF detection on search pages triggers automatic retry on a fresh proxy IP
- CAPTCHA detection with automatic retry on a fresh proxy IP
- Multi-strategy parsing: search result cards (`data-component-type="s-search-result"`) and product detail pages
- Multi-language rating parsing handles "out of 5", "von 5", "sur 5", "de 5" patterns
- European decimal-comma price parsing: `1.299,50 €` → 1299.50, `19,99 €` → 19.99 (vs. US `1,299.50` → 1299.50)
- Brand extraction filters out badge text (Amazon's Choice, Best Seller) to avoid false positives

### Supported Amazon Domains

| Domain | Country | Currency |
|--------|---------|----------|
| `amazon.com` | United States | USD |
| `amazon.co.uk` | United Kingdom | GBP |
| `amazon.de` | Germany | EUR |
| `amazon.fr` | France | EUR |
| `amazon.it` | Italy | EUR |
| `amazon.es` | Spain | EUR |
| `amazon.co.jp` | Japan | JPY |
| `amazon.ca` | Canada | CAD |
| `amazon.com.au` | Australia | AUD |
| `amazon.in` | India | INR |
| `amazon.nl` | Netherlands | EUR |

Each domain returns results in the local currency with localized product listings. The scraper automatically handles multi-language rating formats and European decimal-comma price notation. The `currency` field in each output item reflects the currency actually rendered on the page — if the proxy geo does not match the domain (e.g. EU/US IP hitting `amazon.co.jp`), Amazon may localize the page and prices will be in the proxy's currency rather than the domain's native currency.

### Output Example

```json
{
    "asin": "B0C8S5Y3KV",
    "title": "Sony WH-1000XM5 Wireless Noise Cancelling Headphones",
    "product_url": "https://www.amazon.com/dp/B0C8S5Y3KV",
    "price": 278.00,
    "original_price": 399.99,
    "discount_percentage": 30.5,
    "currency": "USD",
    "rating": 4.6,
    "reviews_count": 12345,
    "brand": "Sony",
    "image_url": "https://m.media-amazon.com/images/I/51aXvjzcukL._AC_SL1500_.jpg",
    "is_prime": true,
    "is_sponsored": false,
    "in_stock": true,
    "badge": "Amazon's Choice",
    "delivery_info": "FREE delivery Tomorrow",
    "amazon_domain": "amazon.com",
    "search_query": "noise cancelling headphones",
    "scraped_at": "2026-04-18T10:30:00Z"
}
```

### Integration & Export

Export data in multiple formats directly from Apify:

- **JSON** — structured data for APIs and databases
- **CSV** — import into Google Sheets, Excel, or BI tools
- **Excel** — ready for business reporting and analysis

Connect with 2,000+ apps via Apify integrations: Google Sheets, Slack, Zapier, Make (Integromat), webhooks, and Amazon S3. Schedule recurring runs to track prices automatically.

### Frequently Asked Questions

**How does this compare to other Amazon scrapers on Apify Store?**

| Scraper | $/1,000 products | AI analysis | BSR snapshot | **BSR time-series** | **BSR webhooks** | Gallery images | Multi-LLM |
|---|---|---|---|---|---|---|---|
| **This actor** | **$5.00** | **YES (5 providers inc. self-hosted Ollama)** | YES | **YES (unique)** | **YES (unique)** | YES | YES |
| `junglee/amazon-crawler` | $3.00 | no | yes | no | no | yes | n/a |
| `junglee/free-amazon-product-scraper` | $6.20 | no | yes | no | no | yes | n/a |
| `automation-lab/amazon-scraper` | $2-4 tiered | no (MCP only) | yes | no | no | limited | n/a |

Every other Amazon scraper on the Store ships raw data — a point-in-time BSR at best. This actor is the only Amazon scraper on the Store with **cross-run BSR time-series tracking AND real-time BSR alert webhooks**: schedule it to run daily and every ASIN emits `rank_change`, `rank_peak`, and `rank_movement` vs. the previous run, plus an optional HTTP POST fires the moment an ASIN crosses your user-defined threshold. Specter, Tracxn, and the big product-intelligence platforms charge $10k+/month for this kind of longitudinal signal; RankBlaze and Keepa charge $19-39/month flat for rank alerts only. Paired with the 5-LLM AI layer (OpenRouter / Anthropic / Google / OpenAI / Ollama self-hosted), this is the only Amazon actor that converts raw catalog data into structured market-momentum intelligence + real-time Slack/Zapier/webhook alerts in a single call.

**Why do I need a residential proxy?**
Amazon's AWS WAF aggressively blocks datacenter IPs, particularly on international marketplaces (`amazon.it`, `amazon.es`, `amazon.co.uk`, `amazon.co.jp`). Residential proxies rotate through real consumer IPs, making requests appear as normal browser traffic. Datacenter works reliably only on `amazon.com`; on other domains expect frequent 503 or CAPTCHA responses without residential.

**Why is amazon.fr failing or returning no products?**
`amazon.fr` runs a stricter AWS WAF than other EU marketplaces. Search queries (`/s?k=...`) are frequently blocked with a JS challenge page even with `apifyProxyCountry: "FR"` residential IPs — all 4 retry attempts may fail. Workarounds: (1) retry the run (WAF posture varies by hour and IP batch), (2) scrape direct product URLs (`/dp/ASIN`) which succeed more reliably than search, or (3) use `amazon.de` / `amazon.co.uk` / `amazon.it` which share much of the same EU catalog and work without issue.

**Why are my amazon.co.jp prices in USD/EUR instead of JPY?**
Amazon localizes pages based on the requesting IP. If your proxy is an EU or US residential IP, Amazon serves the `.co.jp` domain with localized pricing in your IP's currency. Set `apifyProxyCountry: "JP"` in your proxy configuration to get a Japanese residential IP and genuine JPY results. The `currency` field in each output item always reflects what was actually rendered on the page.

**I requested amazon.com but got EUR prices — why?**
Same root cause as the `.co.jp` question above, and the actor now logs a warning or INFO note when it detects a likely mismatch. Amazon serves localized prices based on the visitor IP, not the domain. A residential proxy with no explicit country auto-selects an exit IP that may be anywhere in the world — if it exits in Europe while you're scraping `amazon.com`, Amazon renders EUR. Match the proxy country to your target domain for consistent currency: `apifyProxyCountry=US` for amazon.com, `GB` for amazon.co.uk, `DE` for amazon.de, `FR` for amazon.fr, `IT` for amazon.it, `ES` for amazon.es, `CA` for amazon.ca, `JP` for amazon.co.jp, `AU` for amazon.com.au, `NL` for amazon.nl, `IN` for amazon.in.

**How many products can I scrape per run?**
Up to ~112 products per search query (Amazon limits pagination to ~7 pages). For more products, use multiple search queries or scrape specific product URLs directly.

**Can I scrape product reviews?**
Search results include the star rating and review count. Full review text is available when scraping direct product URLs but not from search result pages.

**How does the AI analysis work?**
When enabled, the AI examines all scraped products and generates a competitive analysis report covering price positioning, brand landscape, market gaps, and buying recommendations. Requires an API key from OpenRouter, Anthropic, Google AI, or OpenAI (or a self-hosted Ollama instance). When `trackBestsellerRank` is ALSO on and a prior snapshot exists, the AI switches to a rank-movement analyst that narrates why products are climbing or falling (top climbers, top fallers, emerging/losing brands, recommended actions).

**How does BSR time-series tracking work under the hood?**
When `trackBestsellerRank: true`, the actor opens a named Apify key-value store (`amazon-bestseller-rank-history`) that persists across runs. On each run it reads the prior snapshot for the current domain (one slot per marketplace — `amazon.com` BSR #5 is unrelated to `amazon.co.uk` BSR #5), annotates each scraped product with rank-delta fields, and writes a new snapshot back. The first run establishes the baseline (all products emit `rank_movement: "new"` with null deltas); the second run onward emits real deltas. Products that drop off the search page but had prior data are carried forward in the snapshot (up to 500 stale ASINs) so they can still emit deltas when they re-appear. Schedule the actor to run once per day or per week via Apify Console → Schedules to build a real longitudinal rank history.

**Does BSR tracking work for direct product URLs too?**
Yes. The domain + ASIN key is the same whether the ASIN came from a search query or a direct product URL, so you can mix modes across runs and still get consistent deltas. First run builds the baseline; second run onward emits deltas.

**How do BSR alert webhooks work, and what happens if my endpoint is down?**
When `alertWebhookUrl` is set (and `trackBestsellerRank` is on — it's auto-enabled if you forget), every product whose **absolute** `rank_change` meets or exceeds `alertRankThreshold` (default 50) triggers an HTTP POST to your URL. Each webhook has a 5-second timeout and up to 1 retry; on permanent failure the scraper logs a warning and continues — your run **never fails** because of a bad webhook. Only successful 2xx dispatches are billed. A `bsr_alert_dispatch_summary` audit item is pushed to the dataset with per-webhook results so you always know what fired. A per-run cap of 250 webhooks prevents runaway misconfigurations. Each request carries an `X-Actor-Run-Id` header for traceability back to the Apify run.

**When do webhooks NOT fire?**
On the first run for a given marketplace (no prior snapshot → no rank\_change to evaluate), when `trackBestsellerRank` is disabled, when no ASIN crosses the threshold, or when the URL isn't `http://` / `https://`. Webhooks only fire on "second run onward" once a baseline exists.

### Limitations

- Amazon aggressively blocks automated access. Even with residential proxies, some requests may be blocked during high-traffic periods
- `amazon.fr` has a noticeably stricter AWS WAF than other EU Amazon domains — expect a higher block rate on search queries, even with `apifyProxyCountry: "FR"`. Use direct product URLs or fall back to `amazon.de` / `.co.uk` / `.it` for equivalent catalogs
- Product availability and pricing may vary by region, login status, and Prime membership
- Review text content is not scraped from search results (only rating and count). Use direct product URLs for richer data
- Maximum ~7 pages of search results per query (~112 products) due to Amazon's pagination limits
- Prices shown are the default listing price; Lightning Deals, coupons, and Subscribe & Save discounts may not be captured
- Some Amazon domains may have different HTML structures that affect extraction accuracy

### Legal and Compliance

This actor is provided as a technical tool. Users are responsible for ensuring their use complies with:

- Amazon's Terms of Service and Conditions of Use
- Applicable data protection laws (GDPR, CCPA, etc.)
- Local laws regarding web scraping and data collection

Amazon's Terms of Service restrict automated access to their platform. Use this tool responsibly and at your own risk. This actor does not store or process personal data — it only extracts publicly visible product catalog information.

The developer of this actor is not responsible for any misuse or legal consequences arising from its use.

# Actor input Schema

## `searchQuery` (type: `string`):

Search keyword(s) to find products on Amazon (e.g., 'iPhone 15 Pro', 'mechanical keyboard', 'air fryer').

## `query` (type: `string`):

CLI alias for searchQuery. Hidden from Console form.

## `q` (type: `string`):

CLI alias for searchQuery. Hidden from Console form.

## `search` (type: `string`):

CLI alias for searchQuery. Hidden from Console form.

## `keyword` (type: `string`):

CLI alias for searchQuery. Hidden from Console form.

## `searchTerm` (type: `string`):

CLI alias for searchQuery. Hidden from Console form.

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

Direct Amazon product URLs to scrape (alternative to search). Example: `https://www.amazon.com/dp/B0C7BZR4DC` (the B0C7BZR4DC is the ASIN). Overrides search query if both are provided.

## `urls` (type: `array`):

CLI alias for productUrls. Hidden from Console form.

## `links` (type: `array`):

CLI alias for productUrls. Hidden from Console form.

## `amazonDomain` (type: `string`):

Which Amazon country marketplace to scrape. Different marketplaces have different ASINs, pricing in local currency, and product availability.

## `domain` (type: `string`):

CLI alias for amazonDomain. Hidden from Console form.

## `country_domain` (type: `string`):

CLI alias for amazonDomain. Hidden from Console form.

## `maxProducts` (type: `integer`):

Maximum number of products to scrape. Amazon shows ~20 per search page. Start with 25 to test before running larger jobs.

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

Alternative name for Max Products — accepts the same values. Use either maxProducts or maxItems.

## `sortBy` (type: `string`):

Sort order for search results. 'Relevance' (default) is Amazon's algorithmic ranking mixing sales, reviews, and keyword match. 'Price Low/High' sorts by current list price. 'Avg. Customer Review' ranks by star rating (biased toward items with many reviews). 'Newest Arrivals' surfaces recently listed products — best for trend-spotting but noisy. Only applies when using a search query, not direct product URLs.

## `fetchDetails` (type: `boolean`):

Fetch individual product detail pages to enrich results with brand, availability, description, features, and seller info. Adds ~2-3s per product but significantly improves data quality. Set to false for fast search-page-only scraping.

## `includeDetails` (type: `boolean`):

CLI alias for fetchDetails. Hidden from Console form.

## `trackBestsellerRank` (type: `boolean`):

Persist each product's Best Sellers Rank across runs and emit rank\_now / rank\_previous / rank\_change / rank\_peak / rank\_movement fields on every scraped item. Pair with Apify scheduler to build a BSR history per ASIN. Forces fetchDetails=true. Adds a per-ASIN `rank-tracked` charge ($0.01) on top of the normal product-scraped charge. When combined with Enable AI Analysis, the AI report switches to a rank-movement narrative (why products are climbing or falling). First run establishes the baseline; second run onward emits deltas.

## `alertWebhookUrl` (type: `string`):

POST a compact JSON payload to this URL whenever a tracked ASIN's absolute rank\_change crosses the threshold below. Turns the actor into a real-time BSR alert service — pipe into Slack/Discord/Zapier/Make/n8n/your own endpoint. Fire-and-forget: 5s timeout, 1 retry on failure, webhook errors never fail the run. Only fires on second-run-onward (needs a prior snapshot for deltas). Setting this automatically enables trackBestsellerRank. Adds an `alert-dispatched` charge ($0.002) per successful webhook. Leave empty to disable.

## `alertRankThreshold` (type: `integer`):

Minimum absolute rank\_change required to fire a webhook. Default 50 means a climb from #200 to #150 (or a drop from #100 to #151) triggers an alert. Lower = more alerts, higher = noisier movers only. Only applies when alertWebhookUrl is set.

## `enableAiAnalysis` (type: `boolean`):

Run AI-powered competitive pricing analysis on scraped products. Requires an LLM API key. When Track Best Sellers Rank is also ON, the AI switches to a rank-movement report (narrates why products are climbing/falling) instead of the default pricing analysis.

## `llmProvider` (type: `string`):

Which AI provider to use for analysis.

## `openrouterApiKey` (type: `string`):

API key for OpenRouter (openrouter.ai). Required if using OpenRouter provider.

## `anthropicApiKey` (type: `string`):

API key for Anthropic Claude (console.anthropic.com). Required if using Anthropic provider.

## `googleApiKey` (type: `string`):

API key for Google AI (Gemini). Get one at aistudio.google.com/app/apikey

## `openaiApiKey` (type: `string`):

API key from platform.openai.com (required if using OpenAI provider)

## `ollamaBaseUrl` (type: `string`):

Base URL for Ollama API. Default: http://localhost:11434

## `llmModel` (type: `string`):

Specific model to use. Leave empty for the provider default (google/gemini-2.0-flash-001 for OpenRouter, claude-sonnet-4-20250514 for Anthropic, gemini-2.0-flash for Google AI, gpt-4o-mini for OpenAI, llama3.1 for Ollama).

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

Proxy settings. Residential proxies recommended for Amazon. Tip: for consistent pricing/currency, match the proxy country to your target amazonDomain (e.g. `apifyProxyCountry=US` for amazon.com, `DE` for amazon.de, `JP` for amazon.co.jp) — Amazon localizes prices by visitor IP.

## Actor input object example

```json
{
  "searchQuery": "iPhone 15 Pro",
  "amazonDomain": "amazon.com",
  "maxProducts": 25,
  "sortBy": "relevance",
  "fetchDetails": true,
  "trackBestsellerRank": false,
  "alertRankThreshold": 50,
  "enableAiAnalysis": false,
  "llmProvider": "openrouter",
  "ollamaBaseUrl": "http://localhost:11434",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "searchQuery": "iPhone 15 Pro",
    "ollamaBaseUrl": "http://localhost:11434",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("harvestlab/amazon-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 = {
    "searchQuery": "iPhone 15 Pro",
    "ollamaBaseUrl": "http://localhost:11434",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("harvestlab/amazon-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQuery": "iPhone 15 Pro",
  "ollamaBaseUrl": "http://localhost:11434",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call harvestlab/amazon-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=harvestlab/amazon-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Amazon Scraper - BSR Alerts, Time-Series & AI",
        "description": "The ONLY Amazon scraper with cross-run BSR time-series + real-time webhook alerts (Slack/Zapier/n8n). Prices, ratings, reviews & seller data across 11 marketplaces. AI competitive analysis via 5 LLM providers. $0.005/product — replaces $19-39/mo rank-alert SaaS.",
        "version": "2.0",
        "x-build-id": "ktBYgmrudEZqO6Fc3"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/harvestlab~amazon-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-harvestlab-amazon-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/harvestlab~amazon-scraper/runs": {
            "post": {
                "operationId": "runs-sync-harvestlab-amazon-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/harvestlab~amazon-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-harvestlab-amazon-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchQuery": {
                        "title": "Search Query",
                        "type": "string",
                        "description": "Search keyword(s) to find products on Amazon (e.g., 'iPhone 15 Pro', 'mechanical keyboard', 'air fryer')."
                    },
                    "query": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "q": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "search": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "keyword": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "searchTerm": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "productUrls": {
                        "title": "Product URLs",
                        "type": "array",
                        "description": "Direct Amazon product URLs to scrape (alternative to search). Example: `https://www.amazon.com/dp/B0C7BZR4DC` (the B0C7BZR4DC is the ASIN). Overrides search query if both are provided.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "urls": {
                        "title": "Product URLs (CLI alias)",
                        "type": "array",
                        "description": "CLI alias for productUrls. Hidden from Console form.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "links": {
                        "title": "Product URLs (CLI alias)",
                        "type": "array",
                        "description": "CLI alias for productUrls. Hidden from Console form.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "amazonDomain": {
                        "title": "Amazon Domain",
                        "enum": [
                            "amazon.com",
                            "amazon.co.uk",
                            "amazon.de",
                            "amazon.fr",
                            "amazon.it",
                            "amazon.es",
                            "amazon.ca",
                            "amazon.co.jp",
                            "amazon.com.au",
                            "amazon.nl",
                            "amazon.in"
                        ],
                        "type": "string",
                        "description": "Which Amazon country marketplace to scrape. Different marketplaces have different ASINs, pricing in local currency, and product availability.",
                        "default": "amazon.com"
                    },
                    "domain": {
                        "title": "Amazon Domain (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for amazonDomain. Hidden from Console form."
                    },
                    "country_domain": {
                        "title": "Amazon Domain (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for amazonDomain. Hidden from Console form."
                    },
                    "maxProducts": {
                        "title": "Max Products",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Maximum number of products to scrape. Amazon shows ~20 per search page. Start with 25 to test before running larger jobs.",
                        "default": 25
                    },
                    "maxItems": {
                        "title": "Max Items (alias for Max Products)",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Alternative name for Max Products — accepts the same values. Use either maxProducts or maxItems."
                    },
                    "sortBy": {
                        "title": "Sort By",
                        "enum": [
                            "relevance",
                            "price_low",
                            "price_high",
                            "avg_review",
                            "newest"
                        ],
                        "type": "string",
                        "description": "Sort order for search results. 'Relevance' (default) is Amazon's algorithmic ranking mixing sales, reviews, and keyword match. 'Price Low/High' sorts by current list price. 'Avg. Customer Review' ranks by star rating (biased toward items with many reviews). 'Newest Arrivals' surfaces recently listed products — best for trend-spotting but noisy. Only applies when using a search query, not direct product URLs.",
                        "default": "relevance"
                    },
                    "fetchDetails": {
                        "title": "Fetch Product Details",
                        "type": "boolean",
                        "description": "Fetch individual product detail pages to enrich results with brand, availability, description, features, and seller info. Adds ~2-3s per product but significantly improves data quality. Set to false for fast search-page-only scraping.",
                        "default": true
                    },
                    "includeDetails": {
                        "title": "Include Product Details (alias)",
                        "type": "boolean",
                        "description": "CLI alias for fetchDetails. Hidden from Console form."
                    },
                    "trackBestsellerRank": {
                        "title": "Track Best Sellers Rank (time-series)",
                        "type": "boolean",
                        "description": "Persist each product's Best Sellers Rank across runs and emit rank_now / rank_previous / rank_change / rank_peak / rank_movement fields on every scraped item. Pair with Apify scheduler to build a BSR history per ASIN. Forces fetchDetails=true. Adds a per-ASIN `rank-tracked` charge ($0.01) on top of the normal product-scraped charge. When combined with Enable AI Analysis, the AI report switches to a rank-movement narrative (why products are climbing or falling). First run establishes the baseline; second run onward emits deltas.",
                        "default": false
                    },
                    "alertWebhookUrl": {
                        "title": "BSR Alert Webhook URL (optional)",
                        "type": "string",
                        "description": "POST a compact JSON payload to this URL whenever a tracked ASIN's absolute rank_change crosses the threshold below. Turns the actor into a real-time BSR alert service — pipe into Slack/Discord/Zapier/Make/n8n/your own endpoint. Fire-and-forget: 5s timeout, 1 retry on failure, webhook errors never fail the run. Only fires on second-run-onward (needs a prior snapshot for deltas). Setting this automatically enables trackBestsellerRank. Adds an `alert-dispatched` charge ($0.002) per successful webhook. Leave empty to disable."
                    },
                    "alertRankThreshold": {
                        "title": "BSR Alert Rank Threshold",
                        "minimum": 1,
                        "maximum": 1000000,
                        "type": "integer",
                        "description": "Minimum absolute rank_change required to fire a webhook. Default 50 means a climb from #200 to #150 (or a drop from #100 to #151) triggers an alert. Lower = more alerts, higher = noisier movers only. Only applies when alertWebhookUrl is set.",
                        "default": 50
                    },
                    "enableAiAnalysis": {
                        "title": "Enable AI Analysis",
                        "type": "boolean",
                        "description": "Run AI-powered competitive pricing analysis on scraped products. Requires an LLM API key. When Track Best Sellers Rank is also ON, the AI switches to a rank-movement report (narrates why products are climbing/falling) instead of the default pricing analysis.",
                        "default": false
                    },
                    "llmProvider": {
                        "title": "LLM Provider",
                        "enum": [
                            "openrouter",
                            "anthropic",
                            "google",
                            "openai",
                            "ollama"
                        ],
                        "type": "string",
                        "description": "Which AI provider to use for analysis.",
                        "default": "openrouter"
                    },
                    "openrouterApiKey": {
                        "title": "OpenRouter API Key",
                        "type": "string",
                        "description": "API key for OpenRouter (openrouter.ai). Required if using OpenRouter provider."
                    },
                    "anthropicApiKey": {
                        "title": "Anthropic API Key",
                        "type": "string",
                        "description": "API key for Anthropic Claude (console.anthropic.com). Required if using Anthropic provider."
                    },
                    "googleApiKey": {
                        "title": "Google AI API Key",
                        "type": "string",
                        "description": "API key for Google AI (Gemini). Get one at aistudio.google.com/app/apikey"
                    },
                    "openaiApiKey": {
                        "title": "OpenAI API Key",
                        "type": "string",
                        "description": "API key from platform.openai.com (required if using OpenAI provider)"
                    },
                    "ollamaBaseUrl": {
                        "title": "Ollama Base URL",
                        "type": "string",
                        "description": "Base URL for Ollama API. Default: http://localhost:11434"
                    },
                    "llmModel": {
                        "title": "LLM Model",
                        "type": "string",
                        "description": "Specific model to use. Leave empty for the provider default (google/gemini-2.0-flash-001 for OpenRouter, claude-sonnet-4-20250514 for Anthropic, gemini-2.0-flash for Google AI, gpt-4o-mini for OpenAI, llama3.1 for Ollama)."
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Proxy settings. Residential proxies recommended for Amazon. Tip: for consistent pricing/currency, match the proxy country to your target amazonDomain (e.g. `apifyProxyCountry=US` for amazon.com, `DE` for amazon.de, `JP` for amazon.co.jp) — Amazon localizes prices by visitor IP."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
