# Coles, Woolworths & Aldi Price Scraper - No Login (`cliqtomedia/au-supermarket-prices`) Actor

Get Woolworths, Coles and Aldi Australia product prices in one table: current and was price, unit price, specials and promo text, stock, category, barcode and links. Search by keyword, category URL or product URL. No login.

- **URL**: https://apify.com/cliqtomedia/au-supermarket-prices.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.38 / 1,000 product saveds

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

Get Coles, Woolworths and Aldi Australia prices in one table. Search by keyword, or paste category or product links. Each row has the price, was price, unit price, special and promo text, stock, brand, size and link. No login or API key. A 20-product Woolworths search costs $0.014 on the Free plan.

Use it to compare supermarket prices, track specials or feed a grocery price app. The smallest run is one keyword in one store, and it finishes in a few seconds.

![Coles, Woolworths & Aldi prices: search milk in Woolworths, Coles and Aldi and get price, was price and unit price in one table, then export JSON or CSV](https://api.apify.com/v2/key-value-stores/zimogCyxHrpoWXDCf/records/HERO-v2.png)

### Table of contents

- [What it does](#what-it-does)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Input examples](#input-examples)
- [Output](#output)
- [Pricing and limits](#pricing-and-limits)
- [API, export and integrations](#api-export-and-integrations)
- [Limits and data notes](#limits-and-data-notes)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Support](#support)

### What it does

The Actor reads public product listings on three Australian supermarket websites: [Woolworths](https://www.woolworths.com.au), [Coles](https://www.coles.com.au) and [Aldi](https://www.aldi.com.au). It saves one row per product. All three stores use the same fields, so you can sort and compare them in one table.

There are three ways to choose products:

- **Search terms**: each term is searched in every store you pick.
- **Category links**: a category page from a store website, for example Coles milk.
- **Product links**: one product page per link.

You can keep only products on special. Prices are the default online prices of each store. The Actor does not pick a local store or postcode.

#### Good fit

- Compare the price of the same product type in Coles, Woolworths and Aldi.
- Watch specials and was prices for a list of products or a category.
- Get unit prices (per litre, per kg, per 100 g) for fair comparison.
- Build price history: schedule the same input and keep each run's rows.

#### Not a fit

- Prices for one store branch or postcode, or delivery prices.
- Nutrition facts, ingredients or product descriptions. They are not collected.
- Other stores, such as IGA, or stores outside Australia.
- Login, loyalty prices (Flybuys, Everyday Rewards) or carts.

### Quick start

1. Open the **Input** tab.
2. Keep **Search terms** as `milk` and **Stores** as `Woolworths` (the prefill).
3. Keep **Max products per search or category** at `20`.
4. Click **Start**.
5. Open the **Prices** view of the dataset. Check `store`, `name`, `price` and `unitPriceText`.

Expected result: 20 Woolworths milk products with prices, in about 4 seconds.

### Use cases

| Task | Input | Key fields | Next step |
| --- | --- | --- | --- |
| Compare three supermarkets | `searchTerms` + all three `stores` | `store`, `price`, `unitPrice`, `unitOfMeasure` | Sort by `unitPrice` in a spreadsheet |
| Track specials in a category | `categoryUrls` + `onlyOnSpecial: true` | `price`, `wasPrice`, `savings`, `promoText` | Schedule a daily run |
| Watch a fixed product list | `productUrls` | `productId`, `price`, `onSpecial`, `inStock` | Compare rows from each run by `store` + `productId` |
| Feed a price app or dashboard | Any mode | `name`, `brand`, `size`, `image`, `url` | Read the dataset by API |

### Input

At least one of `searchTerms`, `categoryUrls` or `productUrls` is required. Duplicate terms and links are removed.

| Field | Type | Default / prefill | Allowed values | What it does |
| --- | --- | --- | --- | --- |
| `searchTerms` | array of strings | empty / `["milk"]` | Up to 50 terms | Each term is searched in every store from `stores`. Example: `coffee pods`, `Tim Tam`. |
| `stores` | array | all three / `["woolworths"]` | `woolworths`, `coles`, `aldi` | Stores for `searchTerms`. An empty list means all three. It does not change category or product links. |
| `maxItems` | integer | `50` / `20` | 1–1000 | Products to save for each search term in each store, and for each category link. |
| `onlyOnSpecial` | boolean | `false` | true / false | Keep only discounted or multi-buy products in searches and categories. It reads up to 25 store pages per search or category. |
| `categoryUrls` | array of strings | empty | Up to 50 links from the three store websites | Products from a category page. The store comes from the link. |
| `productUrls` | array of strings | empty | Up to 500 links from the three store websites | One product per link. |

Links from other websites are rejected with a clear message before any page is read.

#### Settings that work together

- `searchTerms` × `stores`: 2 terms in 3 stores make 6 searches. `maxItems` applies to each of them, so the run can save up to 6 × `maxItems` products.
- `onlyOnSpecial` with `maxItems`: the Actor reads pages until it has `maxItems` specials, the listing ends or 25 pages are read. Each page read is charged, also when it has no specials.
- `onlyOnSpecial` does not filter `productUrls`. Use the `onSpecial` field instead.
- The same product in two searches gives two rows, one per search. Within one search each product appears once.

### Input examples

#### Compare milk in all three stores

```json
{"searchTerms":["milk"],"stores":["woolworths","coles","aldi"],"maxItems":20}
```

Returns up to 60 rows: 20 from each store. Costs about $0.038 on the Free plan.

#### Specials in a Coles category

```json
{"categoryUrls":["https://www.coles.com.au/browse/dairy-eggs-fridge/milk"],"maxItems":100,"onlyOnSpecial":true}
```

Returns up to 100 milk products on special. Every row has `onSpecial: true`, and most have a `wasPrice`.

#### A fixed list of products

```json
{"productUrls":["https://www.woolworths.com.au/shop/productdetails/888140","https://www.coles.com.au/product/coles-full-cream-milk-3l-8150288"]}
```

Returns one row for each product that exists. A product that no longer exists gives no row and is listed as `not_found` in the run summary.

### Output

One dataset row is one product in one store. The **Prices** view shows the main columns. The **All fields** view shows all fields. Download JSON, CSV or Excel from the dataset.

#### Output example

```json
{
  "store": "coles",
  "productId": "3034279",
  "name": "Big M Banana Flavoured Milk | 600mL",
  "brand": "Big M",
  "size": "600mL",
  "price": 3.45,
  "wasPrice": 3.9,
  "savings": 0.45,
  "unitPrice": 5.75,
  "unitOfMeasure": "1L",
  "unitPriceText": "$5.75 / 1L",
  "onSpecial": true,
  "promoText": "Save $0.45",
  "category": "Dairy, Eggs & Fridge > Milk",
  "inStock": false,
  "isSponsored": false,
  "barcode": null,
  "url": "https://www.coles.com.au/product/big-m-banana-flavoured-milk-600ml-3034279",
  "image": "https://cdn.productimages.coles.com.au/productimages/3/3034279.jpg",
  "currency": "AUD",
  "sourceType": "search",
  "query": "milk",
  "sourceUrl": null,
  "position": 15,
  "fetchedAt": "2026-10-14T11:29:52.330Z"
}
```

This is one full row from a `milk` search in October 2026. `null` means the store did not show this value. A missing price is `null`, never `0`.

#### Output fields

| Field | Type | Can be null? | Meaning |
| --- | --- | --- | --- |
| `store` | string | no | `woolworths`, `coles` or `aldi`. |
| `productId` | string | no | The store's own product ID (stockcode or SKU). |
| `name` | string | no | Product name as the store shows it. |
| `brand` | string | yes | Brand, such as `Big M`. |
| `size` | string | yes | Pack size, such as `3L` or `800g`. |
| `price` | number | yes | Current price in AUD. |
| `wasPrice` | number | yes | Price before the discount. Only when the product is on special. |
| `savings` | number | yes | `wasPrice` minus `price`. |
| `unitPrice` | number | yes | Price per comparison unit. |
| `unitOfMeasure` | string | yes | Comparison unit, such as `1L`, `1kg` or `100g`. |
| `unitPriceText` | string | yes | Unit price as the store shows it, such as `$1.65 / 1L`. |
| `onSpecial` | boolean | no | `true` when the product is discounted or on a multi-buy offer. |
| `promoText` | string | yes | Offer text, such as `Save $0.45` or `Pick any 3 for $7.50`. |
| `category` | string | yes | Store category, such as `Dairy, Eggs & Fridge > Milk`. |
| `inStock` | boolean | yes | Online availability. Always `null` for Aldi, because Aldi does not sell online. |
| `isSponsored` | boolean | no | `true` for a paid (sponsored) position in search results. |
| `barcode` | string | yes | GTIN barcode. Woolworths shows it; Coles and Aldi usually do not. |
| `url` | string | no | Product page link. |
| `image` | string | yes | Product image link. |
| `currency` | string | no | Always `AUD`. |
| `sourceType` | string | no | `search`, `category` or `product`: the input that gave this row. |
| `query` | string | yes | The search term, for search rows. |
| `sourceUrl` | string | yes | The category or product link, for those rows. |
| `position` | integer | yes | Place in the store's results (before the specials filter). |
| `fetchedAt` | string | no | Time the row was read, ISO UTC. |

#### Run summary

The key-value store record `RUN_SUMMARY` has `status`, `reason`, `message`, counts per store and per search or link, warnings and request statistics. The run's **Output** tab links to it.

| Situation | Dataset | Status / run | What to do |
| --- | --- | --- | --- |
| Products saved | One row per product | `complete`, succeeded | Export the rows. |
| No product matches | Empty | `not_found`, succeeded | Check spelling or try a wider term. |
| Spending limit reached | Rows saved so far | `partial` + `CHARGE_LIMIT`, succeeded | Raise the maximum cost per run. |
| Run timeout reached | Rows saved so far | `partial` + `TIME_LIMIT`, succeeded | Raise the timeout or split the input. |
| A store blocked or failed | Rows from other stores and pages stay | `partial` or `error` + `SOURCE_ERROR`, failed | Check `tasks` in the summary and retry later. |
| Invalid input | Empty | `invalid_input`, failed | Read the message; fix the field it names. |

### Pricing and limits

Pay per event. The price includes proxy and platform usage. There are three events:

| Event | Price | When it is charged |
| --- | ---: | --- |
| Actor start | $0.002 | Once per run (per GB of memory; the default 256 MB counts as one). |
| Store page read | $0.003 | Each search, category or product page read from a store. One page has up to 36 (Woolworths), 48 (Coles) or 60 (Aldi) products. |
| Product | $0.00045 – $0.00038 | Each saved row. The price depends on your Apify plan (see below). |

| Apify plan | Per product | Per 1,000 products, with pages and start |
| --- | ---: | ---: |
| Free | $0.00045 | about $0.50–$0.54 |
| Bronze | $0.00043 | about $0.48–$0.52 |
| Silver | $0.0004 | about $0.45–$0.49 |
| Gold and higher | $0.00038 | about $0.43–$0.47 |

A page that a store blocked or failed to return is not charged. A product page for a product that no longer exists is charged as a page read, with no product event.

#### Cost examples (Free plan)

| Scenario | Input | Pages | Products | Total |
| --- | --- | ---: | ---: | ---: |
| Prefill | `milk`, Woolworths, 20 | 1 | 20 | $0.014 |
| Three stores | `milk`, all stores, 20 each | 3 | 60 | $0.038 |
| No matches | an unknown word, all stores | 3 | 0 | $0.011 |
| Specials only | `rice`, Coles, `onlyOnSpecial` | 15 | 277 | $0.172 |
| Large search | 1,000 Woolworths products | 28 | 1,000 | $0.536 |
| Product links | 100 product URLs | 100 | 100 | $0.347 |

These totals use the listed event prices and the page and product counts from test runs. Searches with many products are the cheapest per product. Product links cost more, because each link is one page read.

Set a **maximum cost per run** to control spending. The Actor checks it before each page read and each row. When the limit is reached, the run stops and keeps the rows saved so far (`CHARGE_LIMIT`, succeeded).

#### Speed and size

- Default memory is 256 MB; the maximum is 512 MB. More memory does not make runs faster.
- In tests, 20 products took about 4 seconds, 1,800 products took 36 seconds and 5,000 products took 2 minutes.
- Product links are slower: one page per link, about 1 second each. 500 links took about 9 minutes.
- The default timeout is 300 seconds. For more than about 5,000 products or more than about 250 product links, raise the run timeout (for example to 900 seconds) or split the input into several runs.

### API, export and integrations

Start a run with the [Apify API](https://docs.apify.com/api/v2/act-runs-post) and the same JSON input as in the examples:

`POST https://api.apify.com/v2/acts/cliqtomedia~au-supermarket-prices/runs`

Read the default dataset when the run finishes. Export JSON, CSV or Excel. Use `store` + `productId` as the key when you join rows from different runs. Keep your Apify token private.

You can create an Apify schedule yourself to collect prices every day or week. This Actor does not create schedules or send alerts.

#### Use with AI agents (MCP)

[Apify MCP](https://mcp.apify.com?tools=cliqtomedia/au-supermarket-prices) gives agents access to this Actor. The smallest useful input is:

```json
{"searchTerms":["milk"],"stores":["woolworths"],"maxItems":20}
```

Key output fields: `store` names the supermarket, `name` and `size` describe the product, `price` and `wasPrice` are in AUD, `unitPrice` with `unitOfMeasure` allows fair comparison, and `onSpecial` marks a discount. This run takes about 4 seconds and costs $0.014 on the Free plan. Limits: up to 1,000 products per search or category, 50 search terms, 50 category links and 500 product links per run.

### Limits and data notes

- **Online prices.** Prices are the default online prices of each store. Local store and postcode prices can differ.
- **Aldi.** Aldi does not sell online, so `inStock` is always `null`. Aldi shows few specials on its website, so `wasPrice` and `promoText` are usually `null` and `onlyOnSpecial` often returns no Aldi rows.
- **Names.** Each store writes names its own way. Coles adds the size after a bar, for example `Coles Full Cream Milk | 3L`; the size is also in `size`.
- **Coles product links.** Coles finds a product by the number at the end of the link. If the name part of the link is wrong but the number exists, you get that product.
- **Sponsored results.** Store search results can include paid positions. They are kept and marked with `isSponsored: true`.
- **Not complete.** Store search decides which products match a term. The Actor does not promise every product of a store.
- **Changes.** If a store changes its website, a run can fail with `SOURCE_ERROR` until the Actor is updated.

Data comes from the public websites of Woolworths, Coles and Aldi Australia. Their website terms limit automated access and commercial use of their content. You are responsible for checking that your use of the data is allowed. This Actor is an independent tool. It is not made by or endorsed by Woolworths, Coles or Aldi.

### Troubleshooting

#### The dataset is empty

Open `RUN_SUMMARY`. `not_found` means the stores returned no products for your terms or links. Check the spelling, or try a shorter term. With `onlyOnSpecial`, there may be no specials in the first 25 pages; the summary warns about this.

#### One store has no rows

Check `tasks` in `RUN_SUMMARY`. `not_found` means that store has no match. `error` with `BLOCKED` or `SOURCE_UNAVAILABLE` means the store did not answer after retries. Rows from other stores stay. Try again later.

#### The run stopped early

`CHARGE_LIMIT` means the maximum cost per run was reached; raise it. `TIME_LIMIT` means the run timeout was reached; raise the timeout or split the input. In both cases the run succeeds and keeps saved rows.

#### "… is not a woolworths.com.au, coles.com.au or aldi.com.au link"

Only links from woolworths.com.au, coles.com.au and aldi.com.au are accepted. Copy the link from the product or category page in your browser.

### FAQ

#### Do I need a store account or API key?

No. The Actor reads public pages. You only need an Apify account.

#### Are these the prices in my local store?

They are the default online prices. Prices in a local store or another state can be different.

#### Does an empty search cost money?

Yes, but little. It charges the start and the pages read. A search for an unknown word in all three stores costs $0.011.

#### Can I get price history?

Each run gives the current prices. Schedule the same input and keep the datasets to build your own history.

#### Why is the same product in two rows?

You searched for two terms and the product matched both. Each search keeps its own rows. Remove duplicates by `store` + `productId` if you need one row per product.

### Support

Open an issue in this Actor's **Issues** tab. Include the run ID, your input (without private data) and the error text from `RUN_SUMMARY`. Never share your Apify token.

# Changelog

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

# Actor input Schema

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

Products to search for, e.g. milk, coffee pods or Tim Tam. Each term is searched in every store picked below. Up to 50 terms.

## `stores` (type: `array`):

Supermarkets to search. Category and product URLs always go to the store they belong to. Default: all three.

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

How many products to save for each search term in each store, and for each category URL. Product URLs always return one product each.

## `onlyOnSpecial` (type: `boolean`):

Keep only products that are discounted or on a multi-buy offer. Applies to searches and categories, not to product URLs. Reads up to 25 store pages per search or category to find specials; each page read is charged.

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

Category pages copied from the store website, e.g. https://www.coles.com.au/browse/dairy-eggs-fridge/milk, https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk or https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160. Coles https://www.coles.com.au/on-special also works. Up to 50.

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

Product pages copied from the store website, e.g. https://www.woolworths.com.au/shop/productdetails/888140/... or https://www.coles.com.au/product/coles-full-cream-milk-3l-8150288. Up to 500.

## Actor input object example

```json
{
  "searchTerms": [
    "milk"
  ],
  "stores": [
    "woolworths"
  ],
  "maxItems": 20,
  "onlyOnSpecial": false
}
```

# Actor output Schema

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

One row per product: store, name, price, was price, unit price, special and link.

## `details` (type: `string`):

Brand, size, category, barcode, image, sponsored flag and where each row came from.

## `runSummary` (type: `string`):

Status, reason, counts per store and per search, warnings and request statistics.

# 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": [
        "milk"
    ],
    "stores": [
        "woolworths"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/au-supermarket-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 = {
    "searchTerms": ["milk"],
    "stores": ["woolworths"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/au-supermarket-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 '{
  "searchTerms": [
    "milk"
  ],
  "stores": [
    "woolworths"
  ],
  "maxItems": 20
}' |
apify call cliqtomedia/au-supermarket-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/au-supermarket-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/kXm7R92FBxrtPfiLp/builds/qM8ih2I3DKGtrmgmI/openapi.json
