# Alternate Price Watch (`goobertje/alternate-price-watch`) Actor

Compare the same product across alternate.nl, alternate.be and alternate.at: per-country price, shipping and stock, cross-border savings, and alerts for price drops.

- **URL**: https://apify.com/goobertje/alternate-price-watch.md
- **Developed by:** [goober](https://apify.com/goobertje) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 product scrapeds

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Alternate price comparison API: NL, BE and AT prices and price-drop alerts

Compare the same product across **alternate.nl, alternate.be and alternate.at**, find the cheapest country, and get only the products whose price just dropped. It is built for resellers, system builders, deal communities and AI agents. You get one JSON row per product with all three country prices, shipping, stock, and the savings against your home country.

Alternate sells identical hardware (GPUs, CPUs, motherboards, monitors, peripherals) in three countries at different prices, with different stock and shipping. One SKU, three prices: this Actor puts them side by side and does the maths.

### Why use this Actor

- **Cross-country spread**: cheapest in-stock offer vs the second cheapest, vs the median, and vs your home country.
- **Delta mode**: remembers the last lowest price per SKU between runs and can return only new products or real price drops. Skipped products are not billed.
- **Public pages only**: reads Alternate's public product pages and sitemap, about 1 request per second, honest user agent, follows `robots.txt`.
- **No proxies, no browser**: plain HTTP requests, small and fast.
- **Agent-ready**: every input has a clear description and example, so Claude, Cursor, n8n or any MCP client can call it without extra setup.

### Quick start

Click **Start** with the prefilled input. It runs this:

```json
{
  "searchQueries": ["RTX 5070"],
  "maxItems": 5
}
```

You will see up to 5 graphics cards, each with the NL, BE and AT price, the cheapest country and the saving against the Netherlands. Allow about 6 seconds per product.

### What you get per product

| Field | Meaning |
|---|---|
| `sku`, `title`, `brand`, `category`, `mpn`, `ean[]` | Product identity (EAN is a list) |
| `offers[]` | One row per country: `shopName`, `priceEur`, `shippingEur`, `totalPriceEur`, `availability`, `inStock`, rating |
| `pricingSummary` | Lowest total (price + shipping), its country, median total, second-lowest total, country counts |
| `arbitrageAlpha` | `spreadVsSecondLowestEur`, `spreadVsMedianPct`, `savingVsHomeEur`, `savingVsHomePct` |
| `priceChange` | `isNew`, `previousLowestTotalPriceEur`, `dropPct` versus the last run |

Market prices are computed over **in-stock offers only**. An out-of-stock listing is never reported as "the cheapest". If nothing is in stock the summary fields are `null`.

#### Sample output (real run)

```json
{
  "sku": "100065780",
  "title": "Ryzen 9 9950X, 4,4 GHz (5,7 GHz Turbo Boost) socket AM5 processor",
  "brand": "AMD",
  "mpn": "100-100001277WOF",
  "ean": ["0730143315272"],
  "pricingSummary": {
    "lowestCountry": "at",
    "lowestPriceEur": 539.0,
    "lowestShippingPriceEur": 8.9,
    "lowestTotalPriceEur": 547.9,
    "medianTotalPriceEur": 549.0,
    "secondLowestTotalPriceEur": 549.0,
    "countryCount": 3,
    "inStockCountryCount": 3
  },
  "arbitrageAlpha": {
    "spreadVsSecondLowestEur": 1.1,
    "spreadVsMedianPct": 0.2,
    "homeCountry": "nl",
    "homeTotalPriceEur": 549.0,
    "savingVsHomeEur": 1.1,
    "savingVsHomePct": 0.2
  },
  "priceChange": { "isNew": true, "previousLowestTotalPriceEur": null, "dropPct": null },
  "offers": [
    { "country": "nl", "priceEur": 549.0, "shippingEur": 0.0,  "totalPriceEur": 549.0,  "availability": "InStock" },
    { "country": "be", "priceEur": 549.0, "shippingEur": 6.99, "totalPriceEur": 555.99, "availability": "InStock" },
    { "country": "at", "priceEur": 539.0, "shippingEur": 8.9,  "totalPriceEur": 547.9,  "availability": "InStock" }
  ]
}
```

(Offer rows trimmed here for length; the real output also has shop name, offer URL, condition and ratings.)

### How to choose products

1. **`productIds`**: SKUs or full product URLs. The SKU is the number at the end of an Alternate product URL, e.g. `.../html/product/100140531`. Most precise and cheapest.
2. **`searchQueries`**: words matched against product names from Alternate's public sitemap (Dutch names, all words must match), e.g. `RTX 5070`, `Ryzen 9 9950X`. Capped by `maxItems`.
3. **`categoryUrls`**: a category page such as `https://www.alternate.nl/CPUs/AMD-CPUs/Socket-AM5`. Only the **first page** (about 17 products) is read, because further pages load dynamically.

### Price-drop alerts

Set `onlyPriceDrops: true` and `minDropPct` (default 5). Schedule the Actor on the Apify platform (for example every few hours). Each run returns only products that are **new** or whose lowest total fell by at least `minDropPct` since the previous run, and you are charged only for those.

Add a `webhookUrl` (Discord, Slack or any HTTPS endpoint that accepts JSON) to be notified of drops at the end of each run. Alerts cover genuine drops, not first-time discoveries.

State lives in a named key-value store (`stateStoreName`, default `alternate-price-state`). Use different names to keep separate watchlists.

### Pricing (pay per event)

You pay per **product returned** (event `product-scraped`), plus Apify platform usage. The Console price is **$4.00 per 1,000 products** ($0.004 per product); the price shown on this Actor's page is what counts.

- **Free**: products skipped by the price-drop filter, and pages that fail or cannot be parsed, are not returned and not billed.
- **Cost cap**: `maxItems` (default 50) is the most products one run returns, so it is also your cost cap per run. You can also set a maximum cost per run in Apify; the Actor stops cleanly when it is reached.

Worked examples at $0.004 per product:

| Run | Products returned | Event cost |
|---|---|---|
| Quick check | 5 | $0.02 |
| Daily watchlist | 50 | $0.20 |
| Weekly market scan | 500 | $2.00 |
| Hourly drop-watch of 100 SKUs, ~3 drops per run | 3 | $0.012 per run |

Speed: each product needs about 6 polite requests (3 countries, one redirect each), so allow roughly **6 seconds per product** at the default delay.

### Use with AI agents (MCP, Claude, Cursor, n8n, Make)

AI agents can call this Actor through the Apify MCP server. Add this to your MCP client config (Claude Desktop, Cursor and others). You sign in with Apify via OAuth in the browser on first use.

```json
{ "mcpServers": { "apify": { "url": "https://mcp.apify.com?tools=goobertje/alternate-price-watch" } } }
```

Example prompts and the input each one maps to:

1. "Is the Ryzen 9 9950X cheaper in Belgium or Austria than in the Netherlands right now?"
   ```json
   { "searchQueries": ["Ryzen 9 9950X"], "countries": ["nl", "be", "at"], "homeCountry": "nl", "maxItems": 3 }
   ```
2. "Compare product 100065780 in all three Alternate shops and tell me where to buy it."
   ```json
   { "productIds": ["100065780"], "homeCountry": "nl" }
   ```
3. "Tell me which RTX 5070 cards dropped at least 5% since the last check."
   ```json
   { "searchQueries": ["RTX 5070"], "onlyPriceDrops": true, "minDropPct": 5, "maxItems": 50 }
   ```

In n8n, Make or Zapier, use the Apify node or module, pick this Actor and paste the same JSON.

### Use cases

- **Cross-border arbitrage checks** for resellers and system builders in the Benelux and Austria.
- **Repricing and competitor monitoring** for shops selling the same hardware.
- **Deal alerts** for communities and Discord servers, via `onlyPriceDrops` and `webhookUrl`.
- **Personal wishlists**: "tell me when the 9950X drops 5% anywhere".
- **AI agents**: ask Claude or an n8n flow "is this GPU cheaper in Belgium right now?".

### FAQ

**Where does the data come from, and is it allowed?**
From Alternate's public product pages and public sitemap (alternate.nl, .be, .at). The Actor follows `robots.txt`, uses an honest user agent and about 1 request per second. See Legal below.

**How fresh is the data?**
It is read live during each run. Prices and stock can change within minutes after that.

**How much does a typical run cost?**
At $4.00 per 1,000 products, a 50-product run costs about $0.20 plus Apify platform usage. Details are in the Pricing section.

**What happens when the shop blocks me or changes its layout?**
If a shop answers 403 or 429 three times in a row (after short back-offs), the run stops and fails with a clear message. Items already pushed are kept and the price history is saved. The Actor does not try to evade blocking. A layout change that breaks parsing is reported in the run log, and a page that cannot be parsed is skipped.

**Can I schedule it and get only new or cheaper items?**
Yes. Schedule it on the Apify platform and set `onlyPriceDrops: true`. You only get, and only pay for, new products or drops of at least `minDropPct`.

**What is not included?**
Other Alternate countries, products outside the three shops, price history from before your first run, pages beyond the first page of a category, and exact shipping for large orders or free-shipping thresholds.

### Limits and honest caveats

- **Prices are as shown on each shop**, in EUR, including VAT of that country. `shippingEur` is the standard rate the shop publishes on the product page; real shipping can differ for large orders or free-shipping thresholds, and delivery times differ per country. Verify before buying.
- Only **new** and **in-stock** status published by the shop is used; stock can change within minutes.
- Search uses Alternate's sitemap names, which are in Dutch. Product IDs are the most reliable input.
- If a shop answers 403 or 429 three times in a row (after short back-offs), the run **stops and fails with a clear message**; items already pushed are kept and the price history is saved. The Actor does not try to evade blocking.
- A product page that fails or cannot be parsed is logged and skipped; the rest of the run continues.
- If a product has no published shipping rate, its total is unknown and that country is left out of the market prices (it still appears in `offers`).
- A small drop does not move the comparison baseline, so slow slides add up until they reach `minDropPct`.
- The minimum request delay is 1 second. Please keep it that way.

### Legal

This Actor reads publicly available product pages and sitemaps, respects `robots.txt`, collects no personal data and is not affiliated with or endorsed by ALTERNATE. You are responsible for using the data in line with the shops' terms and applicable law.

### Support

Found a problem or want a field added? Open an issue on the Actor page. The parser reads each page's standard `Product` structured data, which is stable, and a layout change is reported in the run log.

# Actor input Schema

## `productIds` (type: `array`):

Alternate product SKUs (the number in the product URL) or full product URLs on alternate.nl, .be or .at. The same product is looked up in every selected country. Example: \["1234567", "https://www.alternate.nl/Gigabyte/RTX-5070/html/product/1234567"].

## `searchQueries` (type: `array`):

Keywords matched against product names found in the Alternate sitemap. Each keyword adds the matching products, up to Max items in total. Example: \["RTX 5070", "Ryzen 7 9800X3D"].

## `categoryUrls` (type: `array`):

Alternate category or listing page URLs; only the first page of each (about 17 products) is read, because further pages load dynamically. Example: \["https://www.alternate.nl/Videokaarten"].

## `countries` (type: `array`):

Which Alternate shops to fetch each product from: nl (Netherlands), be (Belgium), at (Austria). Example: \["nl", "be"] compares only NL and BE.

## `homeCountry` (type: `string`):

The country you normally buy from. The savings in arbitrageAlpha are measured against the total price (item plus shipping) in this country. Example: "nl".

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

Maximum number of products (dataset items, one per SKU, each one billed) a run outputs; the run stops once reached. It also caps how many search results are used per search query. Product IDs you list are all checked until the cap is reached. Example: 50.

## `onlyPriceDrops` (type: `boolean`):

If enabled, only products that are new (first time seen) or whose lowest total price fell by at least Minimum drop (%) since the previous run are output; unchanged, higher or slightly lower prices are skipped and not billed. A small drop does not move the comparison baseline, so slow slides add up until they reach the threshold. Example: true.

## `minDropPct` (type: `number`):

Smallest price drop in percent that counts as a drop when Only output price drops is on. Range 0 to 100. Example: 5.0.

## `stateStoreName` (type: `string`):

Name of the persistent key-value store holding the last seen prices, used for delta tracking between runs. Use a different name to keep separate watchlists. Only lowercase letters, digits and hyphens are allowed (not at the start or end). Example: "alternate-price-state".

## `webhookUrl` (type: `string`):

Optional https URL that receives a JSON alert when price drops are found. Works with Discord, Slack or any generic JSON webhook. Treated as a secret because it contains a token. Example: "https://hooks.slack.com/services/T000/B000/XXXX".

## `requestDelaySecs` (type: `number`):

Pause between requests to Alternate to stay polite. Minimum 1, maximum 30. Example: 1.0.

## Actor input object example

```json
{
  "productIds": [],
  "searchQueries": [
    "RTX 5070"
  ],
  "categoryUrls": [],
  "countries": [
    "nl",
    "be",
    "at"
  ],
  "homeCountry": "nl",
  "maxItems": 5,
  "onlyPriceDrops": false,
  "minDropPct": 5,
  "stateStoreName": "alternate-price-state",
  "requestDelaySecs": 1
}
```

# Actor output Schema

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

Products with prices in NL, BE and AT, cross-border savings and price changes.

# 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 = {
    "searchQueries": [
        "RTX 5070"
    ],
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("goobertje/alternate-price-watch").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 = {
    "searchQueries": ["RTX 5070"],
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("goobertje/alternate-price-watch").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 '{
  "searchQueries": [
    "RTX 5070"
  ],
  "maxItems": 5
}' |
apify call goobertje/alternate-price-watch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,goobertje/alternate-price-watch"
        }
    }
}
```

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/XDVQEsOTg020wCxSD/builds/PxMtrRQrw3uAlpWqN/openapi.json
