# Competitor Price Monitor (EU) — idealo, Google, Amazon (`anyprice/competitor-price-monitor`) Actor

Track competitor prices for your products by EAN on idealo, Google Shopping and Amazon: cheapest competitor, your rank, price gap and change since the last run. Built for scheduled runs.

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

## Pricing

from $3.20 / 1,000 marketplace lookups

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?

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

## Competitor Price Monitor (EU) — idealo, Google, Amazon

Competitor price monitoring for online shops: give it your products by EAN and your selling price, and it finds the cheapest competitor on idealo, Google Shopping and Amazon, your price rank, the price gap, and how the competitor price changed since the last run. On a schedule it works as a price tracker for up to 50 products per run in 8 European countries.

### What it does

For each product the Actor checks the selected marketplaces at the same time and works out where you stand:

- `cheapest_competitor`: the lowest competitor offer across all marketplaces, with seller, price, shipping, total and marketplace
- `my_price`, `price_gap`, `price_gap_percent`: your price and how far you are above (positive) or below (negative) that competitor
- `my_rank`, `cheaper_competitors`, `is_cheapest`: your position among all competitor offers
- `competitor_price_change`: how much the cheapest competitor price moved since the previous run with the same monitor name
- per marketplace: product URL, offer count, cheapest competitor there, and your own offer if it was found

Your own offers are kept out of the competitors when you set `shopName`. Only comparable offers count: new condition (or unknown), with a known price, and not marked by idealo's offer matching as a different product, bundle, multipack or different variant. Offers are compared by total (price + shipping), or by price when the marketplace does not state shipping.

### Who it is for

- Online shops that sell the same products as their competitors and want a daily list of products where they are no longer the cheapest.
- Sellers on idealo or Amazon who reprice by hand and need the current lowest competitor price as a starting point.
- Brands and distributors watching how their resellers price a product range across marketplaces.
- Agencies sending clients a weekly price position report per country.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `products` | array of objects | (required) | 1 to 50 products as `{ "ean": "...", "price": 12.99 }`. `price` is optional |
| `shopName` | string | empty | Your shop or seller name as the marketplaces show it |
| `country` | string | `de` | `de`, `at`, `fr`, `it`, `es`, `uk`, `nl` or `be` |
| `platforms` | array of strings | all three | Any of `idealo`, `google_shopping`, `amazon` |
| `monitorName` | string | `default` | Name for this product list's history: 1 to 40 lowercase letters, digits or hyphens, starting with a letter or digit |

**`price`** is your selling price including shipping, in the marketplace's currency. If you leave it out, the Actor uses your cheapest own offer that it finds through `shopName`. If you give both, `price` wins.

**`shopName`** is matched case-insensitively against the seller name of each offer, and a partial match counts. `myshop` matches "MyShop GmbH" and "myshop.de". Offers that match are treated as yours and never as competitors. If you leave `shopName` empty, your own offers are counted as competitors.

**Country coverage:** idealo covers `de`, `at`, `fr`, `it`, `es` and `uk`. Amazon covers `de`, `fr`, `it`, `es`, `uk`, `nl` and `be`. Google Shopping covers all eight. Marketplaces that don't cover your country are skipped, shown as `unsupported` and not charged.

#### Input example

```json
{
  "products": [
    { "ean": "4006381333931", "price": 2.49 },
    { "ean": "4009803341163" }
  ],
  "shopName": "Example Office",
  "country": "de",
  "platforms": ["idealo", "google_shopping", "amazon"],
  "monitorName": "office-supplies"
}
```

### Output

One row per product, in input order.

| Field | Meaning |
|---|---|
| `ean`, `status` | Barcode and outcome: `found`, `not_found`, `error` or `invalid` |
| `my_price` | Your price from the input, or your cheapest own offer found via `shopName`. `null` if neither exists |
| `cheapest_competitor` | Cheapest competitor offer across marketplaces (`platform`, `seller`, `price`, `shipping`, `total`, `currency`, `url`) |
| `price_gap` | `my_price` minus the cheapest competitor price. Negative means you are cheaper |
| `price_gap_percent` | The gap as a percentage of the competitor price |
| `my_rank` | 1 means nobody is cheaper. A competitor at exactly your price does not push you down |
| `cheaper_competitors` | Number of competitor offers below your price |
| `is_cheapest` | `true` when no competitor offer is below your price |
| `previous_check` | Time and cheapest competitor price of the last run with the same monitor name, country and marketplaces |
| `competitor_price_change` | Cheapest competitor price now minus at `previous_check`. Negative means a competitor went down |
| `platforms.<name>` | `status`, `product_url`, `offers_count`, `cheapest_competitor` and `my_offer` per marketplace |

