# ALDI Australia Scraper — $0.32/1K · Products & Prices (`apivault_labs/aldi-australia-products-price-monitor`) Actor

Scrape ALDI Australia products, prices and Special Buys. Batch search, URL/SKU details, saved price history and change-only filters with AUD, percentage and unit-price movements. $0.32/1K rows + $0.004/start; platform usage extra.

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

## Pricing

from $0.32 / 1,000 product results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Australia Scraper — Products, Prices & Special Buys

**ALDI Australia Scraper** collects product prices, pack sizes, unit prices, images and Special Buys from aldi.com.au. Search by keyword, browse categories or supply product URLs and SKU IDs. Export JSON, CSV or Excel, call the API, or schedule your own price watch.

**$0.32 per 1,000 delivered rows + $0.004 per start event on every plan.** Apify platform usage is extra. The start event is charged per GB of memory, minimum one. An empty search has no result fee but still has the start fee. Category, calendar and filter-information rows also count as delivered rows. Each changed product produces one billable row even when several fields changed; the detailed change list and history are available in the run's key-value store.

### Start with a small search

```json
{"operation":"search","query":"milk","maxResults":25}
```

Use one specific word when possible. ALDI determines search relevance; a multi-word search can match either word. `maxResults` is a **global limit across the entire run**, including overlapping inputs. Products with the same SKU are saved once.

### What you can do

| Operation | Inputs | Result |
|---|---|---|
| `search` | `query` or `queries` | Live product search, optional brand and price filters |
| `category` | `categoryUrls`, `categoryUrl` or `categoryId` | Paginated products from an advertised category |
| `item` | `productUrls` or `productIds` | Exact product records with description and available label fields |
| `categories` | No product input needed | Current category names, IDs and URLs |
| `search_filters` | Optional `query` | Current source facets, values and sorting capabilities |
| `special_events` | No date required | Advertised Special Buys dates and themes |
| `specials` | `eventDate`, optional `theme` | Products from the selected advertised event |

No ALDI login, browser or third-party API key is required. Requests read public web data. The Actor stops and reports blocked or incomplete sources rather than inventing results.

### Examples

#### Batch search with brand, price and sorting

```json
{"operation":"search","queries":["milk","yogurt"],"brand":"FARMDALE","maxPrice":5,"sort":"price_asc","maxResults":30}
```

Source sorting supports `relevance`, `name_asc`, `name_desc`, `price_asc` and `price_desc`. Ordering applies within each input; results from several inputs follow their input order. `page` can start at a later source page for exploration, but monitoring requires page 1.

#### Collect a category

```json
{"operation":"category","categoryUrls":["https://www.aldi.com.au/products/pantry/jams-spreads/k/1111111170"],"maxResults":100}
```

The summary distinguishes a complete category from a capped or partial collection. Set a sufficient limit before using category snapshots for monitoring; the maximum supported global limit is 5,000 rows, not an unlimited-catalogue promise.

#### Exact product details

```json
{"operation":"item","productIds":["000000000000261610"],"maxResults":5}
```

Keep product IDs as strings with their leading zeros. `details=true` adds individual product-page reads to search/category/special results. Product records include `description`, `images`, `labelImages` and source-provided ingredients, allergens, origin and storage instructions where available. Missing information stays null. Label images are links, not OCR or extracted nutrition tables.

#### Discover Special Buys

```json
{"operation":"special_events","maxResults":100}
```

Copy an advertised `eventDate` and optionally `theme` from the returned rows into the `specials` operation. Dates rotate; old examples are not a promise that a past event remains available. `specialsOnly=true` keeps records with an advertised availability label or sale date; it does **not** mean a verified discount from a previous price.

#### Monitor a category and keep your observations

```json
{"operation":"category","categoryUrls":["https://www.aldi.com.au/products/pantry/jams-spreads/k/1111111170"],"maxResults":100,"monitor":true,"historyKey":"my-jam-watch","changesOnly":true,"deliverBaseline":true,"historyLimit":30}
```

Use the same input and private `historyKey` on later runs, and schedule the runs sequentially. The first complete run creates your baseline. Subsequent runs can return only products whose price, pack size, unit-price label, availability label or advertised sale date changed, new listings, and products no longer seen in the completely read scope. All changes for one product share one delivered row with `changedFields`, `before` and `after`.

