# Lidl Price Drop Monitor & Scraper - DE, AT, CH, NL, CZ, SK (`neverempty/lidl-price-drop-monitor`) Actor

Schedule it daily and get only new products, price drops and price rises from the Lidl online shop, with the old price. Search by words or category, paste product links, or read every product: price, strike price, unit price, EAN, rating and store dates. Export JSON, CSV or Excel.

- **URL**: https://apify.com/neverempty/lidl-price-drop-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.19 / 1,000 product returneds

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

## Lidl Price Drop Monitor & Scraper - DE, AT, CH, NL, CZ, SK

**Schedule it daily → get only new products, price drops and price rises from the Lidl online shop, with the old price.** For resellers, deal sites, brands and price analysts: type product words (or paste a Lidl category or product link), pick the country, and every run returns each product's price (EUR, CHF or CZK), strike price, recommended price and discount, unit price, EAN, rating, delivery and store dates. Turn on watch mode and later runs return only what changed since the last run, with the previous price and the change in %.

- Six Lidl online shops: Germany, Austria, Switzerland, Netherlands, Czechia and Slovakia
- Search by product words, paste a category link, or watch exact products by their product links
- Read every product of a shop at once (about 14,000 in Germany) and get every new product and price change
- EAN barcode for matching with your own catalogue or other shops
- Store dates: when a product comes to Lidl stores and when the online offer started
- Filter by price range or only discounted products
- Each product once per run, even when two searches overlap
- Export to JSON, CSV or Excel, or send rows to Google Sheets, Slack or a webhook with Apify integrations

### What you get

| Field | Example |
|---|---|
| `productId`, `url` | `100407757`, link to the Lidl product page |
| `title`, `brand` | `PARKSIDE® 20V Akku 4 Ah »PAP 204 A1«`, `PARKSIDE®` |
| `price`, `priceBeforeDiscount`, `recommendedPrice`, `discountPercent`, `discountText` | `244`, `299.99`, `299.99`, `18`, `55.99 € billiger` (a Bissell floor cleaner; strike price, recommended retail price and the discount as Lidl shows them) |
| `unitPrice`, `unitPriceText`, `packaging` | Price per kg / litre and the pack size, for food and drugstore products |
| `ean`, `eans` | `4054599427669` (all barcodes of the product in `eans`) |
| `rating`, `reviewCount` | `4.8`, `4637` |
| `inStock`, `availabilityText`, `availableOnline`, `availableInStores` | `true`, `lieferbar`, `true`, `false` |
| `inStoreFrom`, `firstOnlineDate`, `offerStart`, `offerEnd` | The day the product comes to Lidl stores, the day it went online, and the online offer period |
| `deliveryDays`, `deliveryCharge`, `deliveryText` | `3`, `5.95`, `Sofort lieferbar! In 3 Arbeitstagen bei dir` |
| `category`, `variantCount` | `Baumarkt > Werkstatt > Akku & Elektrowerkzeuge > Akkus & Ladegeräte`, number of sizes or colours |
| `imageUrl`, `imageUrls` | Main image and all product images |
| `country`, `currency` | `Germany`, `EUR` (`CHF` in Switzerland, `CZK` in Czechia) |
| `status`, `searchQuery`, `scrapedAt` | `ok`, the search or link that found the product, and when it was read |
| `changeType`, `previousPrice`, `priceChange`, `priceChangePercent`, `comparedOn`, `previousInStock`, `previousSeenAt`, `removedReason` | Watch mode: what changed since the last run, and the price it is compared with |

### How to use it

1. Type one or more product words in **Products to find**, for example `parkside`, `kaffeemaschine` or `silvercrest`. You can also paste the link of a Lidl category page (`lidl.de/h/...`) or of a product page (`lidl.de/p/.../p100398365`).
2. Pick one or more countries in **Lidl online shop (country)**. Each word is searched in every shop you pick; links always use the shop of the link.
3. Set **Maximum products per search** and run. Leave everything empty to run the example: 10 Parkside products in Germany.

