# ALDI Scraper (`mlg14/aldi-scraper`) Actor

Extract Australian grocery products and prices by keyword or catalogue URL, including unit prices, availability notes, categories, and images.

- **URL**: https://apify.com/mlg14/aldi-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 results

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

## ALDI Scraper

Scrape ALDI Australia products and prices by keyword, category URL, search URL, or individual product URL. Export ALDI catalogue data to CSV, JSON, or spreadsheet format, including shelf prices in AUD, comparison prices, pack sizes, categories, product images, and short-run availability notes. The actor reads the same public catalogue data that powers the Australian site and returns one structured row per product.

A search for `cheese` is a quick way to build a current price list. A category URL is better when you want the whole range in a department. Product URLs are useful for checking a known item, while several inputs in one run let you collect related ranges without manually combining files. Matching products are saved once per run, identified by SKU.

### What data can you extract from ALDI?

| Field | Description | Example |
| --- | --- | --- |
| `sku` | Product identifier stored as text, including leading zeroes | `000000000517184001` |
| `name` | Published product name | `3 Cheese Potato Bake Recipe Base 45g` |
| `brand` | Published brand when available | a published label |
| `description` | Description from a direct product URL when published | `A short product description` |
| `price` | Current shelf price in Australian dollars | `1.49` |
| `priceDisplay` | Published price text | `$1.49` |
| `wasPrice` | Previous price text, when the site provides one | `$1.99` |
| `pricePerUnit` | Published comparison price per quantity | `$3.31 per 100 g` |
| `packSize` | Selling size or pack quantity | `45 g` |
| `currency` | Price currency | `AUD` |
| `onSale` | Whether a short-run availability note is attached | `false` |
| `availabilityLabel` | Exact note shown for a limited or dated line | `While Stocks Last` |
| `categories` | Broad-to-specific category path | `["Pantry", "Stocks & Gravy"]` |
| `imageUrl` | Primary product image at 600 pixels wide | Image URL |
| `images` | All published product images at 600 pixels wide | Array of image URLs |
| `inStock` | Whether the record is not marked discontinued | `true` |
| `discontinued` | Published discontinued flag | `false` |
| `ageRestricted` | Whether an age or alcohol restriction is present | `false` |
| `productUrl` | Public product page | Product URL |
| `searchTerm` | First input term that found the item | `cheese` |
| `sourceUrl` | First input URL that found the item | Category URL |

Empty fields are omitted. In particular, direct product pages can contain a description that search results do not include. `brand` can be absent for unbranded items. Comparison prices and pack sizes are published for many grocery lines but should not be assumed for every item. The `images` array may have several views, while `imageUrl` provides the first one for simple feeds.

**Availability needs careful interpretation.** `onSale` means an availability label exists; it is not a promise of a reduced price. Labels such as “While Stocks Last” or a dated “On Sale” notice describe how the item is offered. `inStock` indicates that the catalogue record is not marked discontinued. It does not check a particular store shelf, delivery slot, or live inventory. If you need to confirm a store visit, use the retailer's current local availability information.

### How to scrape ALDI

1. Enter one or more product words in **Search terms**, or paste Australian catalogue URLs in **Catalogue URLs**. You can use both input types in one run.
2. Select a sort order and set **Max products per source**. Start with 30–60 while checking a new query. Set it to `0` to continue through the available pages.
3. If you want a firm ceiling across all terms and URLs, set **Max products in run**. This global cap works alongside the per-source cap.
4. Run the actor and inspect the product table. Download the dataset as JSON, CSV, or a spreadsheet file, or retrieve it programmatically.

For category runs, copy a URL with a category ID in its path, such as `/products/pantry/k/970000000`. A short display URL without `/k/` is followed to its ID-bearing category destination. A search URL needs a `q` parameter. Product URLs need the SKU at the end of the path. The actor accepts only public Australian catalogue URLs.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `searchTerms` | string array | Empty | Keywords to search separately. A specific single word often produces a cleaner range. |
| `startUrls` | string array | Empty | Public Australian search, category, or product URLs. |
| `maxResults` | integer | `100` | Maximum new products saved for each term or URL; `0` removes this cap. |
| `maxItems` | integer | `0` | Hard cap on all products saved in the run; `0` removes the overall cap. |
| `sortBy` | selection | `relevance` | Best match, name ascending or descending, or price ascending or descending. |
| `proxyConfiguration` | object | Platform proxy | Connection setting for public catalogue requests. |

At least one term or URL is required. Five terms with `maxResults: 100` can save up to 500 products before deduplication. When the same SKU appears in two inputs, only the first match is saved; a duplicate does not consume the later input's per-source allowance. `maxItems` is useful when the total run cost matters more than the size of any one search.

The available sort values are `relevance`, `name_asc`, `name_desc`, `price_asc`, and `price_desc`. A pasted search or category URL can already include a sort and other supported catalogue filters. The default sort preserves the URL's own sort; selecting another order overrides it. The actor retains supported search and catalogue filter parameters from the URL. Price sorting uses shelf price, so use `pricePerUnit` after export if you want a cost-per-quantity comparison.