Rank and gap need a price for you. Without `price` and without an own offer found by `shopName`, the row still shows the cheapest competitor, but `my_rank`, `price_gap` and `is_cheapest` are `null`.

Rank counts offers, not shops. A competitor listed on idealo and on Google Shopping below your price counts twice.

Example row from a second run (shops and prices are illustrative):

```json
{
  "query": "4006381333931",
  "ean": "4006381333931",
  "country": "de",
  "status": "found",
  "my_price": 2.49,
  "cheapest_competitor": { "platform": "idealo", "seller": "Example Bürobedarf", "price": 2.29, "shipping": 0, "total": 2.29, "currency": "EUR", "url": "https://www.example-buerobedarf.de" },
  "price_gap": 0.2,
  "price_gap_percent": 8.73,
  "is_cheapest": false,
  "my_rank": 2,
  "cheaper_competitors": 1,
  "previous_check": { "checked_at": "2026-10-14T06:00:12.000Z", "cheapest_competitor_price": 2.39 },
  "competitor_price_change": -0.1,
  "platforms": {
    "idealo": {
      "status": "found",
      "product_url": "https://www.idealo.de/preisvergleich/OffersOfProduct/4310112.html",
      "offers_count": 4,
      "cheapest_competitor": { "platform": "idealo", "seller": "Example Bürobedarf", "price": 2.29, "shipping": 0, "total": 2.29, "currency": "EUR", "url": "https://www.example-buerobedarf.de" },
      "my_offer": { "platform": "idealo", "seller": "Example Office", "price": 2.49, "shipping": 0, "total": 2.49, "currency": "EUR", "url": "https://www.example-office.de" }
    },
    "google_shopping": {
      "status": "found",
      "product_url": "https://www.google.com/shopping/product/8842115630921457?gl=de",
      "offers_count": 2,
      "cheapest_competitor": { "platform": "google_shopping", "seller": "Example Papeterie", "price": 2.19, "shipping": 1.95, "total": 4.14, "currency": "EUR", "url": "https://www.example-papeterie.de/stabilo-88" },
      "my_offer": null
    },
    "amazon": {
      "status": "found",
      "product_url": "https://www.amazon.de/dp/B00EX6TMR4",
      "offers_count": 3,
      "cheapest_competitor": { "platform": "amazon", "seller": "Example Seller", "price": 2.64, "shipping": 0, "total": 2.64, "currency": "EUR", "url": "https://www.amazon.de/sp?seller=A1EXAMPLESELLER1" },
      "my_offer": null
    }
  }
}
```

You sell at 2.49. The cheapest competitor is on idealo at 2.29, so you are 0.20 (8.73 %) above it, and one competitor offer is cheaper, which puts you at rank 2. At the previous run that competitor price was 2.39, so it dropped by 0.10.

### How the history works

After each run the Actor saves the cheapest competitor price per EAN in a named key-value store called `competitor-price-monitor` in your Apify account. The entry is keyed by monitor name, country and marketplaces, for example `office-supplies-de-amazon_google_shopping_idealo`. The next run with the same `monitorName`, `country` and `platforms` reads it to fill `previous_check` and `competitor_price_change`.

- On the first run `previous_check` and `competitor_price_change` are `null`.
- Products that come back `error` keep their previous value, so a failed check does not reset the history.
- Use a different `monitorName` for each product list, otherwise the lists overwrite each other's history.
- Named stores are kept until you delete them. Deleting the store resets all monitors.

