# Australian Grocery Prices – Woolworths & ALDI (`tildekai/au-grocery-prices`) Actor

Product prices, unit prices and specials from Woolworths and ALDI Australia as clean JSON.

- **URL**: https://apify.com/tildekai/au-grocery-prices.md
- **Developed by:** [Attila Kis](https://apify.com/tildekai) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.

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

## Australian Grocery Prices – Woolworths & ALDI

**Woolworths and ALDI Australia prices, unit prices and specials in one clean JSON format — with a monitor mode that returns only what changed. You pay only for products with a price.**

### Why this Actor?

- **Two retailers, one schema.** Woolworths and ALDI products come with the same fields and normalized units, so you can compare them directly — no per-site parsing on your side.
- **You pay only for products with a price.** Products without a price, failed searches and blocked requests are free.
- **Only what changed.** Monitor mode returns new products, price changes, special changes and removed products since your last run — ideal for daily schedules.
- **Fails loudly, never silently.** Every product is validated; if a website changes its layout, the run fails with a clear message instead of returning wrong data.
- **Honest rankings.** Woolworths sponsored products are left out by default, so `position` is the real search rank.
- **Fast and light.** A typical search of both retailers takes about 20 seconds.

### What you get

- **Prices you can compare:** shelf price, unit price with a normalized unit (`100g`, `1kg`, `1L`, `100ml`, `1ea`), size, brand, category.
- **Specials:** Woolworths *Half Price*, *Multi-buy* ("2 for $10.00") and *was / now* specials; ALDI *Super Savers* and *Lower Prices*.
- **Barcodes (GTIN)** for Woolworths products.
- **Search, category pages or product lists** as input — mix them in one run.
- **Monitor mode:** schedule the Actor and get only new products, price changes, special changes and removed products since the last run.
- **Coles is on the roadmap** as the third retailer — in the same output format, so your integration will not need to change.

#### Sample output

Two real items from a search for "milk" (2026-09-30):

```json
[
  {
    "retailer": "woolworths",
    "productId": "44528",
    "barcode": "9322042000048",
    "name": "Norco Full Cream Milk 2L",
    "brand": "Norco",
    "size": "2L",
    "price": 4.5,
    "unitPrice": 2.25,
    "unitPriceUnit": "1L",
    "special": { "label": "Special", "wasPrice": 5.05, "saving": 0.55, "multibuy": null },
    "availability": "in_stock",
    "category": "Full Cream Milk",
    "productUrl": "https://www.woolworths.com.au/shop/productdetails/44528/norco-full-cream-milk",
    "source": "search:milk",
    "sponsored": false,
    "position": 6,
    "change": null,
    "previousPrice": null,
    "previousSpecialLabel": null,
    "scrapedAt": "2026-09-30T08:39:25.447110Z"
  },
  {
    "retailer": "aldi",
    "productId": "000000000538441002",
    "barcode": null,
    "name": "Milk Mouse - Milk Chocolate 168g",
    "brand": "CHOCEUR",
    "size": "168 g",
    "price": 3.99,
    "unitPrice": 2.38,
    "unitPriceUnit": "100g",
    "special": null,
    "availability": "unknown",
    "category": "Confectionery",
    "productUrl": "https://www.aldi.com.au/product/choceur-milk-mouse-milk-chocolate-168g-000000000538441002",
    "source": "search:milk",
    "sponsored": false,
    "position": 1,
    "change": null,
    "previousPrice": null,
    "previousSpecialLabel": null,
    "scrapedAt": "2026-09-30T08:39:37.451137Z"
  }
]
```

### Use cases

- **Price comparison apps and websites** — compare the same basket at Woolworths and ALDI by unit price.
- **Brand and category monitoring** — track your products and your competitors' shelf prices and specials every day.
- **Specials tracking** — find this week's half-price and multi-buy offers.
- **Market and inflation research** — build your own grocery price history with scheduled runs.
- **Deal sites and newsletters** — publish the best specials automatically.

### How to use

#### 1. One-off search

```json
{
  "searchQueries": ["milk", "coffee beans", "olive oil"],
  "maxItemsPerScope": 100
}
```

#### 2. Daily monitoring of categories (schedule it)

Create a [schedule](https://docs.apify.com/platform/schedules) that runs this input every day. The first run outputs every product as `new`; later runs output only changes.

```json
{
  "categories": [
    "https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk",
    "https://www.aldi.com.au/products/dairy-eggs-fridge/k/960000000"
  ],
  "maxItemsPerScope": 1000,
  "monitorMode": true
}
```

#### 3. Your own product list

Paste product page URLs. The retailer is detected from the URL.

```json
{
  "products": [
    "https://www.woolworths.com.au/shop/productdetails/44528/norco-full-cream-milk",
    "https://www.aldi.com.au/product/farmdale-light-milk-2l-000000000000398691"
  ]
}
```

Only specials: add `"onlySpecials": true` to any input.

### Pricing, explained

This Actor uses **pay-per-event** pricing. Platform usage is included.

| Event | Price | When |
|---|---|---|
| Product | **$1.00 / 1,000** | A product **with a price** in the output (in monitor mode also a removed product) |
| Checked product (monitor mode) | **$0.30 / 1,000** | A product that monitor mode checked but did not output because it did not change |
| Actor start | $0.00005 | Once per run |

**Free:** products without a price, and everything from searches or pages that failed or were blocked.

Examples:

- **1,000 products** from searches or categories: **$1.00**.
- **Monitoring 2,000 products daily**, about 5% change per day: first run $2.00 (all new), then about **$0.67 per day** (100 changes × $0.001 + 1,900 unchanged × $0.0003) — about $20 per month.

Set a **maximum cost per run** in the run options; the Actor stops cleanly when it is reached and tells you so in the status message.

### Reliability

- Every product is validated before it is output; a product that does not fit the schema is skipped and counted, never output half-empty.
- Blocked or failed requests are retried with backoff and a fresh session. One failing search never stops the others.
- **If a website changes its layout, the run fails loudly** ("… seems to have changed its website …") instead of returning bad data.
- Every run writes a `RUN_SUMMARY` record to its key-value store: counts, failed requests by type, field coverage and billed events.

### Integrations

#### API (run and get the results in one call)

```bash
curl -X POST "https://api.apify.com/v2/acts/tildekai~au-grocery-prices/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["milk"], "maxItemsPerScope": 50}'
```

Synchronous runs time out after 300 seconds; for larger runs, start the run asynchronously and read the dataset when it finishes. Keep your API token on your server, never in a browser.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("tildekai/au-grocery-prices").call(run_input={"searchQueries": ["milk"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["retailer"], item["name"], item["price"])
```

#### No-code and AI tools

Works with Apify's integrations for Make, Zapier, n8n, Google Sheets and webhooks, and with the Apify MCP server for AI assistants.

### FAQ

**How fresh is the data?** Each run reads the websites live, so prices are as shown at the time of the run (`scrapedAt`).

**Are the prices for my state or store?** They are the **national online prices** that the websites show without a selected store. For Woolworths this is the default online price (the same as a regular NSW supermarket in our checks). Most products cost the same in every state, but some fresh products (for example branded milk) can differ slightly by state, and small CBD *Metro* stores can be dearer. ALDI shows one national price.

**Why does a product have no price?** The website did not show a price for it (for example, the product is unavailable online). Such products are output with `"price": null` and are **free**.

**Why is ALDI `availability` always `unknown`?** ALDI Australia sells groceries in store only and does not publish stock levels.

**Why are barcodes only for Woolworths?** ALDI's website does not publish them.

**What counts as a special?** Woolworths: *Half Price*, *Multi-buy* (`multibuy` shows the offer, e.g. "2 for $10.00") and *Special* (a was-price above the price). ALDI: products in the *Super Savers* and *Lower Prices* groups (ALDI does not show a was-price).

**How does monitor mode remember the last run?** It keeps its state in a named key-value store in **your** Apify account (`au-grocery-monitor-<key>`). The key is derived from your input, so a different list of searches starts a new history; set `monitorKey` to name it yourself. A product is reported as `removed` only when its search or category was read completely.

**Is Coles included?** Not yet — Coles is on our roadmap as the third retailer. It will use the same output schema, so anything you build on Woolworths and ALDI data today will work with Coles too.

**Legal:** This Actor extracts publicly available product listings and prices. It is not affiliated with Woolworths or ALDI. You are responsible for how you use the data.

### Limitations

- Prices and specials are as displayed at the time of the run; they can change during the day.
- National online prices only (see the FAQ).
- Up to 5,000 products per search or category, and up to 1,000 product URLs per run.
- Search results follow the websites' own relevance ranking.

### Changelog

See [CHANGELOG.md](CHANGELOG.md). Latest:

- **0.8** — Dataset views "Prices" and "Changes (monitor mode)".
- **0.7** — Default memory 512 MB (cheaper runs).
- **0.6** — Pay-per-event pricing: only products with a price are billed; charge limits stop runs cleanly.

### Input reference

| Field | Type | Default | Description |
|---|---|---|---|
| `retailers` | array | `["aldi", "woolworths"]` | Retailers to search. |
| `searchQueries` | array of strings | — | Up to 50 searches; each runs at every selected retailer. |
| `categories` | array of URLs | — | Up to 50 category page URLs (Woolworths or ALDI). |
| `products` | array of URLs | — | Up to 1,000 product page URLs. |
| `maxItemsPerScope` | integer | 50 | Maximum products per search or category (1–5,000). |
| `onlySpecials` | boolean | false | Output only products on special. |
| `includeSponsored` | boolean | false | Woolworths: include sponsored products (flagged `sponsored: true`). |
| `monitorMode` | boolean | false | Output only changes since the previous run. |
| `monitorKey` | string | derived | Name of the monitor state (`a-z`, `0-9`, `-`, up to 40 characters). |
| `emitUnchanged` | boolean | false | Monitor mode: also output unchanged products (billed as products). |
| `proxyConfiguration` | object | no proxy | Proxy settings; not needed normally. |
| `maxConcurrency` | integer | 3 | Parallel searches / categories per retailer (1–10). |

At least one of `searchQueries`, `categories` or `products` is required.

# Changelog

This Actor's version history is a separate document: https://apify.com/tildekai/au-grocery-prices/changelog.md

# Actor input Schema

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

Which retailers to search.

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

One search per line, e.g. 'milk' or 'coffee beans'. Each query runs at every selected retailer.

## `categories` (type: `array`):

Category pages to list, e.g. https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk or https://www.aldi.com.au/products/dairy-eggs-fridge/k/960000000

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

Product pages to check, e.g. https://www.woolworths.com.au/shop/productdetails/88436/... or https://www.aldi.com.au/product/farmdale-light-milk-2l-000000000000398691

## `maxItemsPerScope` (type: `integer`):

Stop a search after this many products.

## `onlySpecials` (type: `boolean`):

Return only products with a special (half price, multi-buy, was-price, ALDI 'Super Savers' / 'Lower Prices').

## `includeSponsored` (type: `boolean`):

Woolworths shows 8 sponsored products on every result page. Off: they are skipped and 'position' is the organic rank. On: they are included with 'sponsored': true and 'position' is the order on the page.

## `monitorMode` (type: `boolean`):

Output only new, price-changed, special-changed and removed products since the previous run. The first run outputs every product as 'new'.

## `monitorKey` (type: `string`):

Optional name of the monitor state (lowercase letters, digits, '-'). Leave empty: the key is derived from the searches, categories and products, so a changed list starts a new state.

## `emitUnchanged` (type: `boolean`):

Monitor mode only: also output products that did not change (change = 'unchanged').

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

Woolworths and ALDI work without a proxy (tested). Use a proxy only if you see blocked requests in the run summary.

## `maxConcurrency` (type: `integer`):

How many searches / categories run at the same time at one retailer. Keep it low to be polite to the sites.

## Actor input object example

```json
{
  "retailers": [
    "aldi",
    "woolworths"
  ],
  "searchQueries": [
    "milk"
  ],
  "maxItemsPerScope": 50,
  "onlySpecials": false,
  "includeSponsored": false,
  "monitorMode": false,
  "emitUnchanged": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxConcurrency": 3
}
```

# Actor output Schema

## `prices` (type: `string`):

No description

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

No description

## `runSummary` (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 = {
    "searchQueries": [
        "milk"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tildekai/au-grocery-prices").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": ["milk"] }

# Run the Actor and wait for it to finish
run = client.actor("tildekai/au-grocery-prices").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": [
    "milk"
  ]
}' |
apify call tildekai/au-grocery-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tildekai/au-grocery-prices"
        }
    }
}
```

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/eFZtrt8o8epUB6VVU/builds/ryTntaPCPRWUycgWZ/openapi.json