This input searches a word and collects a separate category range, with one overall ceiling:

```json
{
  "searchTerms": ["cheese"],
  "startUrls": ["https://www.aldi.com.au/products/pantry/k/970000000"],
  "sortBy": "name_asc",
  "maxResults": 60,
  "maxItems": 100
}
```

The same product may match the term and appear in the category. It will appear once in the final dataset. The `searchTerm` or `sourceUrl` field shows which input found it first. This order matters if you use provenance fields for a downstream workflow.

### Output example

The following item came from a successful `cheese` run, sorted by name:

```json
{
  "sku": "000000000517184001",
  "name": "3 Cheese Potato Bake Recipe Base 45g",
  "price": 1.49,
  "priceDisplay": "$1.49",
  "pricePerUnit": "$3.31 per 100 g",
  "packSize": "45 g",
  "currency": "AUD",
  "onSale": false,
  "categories": ["Pantry", "Stocks & Gravy"],
  "imageUrl": "https://dm.apac.cms.aldi.cx/is/image/aldiprodapac/product/jpg/scaleWidth/600/85e1ff75-d4a4-4268-925e-8bb84a415512/chefs-cupboard-3-cheese-potato-bake-recipe-base-45g",
  "images": ["https://dm.apac.cms.aldi.cx/is/image/aldiprodapac/product/jpg/scaleWidth/600/85e1ff75-d4a4-4268-925e-8bb84a415512/chefs-cupboard-3-cheese-potato-bake-recipe-base-45g"],
  "inStock": true,
  "discontinued": false,
  "ageRestricted": false,
  "productUrl": "https://www.aldi.com.au/product/chefs-cupboard-3-cheese-potato-bake-recipe-base-45g-000000000517184001",
  "searchTerm": "cheese"
}
```

The image list above is shortened to its first entry for readability; the saved item contains every published image view. A row from a URL input has `sourceUrl` instead of `searchTerm`. Direct product URLs can also supply `description`, depending on the product. Prices are numbers for calculation, and the displayed text is retained separately so a feed can reproduce the site's label.

### Use cases

- **Grocery price tracking:** Save a scheduled dataset and compare `price` by `sku` between runs. This shows changes even when a product title or image changes.
- **Unit price comparison:** Compare `pricePerUnit` within a category and identify pack sizes that give a lower cost per quantity. Keep units consistent before ranking products.
- **Range mapping:** Export category paths, brands, and product identifiers to measure the current breadth of a department or build a product directory.
- **Limited-line monitoring:** Watch `availabilityLabel` and `onSale` for dated or while-stocks-last items, then record each run before the line disappears from the catalogue.
- **Content feeds:** Use names, pack sizes, links, and image galleries to maintain a current catalogue feed. Refresh it regularly because prices and availability notes change.
- **Product checks:** Pass a known product URL to retrieve its current record and any detail description. A removed product may return no row.

For a price history, store a run date alongside each exported dataset. The catalogue publishes current records; it is not a historical price service. Joining successive exports on `sku` is more reliable than joining on a product name, because labels can be edited. If an item disappears, check a later run before treating the absence as a permanent delisting.

### How much does it cost to scrape ALDI?

The current charge is **$1.00 per 1,000 saved product rows**, or **$0.001 per row**. The charge applies to delivered dataset items. A duplicate found under another input is not saved again and does not create another result charge. Platform usage is included in the per-result pricing model.

| Saved products | Result charge |
| ---: | ---: |
| 60 | $0.06 |
| 500 | $0.50 |
| 1,000 | $1.00 |
| 5,000 | $5.00 |

For example, one term capped at 60 products costs $0.06 if all 60 are saved. Five separate terms capped at 100 each could produce at most 500 unique rows, or $0.50, before the global cap is considered. A category with 1,035 available products would cost about $1.04 if all were saved at the current rate. The exact charge depends on how many records the site currently returns and whether the inputs overlap.

Use `maxItems` to put a firm ceiling on the number of charged results in a run. `maxResults` controls each input individually, so multiply that value by the number of terms and URLs when estimating the maximum. A cap of `0` means no chosen result cap, subject to the site's own paging limits.

### Tips for best results

Use one specific term when you want a tightly related set of products. A broad term can match descriptions or category context as well as product titles, and the site's relevance order may place related items before the exact item you expected. Check the first page before raising the cap. For stable periodic exports, name order makes changes easier to inspect than relevance order.

Use category URLs to cover a department, especially when your goal is assortment breadth. The search endpoint paginates through the public results; the actor requests up to 60 products per page and stops when it reaches the reported total or an input limit. A large category may take more requests than a narrow search. If one category is very broad, use its more specific category URLs to divide the collection into smaller runs.

Use a direct product URL when you need detail text or want to check a known SKU. Product descriptions are not included in the ordinary search listing. If you need descriptions on every result, search results alone cannot provide them; collect the individual product URLs separately. Keep the first dataset as your index so you can decide which details are worth collecting.

