# Aldi Netherlands & Germany Price Scraper: Shelf Prices (EUR) (`yappman/nl-de-supermarket-price-scraper`) Actor

Aldi Netherlands, Aldi Nord and Aldi Süd Germany shelf prices by category or product page: price, unit price, pack size, deposit, in EUR.

- **URL**: https://apify.com/yappman/nl-de-supermarket-price-scraper.md
- **Developed by:** [Russell Yapp](https://apify.com/yappman) (community)
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 products

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

## Aldi Netherlands & Germany Price Scraper: shelf prices in EUR

From Yappman Data. Contact: info＠yappman.com.

Shelf prices from Aldi Netherlands (`aldi.nl`), Aldi Nord Germany (`aldi-nord.de`) and Aldi SÜD Germany (`aldi-sued.de`) in one row shape, read during the run (`scrapedAt` on every row): read a category listing page, named product pages, or the listing pages a shop's sitemap names. Each row is one product with `price`, `unitPrice` where the site shows one, `wasPrice`, `promotionText`, `packSize`, `categoryPath`, the bottle `deposit` where a German shop shows one, and a `matchKey` join key. In euros, charged per product returned plus a start fee.

Independent tool. Not affiliated with, endorsed by or sponsored by ALDI Nederland, ALDI Nord, ALDI SÜD or any ALDI company. Their names and marks belong to their owners and are used only to say which website the prices come from.

**Both German Aldi chains.** Germany has two Aldi chains with their own shops and prices: Aldi Nord (`aldi-nord.de`, `retailer` `aldi_nord_de`) and Aldi SÜD (`aldi-sued.de`, `retailer` `aldi_sued_de`). Each row names the shop it came from.

**What it does not copy.** Prices and unit prices are facts about what is on the shelf. Images, product descriptions, ingredients and nutrition are the retailer's content, not ours: they are not read and not returned (`imageUrl` is always null).

**Run it with the default input:** click **Start**. It reads one small `aldi.nl` listing page (eggs, about 4 products) and finishes in under a minute.

| Mode | Use it to | Input | Example question |
| --- | --- | --- | --- |
| `category` | Read every product on a category listing page | `categoryUrls` | "What do the eggs at Aldi cost?" |
| `product` | Read named product pages | `productUrls` | "What does this Aldi product cost today?" |
| `catalogue` | Read the listing pages a shop's sitemap names (about 135 for `aldi.nl` and 120 for `aldi-nord.de`, roughly 6 minutes each; for `aldi-sued.de` the 49 top-level category pages, each read page by page, 30 products a page); the run summary compares the products read with the number of product pages the sitemap lists | `retailers`, `maxItems` | "What are the dairy products at Aldi priced at?" |

There is no search mode in this version.

Sample row (Romige kwark, from a capture of 27 Oct 2026; `scrapedAt` is illustrative):

```json
{
  "retailer": "aldi_nl",
  "retailerProductId": "1235974",
  "ean": [],
  "name": "Romige kwark",
  "brand": "MILSANI",
  "packSize": "500g",
  "price": 1.89,
  "wasPrice": null,
  "unitPrice": { "value": 3.78, "measure": "kg" },
  "loyaltyPrice": null,
  "promotionText": null,
  "promotionEndsAt": null,
  "inStock": null,
  "categoryPath": ["Zuivel, eieren en boter", "Verse zuivel"],
  "url": "https://www.aldi.nl/product/romige-kwark-1235974.html",
  "imageUrl": null,
  "rating": null,
  "scrapedAt": "2026-10-27T18:00:00Z",
  "matchKey": "milsani romige kwark 500g",
  "currency": "EUR",
  "market": "nl"
}
```

### Input reference

- `mode`: `category` (default), `product` or `catalogue`.
- `categoryUrls`: category listing page addresses, e.g. `https://www.aldi.nl/producten/zuivel-eieren-boter/verse-zuivel.html`, `https://www.aldi-nord.de/sortiment/milchprodukte/milch-milchgetraenke.html` or `https://www.aldi-sued.de/produkte/kaese/frischkaese/k/1588161425467086`; the shops can be mixed in one run. On `aldi.nl` and `aldi-nord.de` use a subcategory page: their top-level category pages carry no product list and are refused with a message. On `aldi-sued.de` a top-level page such as `https://www.aldi-sued.de/produkte/kaese/k/1588161425467083` lists products too, and a listing is read page by page (30 products a page) until `maxItems` or the end of the listing. An address with a question mark is refused.
- `productUrls`: product page addresses, e.g. `https://www.aldi.nl/product/mega-scharreleieren-1216651.html`, `https://www.aldi-nord.de/produkt/milchreis-6570.html` or `https://www.aldi-sued.de/produkt/milsani-koerniger-frischkaese-200-g-natur-000000000202687002`.
- `retailers`: `["aldi_nl"]` by default; `aldi_nord_de` and `aldi_sued_de` for Germany (catalogue mode).
- `maxItems`: 1 or more, default 60.

### Fields

`retailer` (`aldi_nl`, `aldi_nord_de` or `aldi_sued_de`), `retailerProductId`, `name`, `brand` (Aldi's own labels, or `Aldi` when none is shown), `packSize` as shown (`500g`, `1L`, `6x50cl`, `10 stuks`, `30 Stück`, `235 g`), `price` (EUR), `wasPrice` and `promotionText` (e.g. `-10%`) while a reduction is shown, `promotionEndsAt` for a reduced price, `unitPrice` (`value` and `measure`, only where the shop shows one: aldi.nl on about three quarters of products, aldi-nord.de on every record seen, aldi-sued.de on every record seen except loose fruit and vegetables; null otherwise, never calculated), `deposit` (the bottle deposit, Pfand, in EUR as the shop shows it, e.g. `0.25`; filled on aldi-nord.de and aldi-sued.de where the shop shows one, null otherwise and always null on aldi.nl), `categoryPath` (Dutch or German, as shown), `url`, `currency`, `market` (`nl` or `de`), `matchKey`, `scrapedAt`. `ean` is always empty (Aldi shows no barcode) and `inStock`, `loyaltyPrice` and `rating` are always null.

### Fields returned and not returned

Returned: the fields listed above, for one product per row. Not returned: images, product descriptions, ingredients, nutrition, reviews, ratings, contact details or any user field. A row describes a product; a brand or product name could in principle be a person's name.

### Pricing

Pay per event: a start fee of $0.005 per run plus one `product` event for each row returned, and no other event. On the Free plan a product costs $0.0015 ($1.50 per 1,000); on higher plans it costs less, down to $0.0009 ($0.90 per 1,000). A run that returns nothing costs only the start fee, and if a shop stops the run you pay only for the rows already returned.

Worked examples on the Free plan: the default run (about 4 rows) costs $0.005 + 4 x $0.0015 = $0.011; a run at the default `maxItems` of 60 costs at most $0.005 + 60 x $0.0015 = $0.095; 1,000 rows cost $1.505. At the lowest plan price the same runs cost $0.0086, $0.059 and $0.905.

### Limitations

- Prices are those shown to a visitor with no store chosen; a price shown is current at `scrapedAt`.
- A reduced price on aldi-nord.de (`wasPrice`, `promotionText`) is not yet verified on a real capture. On aldi-sued.de it was seen on the capture of 28 Oct 2026; that shop gives no end date for a reduction, so `promotionEndsAt` is null there.
- Unit prices are the retailer's own, as the shelf shows them. Where the site gives none, `unitPrice` is null: this Actor never calculates one, because prices are facts as the retailer shows them.
- Stock is per store and not shown online, so `inStock` is null.
- The bottle deposit (Pfand) is returned in `deposit`, separately; `price` is the shelf price as the site gives it, and this Actor makes no claim about whether a shelf price includes the deposit. aldi.nl rows always have `deposit` null.
- Records without a current price, or not on sale yet (for example a promotion that has not started, or an aldi-sued.de offer dated later than the run), are skipped and counted in the run summary.
- At the start of every run the Actor requests each selected shop's robots.txt. Requests go out one at a time, about one every two to three seconds, from the platform's own network. If a shop answers 403 or 429 the run stops for that shop at once, with no retry; a server error is retried a few times, then stops it too; any other answer that is not a normal page stops it as well. You pay only for the rows already returned.

### FAQ

**Why no images or descriptions?** They are the retailer's content. This Actor returns prices and facts only.

**Which German Aldi is covered?** Both: Aldi Nord (`aldi-nord.de`) and Aldi SÜD (`aldi-sued.de`), as separate shops.

**Do the rows carry images?** No: `imageUrl` is always null.

### Ratings and reviews

Ratings and reviews on the Store page help other buyers judge whether this Actor fits their use, whatever your experience.

### Changelog

See the **Changelog** tab on this Actor's page for version history.

# Changelog

This Actor's version history is a separate document: https://apify.com/yappman/nl-de-supermarket-price-scraper/changelog.md

# Actor input Schema

## `retailers` (type: `array`):

Which shops to read. Exact values: aldi_nl (Aldi Netherlands), aldi_nord_de (Aldi Nord Germany), aldi_sued_de (Aldi SÜD Germany). Catalogue mode only; category and product modes take the shop from the page addresses.

## `mode` (type: `string`):

What to run, one mode per run: category (listing pages in categoryUrls), product (pages in productUrls) or catalogue (the listing pages named in each selected shop's sitemap: about 135 for aldi_nl and 120 for aldi_nord_de, roughly 6 minutes each; the 49 top-level category pages for aldi_sued_de, read page by page). Example: "category" with the default category address.

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

Subcategory listing page addresses from aldi.nl or aldi-nord.de, or category listing page addresses from aldi-sued.de, up to 250. An aldi.nl address is /producten/, a category name, a slash, a subcategory name and .html; an aldi-nord.de address is /sortiment/ instead of /producten/; an aldi-sued.de address is /produkte/, one or more category names, /k/ and a 16-digit number (a top-level category page is accepted there). Plain addresses only: an address with a question mark is refused (category mode only).

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

Product page addresses from aldi.nl (/product/<name>-<number>.html), aldi-nord.de (/produkt/<name>-<number>.html) or aldi-sued.de (/produkt/<name>-<18-digit number>), up to 500 (product mode only; leave empty unless you have addresses).

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

The most rows to save in total, 1 or more (every mode). The default run saves up to 60 rows.

## Actor input object example

```json
{
  "retailers": [
    "aldi_nl"
  ],
  "mode": "category",
  "categoryUrls": [
    "https://www.aldi-nord.de/sortiment/milchprodukte/milch-milchgetraenke.html"
  ],
  "productUrls": [
    "https://www.aldi.nl/product/mega-scharreleieren-1216651.html",
    "https://www.aldi-nord.de/produkt/milchreis-6570.html"
  ],
  "maxItems": 60
}
```

# Actor output Schema

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

No description

# 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 = {
    "retailers": [
        "aldi_nl"
    ],
    "mode": "category",
    "categoryUrls": [
        "https://www.aldi.nl/producten/zuivel-eieren-boter/eieren.html"
    ],
    "maxItems": 60
};

// Run the Actor and wait for it to finish
const run = await client.actor("yappman/nl-de-supermarket-price-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 = {
    "retailers": ["aldi_nl"],
    "mode": "category",
    "categoryUrls": ["https://www.aldi.nl/producten/zuivel-eieren-boter/eieren.html"],
    "maxItems": 60,
}

# Run the Actor and wait for it to finish
run = client.actor("yappman/nl-de-supermarket-price-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "retailers": [
    "aldi_nl"
  ],
  "mode": "category",
  "categoryUrls": [
    "https://www.aldi.nl/producten/zuivel-eieren-boter/eieren.html"
  ],
  "maxItems": 60
}' |
apify call yappman/nl-de-supermarket-price-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yappman/nl-de-supermarket-price-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/lfadQXbtnhTl4Dvj2/builds/VZ3MI51PZAA3Aegyv/openapi.json
