# Coles Australia Scraper - Product, Price & Stock (`parsebird/coles-australia-scraper`) Actor

Scrape Coles Australia (coles.com.au) by keyword, category or specials URL, or SKU list. Extract price, unit price, discount, stock, and category data with optional price/stock change tracking.

- **URL**: https://apify.com/parsebird/coles-australia-scraper.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.89 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### Coles Australia Scraper

Scrape product, price, and stock data from **[Coles](https://www.coles.com.au)**, one of Australia's largest supermarket chains, by keyword, category/search/specials URL, or an exact SKU list. Get price, was-price, discount, a normalized unit price, stock availability, and category data as clean JSON, CSV, or Excel — with optional price and stock change tracking across runs.

<table><tr>
<td style="border-left:4px solid #1C1917;padding:12px 16px;font-weight:600">
Search by keyword, browse or specials URL, or scrape an exact SKU list, and get price, was-price, discount amount/percent, a normalized price-per-kg/L/each for real cross-product comparison, stock availability and quantity, and full category data — plus optional Watchlists that flag new listings, price moves, and stock changes run over run.
</td>
</tr></table>

##### Copy to your AI assistant

Copy this block into ChatGPT, Claude, Cursor, or any LLM to start using this Actor.

```
Actor: parsebird/coles-australia-scraper (Apify). Scrapes Coles Australia (coles.com.au) product, price, and stock data. Call it with ApifyClient.
Input: scrapeMode (enum, required: "searchKeyword" | "providedUrls" | "skuList"), searchQuery (string, used in searchKeyword mode, default "milk"), startUrls (array of {url}, Coles search/category/specials URLs, used in providedUrls mode), skuList (array of strings, exact Coles SKU IDs, used in skuList mode), includeDescription (boolean, default true), maxItems (integer, default 25), maxRequestsPerCrawl (integer, default 25, 0 = no limit), monitorMode (enum: "snapshot" default | "changes"), stateName (string, Watchlist name, default "default"), onlyChanged (boolean, default false), proxyConfiguration (object, default {"useApifyProxy": true} using Apify's datacenter proxy).
Output: one row per product — product_id, name, brand, description, size, availability, availability_type, available_quantity, price, original_price, on_special, discount_amount, discount_percent, currency, comparable, unit_price, unit_price_quantity, unit_price_unit, unit_price_basis, price_per_base_unit, image_url, category, sub_category, class_name, trade_profit_centre, category_group, url, scraped_at, retail_limit, promotional_limit, online_special, and in "changes" mode also change_type, old_price, price_delta, price_delta_pct.
Pricing: pay-per-event, $1.19 per 1,000 products on the Free plan (cheaper on paid plans). API docs: https://apify.com/parsebird/coles-australia-scraper/api
```

### What does Coles Australia Scraper do?

This Actor reads the same server-rendered JSON that [Coles](https://www.coles.com.au)'s own website hydrates its React app from, so every product comes back with its real price, unit price, stock, and category data — no guessing from what's visible on the page.

- 🔍 **Three ways to find products** — a plain keyword search, a list of Coles search/category/specials URLs (including `/on-special`), or an exact list of SKU IDs.
- 💲 **Real pricing detail** — current price, was-price, discount amount and percent (computed from the genuine list vs. sale price, since Coles' own discount field is unreliable), and Coles' own unit-price text.
- ⚖️ **Cross-product unit price** — Coles displays some products per-100g and others per-1kg, which makes prices impossible to compare directly. `price_per_base_unit` normalizes every product to a price per full kg, L, or each.
- 📦 **Stock data** — availability, availability type (e.g. in-store and online), and available quantity, for keyword and URL searches.
- 🏷️ **Full category hierarchy** — category, sub-category, class name, trade profit centre, and category group, straight from Coles' own merchandising taxonomy.
- 👀 **Watchlists (price & stock change tracking)** — set `monitorMode` to `changes` and a `stateName`, and every row gets a `change_type` of `NEW`, `PRICE_DROP`, `PRICE_RISE`, `BACK_IN_STOCK`, `OUT_OF_STOCK`, or `UNCHANGED` compared to that Watchlist's last run.
- 🛡️ **Handles Coles' bot protection automatically** — Coles sits behind Incapsula, which blocks plain HTTP requests outright. The Actor drives a real, stealth-configured browser session and retries on a fresh proxy session if a request gets blocked.

#### How is this different from browsing Coles by hand?

Coles' website only shows a handful of fields per product tile and requires a real session to reveal stock. This Actor reads the underlying data Coles' own site is built from, so every result already includes fields the page doesn't render at all — the raw category hierarchy (trade profit centre, category group, class name), the exact available quantity, and a properly normalized per-kg/L/each price for comparison shopping.

### Input parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| scrapeMode | enum | **Yes** | How to find products: `searchKeyword`, `providedUrls`, or `skuList`. |
| searchQuery | string | No | Search term, product name, or barcode. Used in keyword search mode. |
| startUrls | array of URLs | No | Coles search, category, or weekly specials URLs. Used in URL mode. |
| skuList | array | No | Specific Coles product SKU IDs to scrape. Used in SKU list mode. |
| includeDescription | boolean | No | Include the full product description text. Default: `true`. |
| maxItems | integer | No | Maximum number of products to scrape per run. Default: 25. |
| maxRequestsPerCrawl | integer | No | Maximum pages to load per run. Set to 0 for no limit. |
| monitorMode | enum | No | `snapshot` (default) returns current data every run. `changes` compares against the last run of the same Watchlist name and adds change fields to every row. |
| stateName | string | No | Watchlist name that separates independent change-tracking scopes. Only used when `monitorMode` is `changes`. |
| onlyChanged | boolean | No | When `monitorMode` is `changes`, emit only rows that changed since the last run. Default: `false`. |
| proxyConfiguration | object | No | Keep the datacenter default. Blocked attempts are retried automatically on a fresh IP, and residential proxies cost far more per run for the same data. Switch only if a very large run still fails after retries. |

#### Basic keyword search

```json
{
  "scrapeMode": "searchKeyword",
  "searchQuery": "milk",
  "maxItems": 25
}
```

#### Scrape a category or specials URL

```json
{
  "scrapeMode": "providedUrls",
  "startUrls": [
    { "url": "https://www.coles.com.au/on-special" }
  ],
  "maxItems": 50
}
```

#### Watchlist: track price and stock changes over time

```json
{
  "scrapeMode": "skuList",
  "skuList": ["8150288", "4845732"],
  "monitorMode": "changes",
  "stateName": "dairy-watchlist",
  "onlyChanged": true
}
```

Run this on a [schedule](https://docs.apify.com/platform/schedules) with the same `stateName` and only genuinely new, repriced, or restocked products come back.

### Output example

```json
{
  "product_id": "8150288",
  "name": "Full Cream Milk",
  "brand": "Coles",
  "description": "COLES FULL CREAM MILK 3L",
  "size": "3L",
  "availability": true,
  "availability_type": "InStoreAndOnline",
  "available_quantity": 5128,
  "price": 4.95,
  "original_price": null,
  "on_special": false,
  "discount_amount": null,
  "discount_percent": null,
  "currency": "AUD",
  "comparable": "$1.65/ 1L",
  "unit_price": 1.65,
  "unit_price_quantity": 1,
  "unit_price_unit": "l",
  "unit_price_basis": "L",
  "price_per_base_unit": 1.65,
  "image_url": "https://productimages.coles.com.au/productimages/8/8150288.jpg",
  "category": "DY MILK",
  "url": "https://www.coles.com.au/product/8150288",
  "scraped_at": "2026-09-16T05:59:50.306Z",
  "retail_limit": 20,
  "promotional_limit": 12,
  "online_special": false,
  "sub_category": "WHOLE WHITE",
  "class_name": "WHOLE 3-3.9L",
  "trade_profit_centre": "DAIRY",
  "category_group": "DAIRY"
}
```

### What data can you extract from Coles?

| Field | Description |
|-------|-------------|
| product\_id / name / brand / description / size | Core product identity fields. |
| availability / availability\_type / available\_quantity | Stock status, channel (e.g. in-store and online), and exact quantity on hand. Populated for keyword and URL searches; SKU list mode does not expose these (see FAQ). |
| price / original\_price / on\_special / discount\_amount / discount\_percent | Current price, genuine pre-discount price, and a computed discount — only set when `original_price` genuinely exceeds `price`. |
| comparable / unit\_price / unit\_price\_quantity / unit\_price\_unit | Coles' own unit-price text and its parsed numeric parts. |
| unit\_price\_basis / price\_per\_base\_unit | Normalized comparison basis (`kg`, `L`, or `ea`) and the true price per full base unit — comparable across products, unlike `comparable`, which mixes per-100g and per-1kg displays. |
| image\_url / url | Full-resolution product image and the Coles product page URL. |
| category / sub\_category / class\_name / trade\_profit\_centre / category\_group | Coles' full internal merchandising hierarchy. |
| retail\_limit / promotional\_limit / online\_special | Purchase limits and whether the item is an online-only special. |
| change\_type / old\_price / price\_delta / price\_delta\_pct | Only present in `changes` monitor mode — see Watchlists above. |

### How to scrape Coles

1. Open **Coles Australia Scraper** on Apify.
2. Choose a **Scrape mode**: search by **keyword**, paste category/search/specials **URLs**, or list exact **SKUs**.
3. Set **Max items** and **Max requests per crawl** — start small (e.g. 25 and 25) for a fast, cheap test run.
4. Leave **Proxy configuration** on the datacenter default; only switch to residential if a large run still fails after automatic retries.
5. For recurring price or stock tracking, set **Monitor mode** to `changes` and give it a **Watchlist name**.
6. Click **Start**, then download results as JSON, CSV, or Excel from the **Storage** tab, or pull them from the [API](https://apify.com/parsebird/coles-australia-scraper/api).

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

This Actor uses the **pay-per-event** model — you are charged per product saved to the dataset, with lower rates on paid Apify plans.

| Plan | Price per product | Price per 1,000 products |
|------|--------------------|----------------------------|
| Free | $0.00119 | **$1.19** |
| Bronze | $0.00099 | **$0.99** |
| Silver | $0.00089 | **$0.89** |
| Gold | $0.00089 | **$0.89** |

Scraping 1,000 products on the Free plan costs about **$1.19**. Your monthly Apify platform credits typically cover several thousand products on top of that.

### How it works

1. The Actor launches a stealth-configured [Camoufox](https://camoufox.com) browser session and warms it up against coles.com.au's homepage to pass Incapsula's bot challenge.
2. Depending on `scrapeMode`, it navigates to a search/category/specials URL (paginating via Coles' own `start` offset) or an individual `/product/{sku}` page for each SKU.
3. It parses the `__NEXT_DATA__` JSON Coles' own Next.js app hydrates from, flattens each product into the fields above, and pushes it to the dataset immediately.
4. In `changes` monitor mode, each product is compared against the named Watchlist's last recorded price and availability, and the result is merged back into that Watchlist for the next run.
5. If a request gets blocked mid-run, the Actor retries it on a fresh browser session and proxy IP automatically.

### Using the Actor via API

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run = client.actor("parsebird/coles-australia-scraper").call(run_input={
    "scrapeMode": "searchKeyword",
    "searchQuery": "yoghurt",
    "maxItems": 50,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["name"], "—", item.get("price"), item.get("comparable"))
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });

const run = await client.actor('parsebird/coles-australia-scraper').call({
    scrapeMode: 'providedUrls',
    startUrls: [{ url: 'https://www.coles.com.au/on-special' }],
    maxItems: 50,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

The Actor runs on the [Apify platform](https://apify.com), so you also get [scheduling](https://docs.apify.com/platform/schedules), a full [REST API](https://docs.apify.com/api/v2), webhooks, and native integrations with Make, Zapier, Google Sheets, and Slack. Results export as JSON, CSV, Excel, XML, or HTML.

### Use cases

- **Price monitoring** — track price and discount changes for specific products or a whole category over time.
- **Weekly specials tracking** — scrape `/on-special` on a schedule to catch new discounts as they go live.
- **Stock and availability tracking** — watch specific SKUs for `BACK_IN_STOCK` or `OUT_OF_STOCK` transitions.
- **Basket price comparison** — use `price_per_base_unit` to compare like-for-like unit prices across brands and pack sizes.
- **Market and category research** — analyze category mix, discount depth, and stock patterns across a search term.

### FAQ

**Do I need a proxy to scrape Coles?**
Yes — Coles sits behind Incapsula, which blocks plain requests outright. Keep the default `{"useApifyProxy": true}`, which uses Apify's datacenter proxy; the Actor retries blocked attempts automatically on a fresh session. Only switch to a residential proxy group if a very large run still fails after retries — it costs far more for the same data.

**Why don't SKU list results have `availability_type` or `available_quantity`?**
Coles only publishes those two fields on search, category, and specials result tiles — not on the individual product page that SKU list mode fetches directly. `availability` (a plain in-stock/out-of-stock boolean) is still populated in every mode.

**How is `discount_percent` calculated?**
From `original_price` and `price` directly. `on_special` is only `true` when Coles published a genuine pre-discount "was" price that's actually higher than the current price.

**What is a Watchlist?**
Set `monitorMode` to `changes` and give it a `stateName` (the Watchlist name). Each run compares the scraped products against that Watchlist's last run and tags every row with a `change_type`. Different `stateName` values track completely independent histories, so you can run several Watchlists — a category, a competitor set, your own SKU list — side by side.

**Does `maxRequestsPerCrawl: 0` really mean unlimited?**
Yes. It removes the page-load cap entirely; `maxItems` is still the ultimate stopping point.

**Can I schedule recurring runs?**
Yes. Use Apify's [scheduler](https://docs.apify.com/platform/schedules) to run this Actor daily or weekly — combine it with `monitorMode: changes` to get a clean feed of only what moved. Found a problem? Open an issue on the **Issues** tab.

### Related Actors

- [Woolworths Australia Grocery Products Scraper](https://apify.com/parsebird/woolworths-au-scraper) — the same kind of price, stock, and category data from Coles' biggest competitor.
- [Daraz.pk Ecommerce Scraper](https://apify.com/parsebird/daraz-pk-ecommerce-scraper) — product and pricing data from Daraz, Pakistan's Lazada-platform marketplace.
- Browse the full [ParseBird publisher page](https://apify.com/parsebird) for more scrapers.

### Is it legal to scrape Coles Australia?

Is it legal? This Actor collects only publicly available product listings from Coles — data any visitor can view without logging in. It does not access private account data or bypass authentication. You are responsible for using the scraped data lawfully, including respecting Coles' terms and applicable data-protection laws such as the Australian Privacy Act or GDPR where relevant. If you plan to scrape at scale or redistribute the data commercially, seek legal advice first. See Apify's guide on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

# Actor input Schema

## `scrapeMode` (type: `string`):

How to find products: search by keyword, from category/search/specials URLs, or by an exact SKU list.

## `searchQuery` (type: `string`):

Search term, product name, or barcode. Used in 'Search by keyword' mode.

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

Coles search, category, or weekly specials URLs. Used in 'From URLs' mode.

## `skuList` (type: `array`):

Specific Coles product SKU IDs to scrape. Used in 'By SKU list' mode.

## `includeDescription` (type: `boolean`):

Include the full product description text.

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

Maximum number of products to scrape per run.

## `maxRequestsPerCrawl` (type: `integer`):

Maximum pages to load per run. Set to 0 for no limit.

## `monitorMode` (type: `string`):

'Snapshot' returns current data every run. 'Changes' compares against the last run of the same Watchlist name and adds change fields to every row.

## `stateName` (type: `string`):

Watchlist name that separates independent change-tracking scopes. Only used when Monitor mode is 'Changes'.

## `onlyChanged` (type: `boolean`):

When Monitor mode is 'Changes', emit only rows that changed since the last run.

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

Keep the datacenter default. Blocked attempts are retried automatically on a fresh IP, and residential proxies cost far more per run for the same data. Switch only if a very large run still fails after retries.

## Actor input object example

```json
{
  "scrapeMode": "searchKeyword",
  "searchQuery": "milk",
  "startUrls": [
    {
      "url": "https://www.coles.com.au/browse/fruit-vegetables/vegetables"
    }
  ],
  "skuList": [
    "5900530",
    "4845732"
  ],
  "includeDescription": true,
  "maxItems": 25,
  "maxRequestsPerCrawl": 25,
  "monitorMode": "snapshot",
  "stateName": "default",
  "onlyChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (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 = {
    "scrapeMode": "searchKeyword",
    "searchQuery": "milk",
    "startUrls": [
        {
            "url": "https://www.coles.com.au/browse/fruit-vegetables/vegetables"
        }
    ],
    "skuList": [
        "5900530",
        "4845732"
    ],
    "maxItems": 25,
    "maxRequestsPerCrawl": 25,
    "monitorMode": "snapshot",
    "stateName": "default",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/coles-australia-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 = {
    "scrapeMode": "searchKeyword",
    "searchQuery": "milk",
    "startUrls": [{ "url": "https://www.coles.com.au/browse/fruit-vegetables/vegetables" }],
    "skuList": [
        "5900530",
        "4845732",
    ],
    "maxItems": 25,
    "maxRequestsPerCrawl": 25,
    "monitorMode": "snapshot",
    "stateName": "default",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("parsebird/coles-australia-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 '{
  "scrapeMode": "searchKeyword",
  "searchQuery": "milk",
  "startUrls": [
    {
      "url": "https://www.coles.com.au/browse/fruit-vegetables/vegetables"
    }
  ],
  "skuList": [
    "5900530",
    "4845732"
  ],
  "maxItems": 25,
  "maxRequestsPerCrawl": 25,
  "monitorMode": "snapshot",
  "stateName": "default",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call parsebird/coles-australia-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/coles-australia-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/YKKct5srn0f1yAAfq/builds/CoHzl2R8qIlcTDDF4/openapi.json