`deliverBaseline=false` creates the first baseline without product rows. Unchanged later watches can return zero rows. Both still incur the start event and platform usage. `HISTORY` contains up to `historyLimit` successful observations (1–90); history starts with your first run, not past store prices. The watch retains the latest complete product snapshot and a bounded observation history in your Apify account.

#### Track meaningful price movements

```json
{"operation":"category","categoryUrls":["https://www.aldi.com.au/products/pantry/jams-spreads/k/1111111170"],"maxResults":100,"monitor":true,"historyKey":"jam-price-drops","changesOnly":true,"deliverBaseline":false,"changeFilter":"price_drop","minChangePercent":5}
```

Changed products include `previousPrice`, signed `priceChange` in AUD, `priceChangePercent`, `priceDirection`, `unitPriceChange`, `unitPriceChangePercent`, `unitPriceChangeUnit` and `packSizeChanged`. Percentages compare with the previous complete observation: `(current-previous)/previous*100`. They are observed movements, not ALDI advertised discounts. Percentages are null when the previous price is zero. Unit-price comparisons require the same published currency and normalized unit; incompatible or missing units stay null. A changed pack label is not proof of shrinkflation.

With `changesOnly=true`, choose `changeFilter`: `all`, `price_drop`, `price_rise`, `unit_price_increase` or `pack_size_change`. Optional `minChangePercent` keeps movements whose absolute percentage meets the threshold (inclusive); it uses unit price for `unit_price_increase` and product price otherwise. With a positive threshold, missing/zero-denominator percentages are excluded. New/missing listings pass only `all` with threshold zero. The initial baseline follows `deliverBaseline` independently of these filters; use false for silent setup.

Filtered-out movements are not Dataset rows and incur no result events. A complete observation still advances the baseline, so small movements do not accumulate across runs into a later threshold alert. Switching change filters does not reset the watch's collection scope. `CHANGES` retains all observed events, and `SUMMARY` reports `observedChanges`, `matchedChanges` and `suppressedChanges`; these diagnostic records have no result events. Start fees and platform usage still apply to empty checks. This prepares filtered output for your own integrations; the Actor does not send email or messages.

An incomplete, blocked, capped or undelivered snapshot **never advances the baseline or creates disappearance events**. Change-only watches fail clearly when the source scope is incomplete. Change the limit or narrow the scope before trying again. Changing inputs, filters or sort under an existing key is refused: use a new key. Overlapping runs with the same watch key are refused using an exclusive server-side lease; schedule each watch sequentially. A crash after output delivery but before state persistence may cause changes to be delivered again on the next run; deduplicate using SKU, observed time and change fields if needed.

### Output

The default Dataset contains useful product, category, event or filter rows. Product fields include:

| Group | Fields |
|---|---|
| Identity | `sku`, `name`, `brand`, `url`, `recordType`, `country` |
| Pricing | `price`, `currency`, `priceDisplay`, `packSize`, `unitPriceDisplay`, `unitPriceNormalized` |
| Product context | `categories`, `categoryIds`, `imageUrl`, `images`, `description`, `detailStatus` |
| Availability context | `availabilityLabel`, `specialBuyDate`, `onSpecial`, `ageRestricted`, `storeStock`, `priceScope` |
| Audit trail | `sourceUrl`, `collectedAt`; monitoring adds `changeType`, `changedFields`, `before`, `after` |

`unitPriceNormalized` is calculated from ALDI's published comparison label and converts supported weights to AUD/kg and volumes to AUD/litre. It is not an inferred unit price when the source label is missing.

The run also exposes `SUMMARY` (rows, completeness, per-input coverage, limits, watch status), `ERRORS`, `CHANGES`, `HISTORY` for a successful watch, and `OUTPUT` links. Diagnostics are stored outside the billable Dataset. Partial useful results can be delivered and charged; check `SUMMARY.complete` before treating them as a complete category.

### API and AI agents