See the Apify docs on [key-value stores](https://docs.apify.com/platform/storage/key-value-store) for how to view or delete it.

### Scheduling

1. Save your input as a task.
2. Add an Apify [schedule](https://docs.apify.com/platform/schedules), for example once a day.
3. Read the newest dataset after each run. Filter on `is_cheapest = false` or a negative `competitor_price_change`.

Keep `platforms` the same between runs, so the change is measured against the same set of marketplaces. If you run several monitors, start them at different times: your account can have only 50 idealo lookups from this Actor in progress at once, and a full list of 50 products uses all of them. Runs that overlap wait for capacity and may return `concurrency_limit_reached`.

### Billing

Pay per event; see the **Pricing** tab for current rates.

One `platform-lookup` event is charged for each marketplace that returns `found` or `not_found` for a product. 50 products on three marketplaces are at most 150 events per run. Free of charge:

- invalid EANs (never sent to a marketplace)
- marketplaces marked `unsupported` for your country
- lookups that end in an error or a timeout

### Limits and errors

- 50 products per run, one country per run. Duplicate EANs are merged; the first entry wins.
- A product entry without an `ean`, or with a negative `price`, stops the whole run with an input error.
- All marketplaces share an 890-second budget. Lookups without an answer by then become free errors with `errorCode` `timeout`. Set the run timeout to at least 960 seconds.
- The run always ends as **Succeeded**. Check `status` on each row.

| `errorCode` | Meaning |
|---|---|
| `invalid_input` | Not a valid EAN: wrong length, wrong check digit, or one digit repeated |
| `timeout` | A marketplace did not answer in time |
| `blocked` | A marketplace blocked the lookup |
| `free_limit_reached` | Your free Apify plan's allowance for that marketplace is used up |
| `concurrency_limit_reached` | Other runs held all lookup capacity for that marketplace |
| `internal_error` | Any other failure. On the row it means no marketplace could be checked |

### FAQ

**Does it change my prices?**
No. It only reads marketplace prices and reports your position.

**What if two shops have the same price as me?**
Ties count in your favour. You keep rank 1 and `is_cheapest` stays `true` as long as nobody is strictly cheaper.

**How is the Amazon product found?**
Amazon is searched for the EAN and the first search result is used. Check `platforms.amazon.product_url` once for each product to make sure it is the right listing.

**Can I track the same products in two countries?**
Yes. Run the same `monitorName` with a different `country`; each country has its own history.

Questions or higher limits: support@anyprice.dev

# Actor input Schema

## `products` (type: `array`):

Up to 50 products as {"ean": "…", "price": 12.99}. price is your selling price including shipping; leave it out to use your own offer found via Shop name.

## `shopName` (type: `string`):

Your shop or seller name as shown on the marketplaces (case-insensitive, partial match). Your own offers are excluded from the competitors.

## `country` (type: `string`):

Marketplace country. idealo covers DE, AT, FR, IT, ES and UK; Amazon covers DE, FR, IT, ES, UK, NL and BE; Google Shopping covers all eight. Marketplaces that do not cover the country are marked unsupported and not charged.

## `platforms` (type: `array`):

Marketplaces to watch. Each marketplace checked for a product is one charged lookup.

## `monitorName` (type: `string`):

Keeps the history of separate product lists apart. Runs with the same name, country and marketplaces compare against each other's last result.

## Actor input object example

```json
{
  "products": [
    {
      "ean": "4006381333931",
      "price": 2.49
    },
    {
      "ean": "4009803341163"
    }
  ],
  "country": "de",
  "platforms": [
    "idealo",
    "google_shopping",
    "amazon"
  ],
  "monitorName": "default"
}
```

# Actor output Schema

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

URL of the default dataset items. Check each row's status.

# 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 = {
    "products": [
        {
            "ean": "4006381333931",
            "price": 2.49
        },
        {
            "ean": "4009803341163"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("anyprice/competitor-price-monitor").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 = { "products": [
        {
            "ean": "4006381333931",
            "price": 2.49,
        },
        { "ean": "4009803341163" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("anyprice/competitor-price-monitor").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 '{
  "products": [
    {
      "ean": "4006381333931",
      "price": 2.49
    },
    {
      "ean": "4009803341163"
    }
  ]
}' |
apify call anyprice/competitor-price-monitor --silent --output-dataset

```

## MCP server setup

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

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/uCd9ZG4OrPqPZqivy/builds/rddNYBMh1kOwgPhgp/openapi.json