To read a whole shop, turn on **Every product of the shop**. To keep track of prices, turn on **Watch mode** and add a schedule. See [Monitoring on a schedule](#monitoring-on-a-schedule).

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Product words, Lidl category links or product links. Up to 50 searches and 1,000 product links per run. |
| `countries` | `de`, `at`, `ch`, `nl`, `cz`, `sk` (default Germany). |
| `wholeCatalog` | Read every product the shop lists instead of searching by words. |
| `onlyOnSale` | Only products with a strike price or a price below the recommended price. |
| `minPrice`, `maxPrice` | Price range in the shop's currency. |
| `maxResultsPerSearch` | Products per search (default 100, up to 20,000). |
| `monitoringMode` | Watch mode for schedules. |
| `minChangePercent` | Report a price move only when it reaches this % (default 1). |
| `includeUnchanged` | Also return products that did not move. |
| `watchName`, `resetMonitoringState` | Keep separate memories for two watches of the same search, or start over. |

#### Example input

```json
{
    "searchQueries": ["parkside", "https://www.lidl.de/h/akkuwerkzeuge/h10067531"],
    "countries": ["de", "at"],
    "onlyOnSale": true,
    "maxResultsPerSearch": 200,
    "monitoringMode": true,
    "minChangePercent": 5
}
```

### Output

A product row from a real run (category link, Germany):

```json
{
    "status": "ok",
    "productId": "100407757",
    "url": "https://www.lidl.de/p/parkside-20v-akku-4-ah-pap-204-a1/p100407757",
    "title": "PARKSIDE® 20V Akku 4 Ah »PAP 204 A1«",
    "brand": "PARKSIDE®",
    "price": 29.99,
    "priceBeforeDiscount": null,
    "recommendedPrice": null,
    "discountPercent": null,
    "discountText": null,
    "unitPrice": null,
    "unitPriceText": null,
    "packaging": null,
    "ean": "4054599427669",
    "eans": ["4054599427669"],
    "rating": 4.8,
    "reviewCount": 4637,
    "inStock": true,
    "availabilityText": "lieferbar",
    "availableOnline": true,
    "availableInStores": false,
    "inStoreFrom": null,
    "deliveryDays": 3,
    "deliveryCharge": 5.95,
    "deliveryText": "Sofort lieferbar! In 3 Arbeitstagen bei dir",
    "category": "Baumarkt > Werkstatt > Akku & Elektrowerkzeuge > Akkus & Ladegeräte",
    "imageUrl": "https://www.lidl.de/assets/gcp0473633e96e04c1785f7e9e2a9f846cf.jpg",
    "imageUrls": ["https://www.lidl.de/assets/gcp0473633e96e04c1785f7e9e2a9f846cf.jpg", "..."],
    "variantCount": 0,
    "firstOnlineDate": "2026-06-29",
    "offerStart": "2026-07-22T08:58:48.344Z",
    "offerEnd": null,
    "country": "Germany",
    "currency": "EUR",
    "searchQuery": "https://www.lidl.de/h/akkuwerkzeuge/h10067531",
    "scrapedAt": "2026-10-29T12:13:13.008Z"
}
```

In watch mode each row also carries `changeType` and, for a price move, `previousPrice`, `priceChange`, `priceChangePercent` and `comparedOn`.

Every row has a `status`. Product rows are `ok`. When a search finds nothing, a product page is gone, or a search stops early, you get one free row that says so (`no-results`, `not-found`, `incomplete`, `blocked`, `unreadable`, `bad-input`), so you can see how the run went.

### Monitoring on a schedule

Turn on **Watch mode**, then click **Schedule** on the Actor page and pick daily (or hourly).

- **First run:** returns the products it finds, each with `changeType: "new"`, and remembers their prices.
- **Every later run:** returns only
  - `new` - products that just appeared in your search, or came back after leaving it
  - `price_drop` - cheaper than last time, with `previousPrice`, `priceChange` and `priceChangePercent`
  - `price_up` - pricier than last time
  - `removed` - the product was missing for two runs in a row: its page was taken down (`removedReason: "page-removed"`) or it left your search (`"left-list"`). Reported for word searches, product links and whole shops that fit in Maximum products per search, without a price or discount filter.
- **Quiet run:** one free `no-change` row confirms the run worked. You pay the start fee and the small watch check fee for the products it read.

Small moves add up: with `minChangePercent: 5`, a product that drops 2% a day is reported on the third day, compared with the price it was last reported at.

Each search and country is tracked separately. To watch the same search twice with different settings, give each task its own `watchName`. To start over, run once with `resetMonitoringState`.

### Pricing

You pay per product row, plus a small start fee per run. In watch mode, a small check fee also applies to each product that was read and had not changed:

| Event | Price |
|---|---|
| Product row (`ok`) | $2.79 per 1,000 rows (down to $2.19 on higher Apify plans) |
| Run start | $0.008 |
| Watch check (watch mode, second run on) | $0.15 per 1,000 unchanged products read (down to $0.12 on higher Apify plans) |

Status rows (`no-change`, `no-results`, `not-found`, `incomplete` and the others) are free. In watch mode, from the second run on, you pay the row price for the rows that changed (`new`, `price_drop`, `price_up` or `removed`) and $0.00015 for each unchanged product that was read. A daily watch of 500 products where 20 moved costs about $0.14 per run; a quiet day for the same 500 costs about $0.08. Watching every product of Lidl Germany (about 14,000) costs about $2.10 on a quiet day. The first watch run returns every product as `new`, so it bills all of them as rows (500 products = about $1.40 + $0.008) and has no check fee. With `includeUnchanged`, unchanged products come back as rows at the row price instead of the check fee. Runs with Watch mode off never pay the check fee.

### FAQ

**How do I get an alert when a Lidl product drops in price?**
Turn on Watch mode, type the product words or paste the product link, pick the country, set `minChangePercent` (for example 5) and schedule the Actor daily. Each run returns only the products that got cheaper or pricier, with the old price. Connect the run to Slack, email or a webhook with Apify integrations to get the alert.

**How do I see every new product Lidl puts online?**
Turn on **Every product of the shop** and Watch mode, pick the country and schedule it daily. Every product that appears for the first time comes back with `changeType: "new"`, with its price, EAN and store date.

**Can I watch exact products?**
Yes. Paste the Lidl product links, one per line, from any of the six shops. Each run returns those exact products, and a free `not-found` row lists the ones Lidl took offline.

**Can I track a whole category?**
A category link gives you the first 48 products of the category. For a full brand or product type across the shop, type the words instead, for example `parkside` or `silvercrest`, or turn on Every product of the shop.

**Which words find products?**
Words from the product name, in the shop's language: `kaffeemaschine` in Germany, `koffiezetapparaat` in the Netherlands, `kávovar` in Czechia. Brand names such as `parkside`, `silvercrest` or `crivit` work in every language.

**Can I match Lidl products with my own catalogue?**
Yes. Most rows carry the EAN barcode (`ean`, and all codes in `eans`), so you can join them with your products or with other shops.

**What currency are the prices in?**
The shop's currency: EUR in Germany, Austria, the Netherlands and Slovakia, CHF in Switzerland and CZK in Czechia. Every row carries `currency` and `country`.

**How often should I schedule it?**
Daily suits most price watches. Hourly works too; a quiet run returns one free row and costs the start fee plus the check fee for the products it read (500 products = about $0.08).

**Can I export to Excel or Google Sheets?**
Yes. Download the dataset as CSV or Excel, or use the Google Sheets integration.

### Good to know

- Unofficial and not affiliated with Lidl.
- Covers the products of the Lidl online shops (lidl.de, lidl.at, lidl.ch, lidl.nl, lidl.cz, lidl.sk), not the weekly store leaflet.
- Every search word must appear in the product name, in the shop's language. A word that is part of a longer compound may not match (`akkuschrauber` does not find `Akku-Bohrschrauber`); use shorter words or the brand.
- A category link reads the first page of the category (up to 48 products). For more, use words or Every product of the shop.
- Up to 20,000 products are read for one search, 50 searches and 1,000 product links per run.
- In tests, 10 products took about 10 seconds, 40 products about 1 minute, and the whole Austrian shop (473 products) about 10 minutes. Products are read at about 45 per minute, so a whole shop of Germany (about 14,000 products) or the Netherlands (about 13,000) takes about 5 hours, and Czechia (about 12,000) or Slovakia (about 11,000) about 4 to 4.5 hours: the default timeout is 8 hours. If a run reaches its timeout, it stops a little before, keeps what it returned and adds a free `incomplete` row; raise Timeout under Run options and run again.
- In word searches and whole shops, a product that Lidl still lists but whose page was taken down returns no row and is not charged (the run log counts them). A product link you gave whose page is gone returns a free `not-found` row.
- `priceBeforeDiscount`, `recommendedPrice`, `unitPrice`, `ean`, `rating`, `inStoreFrom` and `firstOnlineDate` are `null` when Lidl shows no value for the product.
- Watch mode compares the price. A change in stock alone is not reported, but every watch row carries `inStock` and `previousInStock`.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

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

One per line: product words as they appear in the product name (parkside, silvercrest, kaffeemaschine), the link of a Lidl category page (lidl.de/h/…) or the link of a product page (lidl.de/p/…/p100398365) to watch that exact product. Every word must appear in the product name. Up to 50 searches and 1000 product links per run. Leave it empty to run the example: 10 Parkside tools in Germany.

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

Which Lidl online shops to search. Each product word is searched in every shop you pick. Product and category links always use the shop of the link. Empty = Germany.

## `wholeCatalog` (type: `boolean`):

Read every product the Lidl online shop lists (about 14,000 in Germany, 13,000 in the Netherlands, 900 in Switzerland, 470 in Austria), instead of searching by words. Combine with watch mode to get every new product and price change of the whole shop.

## `onlyOnSale` (type: `boolean`):

Return only products with a strike price or a price below the recommended retail price.

## `minPrice` (type: `number`):

Lowest price to return, in the shop's currency (EUR, or CHF / CZK in Switzerland / Czechia).

## `maxPrice` (type: `number`):

Highest price to return, in the shop's currency.

## `maxResultsPerSearch` (type: `integer`):

How many products to read for each search (each word per country, each category link). Up to 20,000. Empty = 100 (or every product when "Every product of the shop" is on).

## `monitoringMode` (type: `boolean`):

Remember the products of each search between runs. The first run returns everything as new; after that each run returns only products that are new, cheaper, pricier, or gone (with the previous price and the change in %). Runs where nothing changed return one free row. From the second run on, each unchanged product read costs a small check fee ($0.15 per 1,000). Use it with a schedule.

## `minChangePercent` (type: `number`):

Watch mode: price moves smaller than this are not returned. Small moves add up: the price is compared with the last price that was reported.

## `includeUnchanged` (type: `boolean`):

Watch mode: also return products whose price did not change (marked unchanged). Each of them is a charged row.

## `watchName` (type: `string`):

Watch mode: give each schedule its own name to keep separate memories for the same search (for example "daily" and "hourly").

## `resetMonitoringState` (type: `boolean`):

Watch mode: forget the remembered products of these searches before this run, so everything comes back as new.

## Actor input object example

```json
{
  "searchQueries": [
    "parkside"
  ],
  "countries": [
    "de"
  ],
  "wholeCatalog": false,
  "onlyOnSale": false,
  "maxResultsPerSearch": 10,
  "monitoringMode": false,
  "minChangePercent": 1,
  "includeUnchanged": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per Lidl product: product ID, link, title, brand, price, strike price, recommended price and discount, unit price, EAN, rating, review count, online and store availability, store start date, delivery, category, images and the country with its currency. Watch mode marks rows new, price_drop, price_up or removed with the previous price and the change in %. A search with no results, nothing changed, a refused request or a run that hit its maximum charge comes back as a free row that says why.

# 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": [
        "parkside"
    ],
    "countries": [
        "de"
    ],
    "maxResultsPerSearch": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/lidl-price-drop-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 = {
    "searchQueries": ["parkside"],
    "countries": ["de"],
    "maxResultsPerSearch": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/lidl-price-drop-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 '{
  "searchQueries": [
    "parkside"
  ],
  "countries": [
    "de"
  ],
  "maxResultsPerSearch": 10
}' |
apify call neverempty/lidl-price-drop-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/lidl-price-drop-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/mKRFvOodSSjqtGNSw/builds/cdf7nOnWeasQxHhFz/openapi.json