Use Actor ID `faehi28wKzR4XDhPJ`. Supply the same JSON input through Apify Console, the API, Python/JavaScript clients, integrations or Apify MCP. Read the default Dataset and `SUMMARY` after the run. JSON, CSV and Excel are standard Apify exports; Google Sheets or other destinations require your own integration configuration.

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("faehi28wKzR4XDhPJ").call(
    run_input={"operation": "search", "query": "coffee", "maxResults": 10}
)
rows = client.dataset(run["defaultDatasetId"]).list_items().items
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("SUMMARY")
```

### Scope and limitations

- Australia only, aldi.com.au. This independent Actor is not affiliated with or endorsed by ALDI.
- Prices are public web observations without a selected store. Local prices and ranges can differ. `storeStock=null` is intentional: a public listing or Schema.org InStock is not proof of shelf availability at a store.
- `missing_from_complete_scope` means the product was not seen in that successfully read input scope. It does not prove out-of-stock or discontinuation.
- Exact price history before your first watch, member prices, checkout, store-specific stock, OCR nutrition and automatic cross-chain identity matching are not provided.
- Search and catalogue contents can change during collection. Changed totals, repeated pages, incomplete pages and denied requests are reported; a stable source total is a coverage check, not proof of nationwide inventory.
- No unsupported availability or discount promises: Special Buys labels can refer to future dates or limited stock rather than reduced prices.

Keep requests bounded and use the data in accordance with applicable source terms and your requirements. Start with a small result limit and inspect the summary before scheduling recurring collection.

# Actor input Schema

## `operation` (type: `string`):

Choose product search, category, exact item, discovery, filters or Special Buys.

## `query` (type: `string`):

For search. Prefer a specific word; relevance follows ALDI.

## `queries` (type: `array`):

Up to 20 phrases; one global result cap across inputs.

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

ALDI Australia category links. Use categories to discover URLs.

## `categoryUrl` (type: `string`):

Optional single category URL, alternative to categoryUrls.

## `categoryId` (type: `string`):

Numeric ID from categories, kept as a string.

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

Public ALDI Australia product URLs, maximum 20.

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

Keep leading zeros. Example: 000000000000261610.

## `eventDate` (type: `string`):

YYYY-MM-DD date currently advertised in special_events.

## `theme` (type: `string`):

Optional exact theme returned by special_events.

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

Global delivered-row cap, not per query. Watches require enough room for a complete scope.

## `sort` (type: `string`):

Sort within each input.

## `page` (type: `integer`):

Monitoring requires page 1. Later pages are partial-scope exploration.

## `brand` (type: `string`):

Source brand filter and case-insensitive exact validation.

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

Minimum observed product price in AUD.

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

Maximum observed product price in AUD.

## `specialsOnly` (type: `boolean`):

Availability labels/dates do not prove a discount.

## `details` (type: `boolean`):

Adds requests; source may leave label fields null. Exact items always include details.

## `monitor` (type: `boolean`):

Runs must be sequential; partial snapshots never replace the baseline.

## `historyKey` (type: `string`):

Required for monitoring. 1–80 letters, digits, underscores or hyphens. Same inputs on later runs.

## `changesOnly` (type: `boolean`):

Implies monitoring. Requires historyKey and complete collection.

## `deliverBaseline` (type: `boolean`):

Deliver current products on the first successful complete watch run.

## `historyLimit` (type: `integer`):

Keep this many successful complete observations, from 1 to 90.

## `changeFilter` (type: `string`):

Requires changesOnly=true for a non-default filter. Baseline delivery is controlled separately; all events remain in CHANGES.

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

Requires changesOnly=true. Inclusive threshold versus previous complete observation; unit price for unit_price_increase, product price otherwise. Filtered observations still advance baseline.

## Actor input object example

```json
{
  "operation": "search",
  "query": "milk",
  "queries": [],
  "categoryUrls": [],
  "productUrls": [],
  "productIds": [],
  "maxResults": 25,
  "sort": "relevance",
  "page": 1,
  "specialsOnly": false,
  "details": false,
  "monitor": false,
  "changesOnly": false,
  "deliverBaseline": true,
  "historyLimit": 30,
  "changeFilter": "all",
  "minChangePercent": 0
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

## `errors` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `history` (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 = {
    "query": "milk"
};

// Run the Actor and wait for it to finish
const run = await client.actor("apivault_labs/aldi-australia-products-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 = { "query": "milk" }

# Run the Actor and wait for it to finish
run = client.actor("apivault_labs/aldi-australia-products-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 '{
  "query": "milk"
}' |
apify call apivault_labs/aldi-australia-products-price-monitor --silent --output-dataset

```

## MCP server setup

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