Treat `pricePerUnit` as a published text label. It can use kilograms, grams, litres, or counts depending on the product. Normalize the unit in your own analysis before comparing two rows. `price` is a numeric AUD amount converted from the catalogue's minor-unit value, suitable for arithmetic without parsing a currency symbol.

### Limits

This actor covers the public **Australian** catalogue. The root `aldi.com` address is a gateway to regional sites, so URLs from other countries are outside this actor's scope. The catalogue can change products, prices, imagery, category placement, and URL slugs between runs. A product link that has disappeared may no longer return data.

The public search endpoint accepts only specific page sizes; this actor uses 60, which was confirmed by a live response. The site configuration sets a maximum offset of 9,999. Very large single queries can stop at that boundary. Split such collections by category or narrower search terms. No login is needed for the public product data used here.

Some fields are optional at the source. Unbranded produce may omit `brand`; alcohol and other items may omit a unit comparison; product detail text is only requested for direct product URLs; previous prices appear only if published. The catalogue's discontinued flag and public listing do not prove store-level stock. The actor does not claim a price reduction from a limited-run label.

### Programmatic workflows

The input and output fields are designed for repeatable data collection. One workflow can request a category URL every morning, keep only `sku`, `price`, `pricePerUnit`, and `availabilityLabel`, then compare them with the previous export. Another can search a list of grocery terms, apply a total result cap, and save the dataset into a product database keyed by SKU. Use the field descriptions in the input schema when building automated calls.

When requesting several terms, keep a note of the first input order if you use `searchTerm` or `sourceUrl`. Since rows are deduplicated, the first matching source owns the provenance field. The product itself is the same either way. If source-specific membership is important, run the terms separately or keep a separate record of each query's matches.

### FAQ

#### Is this suitable for public product research?

Yes. It collects public catalogue product information. Review the site's terms and applicable law for your use, respect privacy rules, and avoid using the data for deceptive or unlawful purposes. This actor does not use an account or retrieve private customer records.

#### Do I need to choose a proxy?

Usually no. The default connection uses the platform's proxy and the actor handles retry and connection changes when needed. Leave the proxy input at its default unless you have a specific connection requirement.

#### How fast is a run?

Duration depends on the number of pages, response times, and the number of products saved. A 130-product golden run crossed a search page boundary and included a short category URL. A small search needs fewer requests than a full category. Start with a modest cap to estimate time for your own input.

#### Can I schedule price monitoring?

Yes. Schedule repeated runs, export each dataset, and compare prices by SKU. Record when each run occurred. The actor returns the current catalogue record, so it does not recreate prices from before you began collecting.

#### Can I export to a spreadsheet?

Yes. Download the dataset as CSV or a spreadsheet file, or send its JSON rows to your reporting system. Numeric `price` makes totals and comparisons straightforward. `pricePerUnit` remains a display string and needs unit parsing for cross-product calculations.

#### Why is a field missing from a row?

The source did not publish that value in the requested view. Search listings provide pricing and product metadata, while a direct product URL may include a description. Optional fields are left out rather than filled with invented values.

#### Is `onSale` a discount flag?

No. It indicates a published availability note. Read `availabilityLabel` for the actual message. A temporary product can have an availability note without a reduced shelf price, and a price change can happen without such a note.

### Integrations

Use the dataset endpoint for programmatic retrieval, set a webhook for completion, or schedule recurring runs. Downloaded CSV and spreadsheet exports fit common reporting workflows. A downstream automation can compare saved SKUs, detect price or label changes, and update a catalogue or alert system. Keep the original product URL and the run date with each export so changes remain traceable.

### Support

Open an issue on the Issues tab; we reply within 24 hours and add fields on request. Include a public input URL, the expected field, and a recent run reference so the product or category can be checked against the current catalogue.

# Actor input Schema

## `searchTerms` (type: `array`):

Product keywords to search on the Australian catalogue. Each term is searched separately.

## `startUrls` (type: `array`):

Public Australian search, category, or product URLs. Short category URLs are followed to their catalogue destination.

## `maxResults` (type: `integer`):

Maximum new products saved from each term or URL. Set 0 to collect all available pages.

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

Hard cap across all terms and URLs. Set 0 for no overall cap.

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

Order products within each search or category. A non-default choice overrides a sort already in a URL.

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

Proxy connection for public catalogue requests.

## Actor input object example

```json
{
  "searchTerms": [
    "cheese"
  ],
  "startUrls": [],
  "maxResults": 100,
  "maxItems": 0,
  "sortBy": "relevance",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Extracted products in the default dataset.

# 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 = {
    "searchTerms": [
        "cheese"
    ],
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/aldi-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 = {
    "searchTerms": ["cheese"],
    "startUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/aldi-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 '{
  "searchTerms": [
    "cheese"
  ],
  "startUrls": []
}' |
apify call mlg14/aldi-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/aldi-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/lIHTFpxK0XJ1A2ndf/builds/gnjNSIbEIgRLEG6sY/openapi.json
