# Woolworths Australia Grocery Product & Reviews Scraper (`abotapi/woolworths-au-scraper`) Actor

Scrape Woolworths Australia (woolworths.com.au) grocery products and reviews. Search by keyword or paste product / search links. Returns name, brand, price, was price, unit price, pack size, specials, availability, images, category, nutrition, allergens, ingredients, rating and customer reviews.

- **URL**: https://apify.com/abotapi/woolworths-au-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Developer tools, Automation
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 product results

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/platform/actors/running/actors-in-store#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

## Woolworths Australia Grocery Product & Reviews Scraper

Scrape woolworths.com.au grocery products and customer reviews at scale. Search by keyword with filters, browse a whole specials & offers category, or paste product / search / specials links (or bare product codes). Every product comes back as one flat record with price, was-price and discount, unit (cup) price, specials, pack size, availability, images, category, nutrition, allergens, ingredients, rating and - optionally - customer reviews.

### What it does

- **Three ways to start**
  - **Search** - one or more keywords, with sort, minimum-rating, price-range and specials-only filters, across all result pages.
  - **Specials & offers category** - browse a whole Woolworths specials category (Half Price, Online Only Specials, Everyday Low Price, Seasonal Price, Buy More Save More, Lower Shelf Price, Bundles, or all specials at once).
  - **URL** - paste product pages (`/shop/productdetails/<code>`), search pages (`/shop/search/products?searchTerm=...`), category pages (`/shop/browse/fruit-veg`, including sub-categories like `/shop/browse/fruit-veg/fruit`), specials listing pages (`/shop/browse/specials/half-price`), or bare numeric product codes. Category pages walk the category's full catalog.
- **Was-price & discount** - on every discounted product you get the strike-through was-price, the savings amount and percentage, and the offer label (e.g. "Half Price", "Buy More Save More"), alongside the current price and unit (cup) price.
- **Full product detail** - collect each product's rich description, barcode (EAN/GTIN), department / category, country of origin, health-star rating, ingredients, allergens, dietary statement, storage instructions, the nutrition panel, images and sibling variants.
- **Ratings & reviews** - the aggregate rating (average score, rating count, review count and the 5-star-to-1-star distribution) plus customer reviews: star rating, review text, author display name and date.

### Specials & offers

Pick a category under **Specials & offers category** (search mode) to walk a whole promotion, or paste a specials listing link in URL mode. The categories mirror Woolworths' own specials taxonomy:

| Category value | Covers |
|----------------|--------|
| `all-specials` | Every specials category, merged. |
| `half-price` | Half Price. |
| `lower-shelf-price` | Lower Shelf Price. |
| `online-only-specials` | Online Only Specials. |
| `everyday-low-price` | Everyday Low Price. |
| `seasonal-price` | Seasonal Price. |
| `buy-more-save-more` | Buy More Save More (multibuy). |
| `bundles` | Bundles. |

Each product on a specials listing carries its was-price, savings and offer label; multibuy offers also carry the buy-more quantity and price.

#### Skipping departments you don't want

To scrape a listing *except* certain departments, use `excludeCategories`:

```json
{
  "specialsCategory": "half-price",
  "excludeCategories": ["beer-wine-spirits", "everyday-market"]
}
```

A product is skipped if it belongs to **any** listed department. This matters because the storefront files the same product under several departments at once — roughly half of the products in the specials catalogue are cross-listed — so a product carried under both an excluded and a kept department is still dropped. Accepts the name from a browse link (`beer-wine-spirits`), the display name (`Beer, Wine & Spirits`), or a pasted `/shop/browse/<department>` URL. The decision is made from the listing page, so skipped products are never fetched in detail and cost nothing.

Every record also carries `departments` and `departmentIds` listing **every** department the product sits under.

Use `excludeSpecialsCategories` to do the opposite: drop products in the categories you list instead of collecting and paying to enrich them. It uses the same category values as above; excluding `all-specials` drops every on-special product. Applied to the Specials & offers category, a pasted specials URL, and keyword searches; a dedicated specials listing is skipped before any page is fetched, so an excluded category costs nothing. Dropped products are never pushed or charged.

### Ratings & reviews: what you get

Woolworths shows an **aggregate rating** (average score, rating count, review count and the 5-star-to-1-star distribution) and a **customer review list** on product pages. This actor captures both:

- The **aggregate rating** and star distribution are collected during detail enrichment (`fetchDetails`), so every enriched product carries `rating`, `ratingCount`, `reviewCount` and `ratingDistribution` when it has ratings.
- The **review list** is collected when `fetchReviews` is on, up to your `maxReviewsPerProduct` cap. Each review carries star rating, text, author display name and date.

> **Note:** many grocery lines carry few or no reviews. When a product has no ratings, the rating fields are omitted (not guessed) and `reviews` comes back empty. Products with reviews return them in full.

### Input

| Field | Type | Description |
|-------|------|-------------|
| `mode` | select | `search` (keyword + filters) or `url` (paste links / product codes). |
| `queries` | string\[] | Search keywords (search mode). Each keyword is searched separately. |
| `specialsCategory` | select | Browse a whole specials category: `half-price`, `lower-shelf-price`, `online-only-specials`, `everyday-low-price`, `seasonal-price`, `buy-more-save-more`, `bundles`, or `all-specials`. Leave blank to skip. |
| `excludeSpecialsCategories` | select (multi) | Drop products in these specials categories instead of collecting them. Same values as `specialsCategory` (minus blank); excluding `all-specials` drops every on-special product. |
| `excludeCategories` | string\[] | Optional. Departments to skip entirely, e.g. `beer-wine-spirits`, `everyday-market`, `pet`. Skipped products are never collected, so they cost nothing. |
| `sortBy` | select | `relevance`, `price_asc`, `price_desc`, `name`, `savings`. |
| `minRating` | select | Keep products rated at least `3`, `4` or `5` stars (requires details). |
| `specialsOnly` | boolean | Keep only products currently on special / half price. |
| `includeMarketplace` | boolean | Also keep products shipped by third-party Everyday Market sellers (often tens of thousands of extra items per department). By default only products sold directly by Woolworths are returned. Default `false`. |
| `minPrice` / `maxPrice` | integer | Keep products within an AUD price range. |
| `urls` | string\[] | Product / search URLs, or bare product codes (url mode). |
| `fetchDetails` | boolean | Collect full product detail + aggregate rating. Default `true`. |
| `fetchReviews` | boolean | Also collect customer reviews. Default `false`. |
| `maxReviewsPerProduct` | integer | Cap reviews per product (`0` = all). Default `20`. |
| `maxItems` | integer | Primary limit: max products for the whole run (`0` = unlimited). Default `20`. |
| `maxPages` | integer | Optional bound on result pages walked per keyword/category/URL (36 products/page). Empty walks every result page (does not cap products; `maxItems` does). |
| `resumeFromRunId` | string | Optional. ID of a previous run of this actor (or a dataset ID). Products already in that dataset are skipped, so this run returns only NEW products (a delta). Combine both runs' datasets for the full set. Max products then counts only the new products. |
| `proxy` | object | Proxy configuration. Apify Proxy is used by default and works on every plan. |

#### Example input

```json
{
  "mode": "search",
  "queries": ["milk"],
  "sortBy": "relevance",
  "specialsOnly": false,
  "minPrice": 1,
  "maxPrice": 20,
  "fetchDetails": true,
  "fetchReviews": true,
  "maxReviewsPerProduct": 20,
  "maxItems": 20,
  "proxy": { "useApifyProxy": true }
}
```

#### Example input - specials category

```json
{
  "mode": "search",
  "specialsCategory": "half-price",
  "sortBy": "savings",
  "fetchDetails": true,
  "maxItems": 50,
  "proxy": { "useApifyProxy": true }
}
```

### Output

One record per product. Example (values below are illustrative):

```json
{
  "stockcode": "100000",
  "name": "Example Full Cream Milk 2L",
  "displayName": "Example Full Cream Milk 2L",
  "title": "Example Full Cream Milk 2L",
  "brand": "ExampleBrand",
  "url": "https://www.woolworths.com.au/shop/productdetails/100000/example-full-cream-milk",
  "price": 3.1,
  "wasPrice": 3.9,
  "savingsAmount": 0.8,
  "savingsPercent": 21,
  "isOnSpecial": true,
  "isHalfPrice": false,
  "isEdrSpecial": false,
  "promoLabel": "Prices Dropped",
  "savingsLabel": "Save $0.80",
  "promotionType": "Special",
  "cupPrice": 1.55,
  "cupMeasure": "1L",
  "cupString": "$1.55 / 1L",
  "packageSize": "2L",
  "unit": "Each",
  "currency": "AUD",
  "barcode": "9300000000000",
  "gtinFormat": 13,
  "isAvailable": true,
  "isInStock": true,
  "isNew": false,
  "isOnlineOnly": false,
  "variety": "Full Cream",
  "offerId": "24000000",
  "isMarketProduct": false,
  "image": "https://cdn0.woolworths.media/content/wowproductimages/large/100000.jpg",
  "thumbnailImage": "https://cdn0.woolworths.media/content/wowproductimages/small/100000.jpg",
  "images": ["https://cdn0.woolworths.media/content/wowproductimages/large/100000.jpg"],
  "richDescription": "A fresh Australian full cream milk, rich and creamy.",
  "department": "Dairy, Eggs & Fridge",
  "category": "Milk",
  "subCategory": "Full Cream Milk",
  "countryOfOrigin": "Made in Australia",
  "healthStarRating": "4.5",
  "ingredients": "Full cream milk.",
  "allergenContains": "Milk",
  "dietaryStatement": "Vegetarian",
  "storageInstructions": "Keep refrigerated below 4C.",
  "nutrition": { "servingsPerPack": "8", "servingSize": "250mL" },
  "rating": 4.6,
  "ratingCount": 128,
  "reviewCount": 54,
  "ratingDistribution": { "5": 96, "4": 20, "3": 7, "2": 3, "1": 2 },
  "reviews": [
    {
      "rating": 5.0,
      "body": "Fresh and creamy, our family buys it every week.",
      "author": "sampleShopper",
      "date": "2025-02-14T06:01:00.000Z",
      "syndicatedFrom": "woolworths"
    }
  ],
  "reviewsCollected": 1,
  "searchMode": "search"
}
```

#### Field reference

| Field | Description |
|-------|-------------|
| `stockcode` | Woolworths product code (the SKU). |
| `name`, `displayName`, `title`, `brand`, `url` | Product name, display name, fuller canonical title (includes pack size), brand, product page URL. |
| `price`, `wasPrice`, `savingsAmount`, `savingsPercent`, `isOnSpecial`, `isHalfPrice`, `isEdrSpecial` | Current price, strike-through was-price, savings amount + percentage, and specials flags (including Everyday Rewards member specials). |
| `promoLabel`, `savingsLabel`, `promotionType`, `promotionInfo` | Offer label (e.g. "Half Price"), savings tag (e.g. "Save $7.00"), promotion type and the human-readable promotion sentence. Present on discounted products. |
| `multibuy`, `memberPrice` | Buy-more-save-more offer (`quantity`, `price`, `cupString`) and Everyday Rewards member price when applicable. |
| `cupPrice`, `cupMeasure`, `cupString`, `packageSize`, `unit`, `currency` | Unit (cup) price, pack size and currency (AUD). |
| `barcode`, `gtinFormat` | EAN/GTIN barcode and its GTIN format code. |
| `isAvailable`, `isInStock`, `isNew`, `isOnlineOnly`, `productLimit`, `isMarketProduct`, `vendor`, `variety`, `offerId` | Availability, new/online-only flags, purchase limit, market-seller info, variety and the offer id. |
| `image`, `thumbnailImage`, `images` | Primary image, thumbnail and the image list. |
| `richDescription`, `department`, `category`, `subCategory`, `countryOfOrigin` | Description, category path and origin (with details). |
| `healthStarRating`, `ingredients`, `allergenContains`, `allergenMayContain`, `dietaryStatement`, `storageInstructions`, `nutrition` | Grocery labelling and nutrition (with details). |
| `variants` | Sibling products (flavours / sizes) with code, name, price and URL. |
| `rating`, `ratingCount`, `reviewCount`, `ratingDistribution` | Aggregate rating and the 5★→1★ split (with details, when the product has ratings). |
| `reviews`, `reviewsCollected` | Collected reviews and their count (present when reviews are enabled). |

Whenever a value is not published for a product, the field is omitted or `null` rather than guessed.

Marketplace records (`includeMarketplace: true`) carry `isMarketProduct: true` and name their third-party seller in `vendor`.

### Proxy

Apify Proxy is used by default and works on every plan. Leave proxy enabled for the most reliable results.

### Notes

- Prices, specials, ratings and stock are captured as shown on the storefront at scrape time.
- Filters (`minRating`, `minPrice`, `maxPrice`, `specialsOnly`, `includeMarketplace`) are applied to each product's own values, so exact numeric ranges work independently of the on-site filters.
- Department listings mix the supermarket's own range with a much larger third-party marketplace catalog (some departments list 50,000+ marketplace items), so an uncapped category walk can legitimately run for hundreds of pages. The page log shows how many unique products have been seen against the listing's own total. Marketplace items are excluded by default; set `includeMarketplace: true` to collect them too.
- The run periodically checkpoints its progress, so if the platform restarts the run (a server migration, or you use Resurrect) it picks up where it left off instead of starting over, with no duplicate products and no double charges. No input is needed for this; it is automatic.

# Actor input Schema

## `mode` (type: `string`):

Choose 'search' to use keywords and filters, or 'url' to scrape specific Woolworths Australia product or search URLs (or bare product codes) you paste below.

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

One or more keywords to search, for example 'milk' or 'tim tam'. Each keyword is searched separately.

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

Result ordering, as offered by Woolworths.

## `minRating` (type: `string`):

Optional. Only keep products whose average customer rating is at least this many stars. Requires 'Fetch product details' (ratings are collected during enrichment).

## `specialsCategory` (type: `string`):

Optional. Browse a whole Woolworths specials / offers category instead of (or as well as) searching keywords. Uses Woolworths' own specials taxonomy. Leave blank to skip. In URL mode you can instead paste a specials listing link such as https://www.woolworths.com.au/shop/browse/specials/half-price.

## `excludeSpecialsCategories` (type: `array`):

Drop products belonging to these specials categories instead of collecting and paying to enrich them. Applies to the specials category above, a pasted specials listing URL, and (best-effort, for the categories the storefront flags on the product card itself) keyword searches. Excluding 'All Specials & Offers' drops every product currently on special. Leave empty to exclude nothing (existing behaviour).

## `excludeCategories` (type: `array`):

Optional. Departments to leave out, so they are never collected and never charged. Use the name from a browse link, for example 'beer-wine-spirits', 'everyday-market' or 'pet'. A product is skipped when it belongs to any listed department, even if it also appears under one you keep - the storefront files the same product under several at once. To skip promo types (half price, online only) instead, use Exclude specials categories above.

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

Optional. Keep only products currently on special / half price. Applies to any keyword, specials category or pasted URL.

## `includeMarketplace` (type: `boolean`):

Also keep products shipped by third-party Everyday Market sellers. Category listings carry tens of thousands of these extra items per department, so they are excluded by default — only products sold directly by Woolworths are returned. Tick to include them.

## `minPrice` (type: `integer`):

Optional. Only keep products priced at or above this amount.

## `maxPrice` (type: `integer`):

Optional. Only keep products priced at or below this amount.

## `urls` (type: `array`):

Product or search URLs (or bare numeric product codes) to scrape, for example https://www.woolworths.com.au/shop/search/products?searchTerm=milk or a product page under /shop/productdetails/. Multiple entries supported.

## `fetchDetails` (type: `boolean`):

Collect each product's full detail (rich description, barcode, department / category, country of origin, health-star rating, ingredients, allergens, dietary statement, storage, nutrition panel, variants) plus its aggregate rating.

## `fetchReviews` (type: `boolean`):

Also collect customer reviews for each product (star rating, review text, author display name, date and source), plus the aggregate rating and star distribution.

## `maxReviewsPerProduct` (type: `integer`):

Cap on reviews collected per product when 'Fetch customer reviews' is on. Use 0 for all available reviews.

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

Maximum number of products to return across the whole run. Use 0 for unlimited.

## `maxPages` (type: `integer`):

Optional bound on how many result pages are walked per keyword or category or URL (36 products per page). Leave empty to walk every result page until the store runs out. This does not cap the number of products; Max products does that.

## `resumeFromRunId` (type: `string`):

Optional. ID of a previous run of this actor (or a dataset ID). Products already in that dataset are skipped, so this run returns only NEW products (a delta). Combine both runs' datasets for the full set. Max products then counts only the new products.

## `proxy` (type: `object`):

Leave enabled to use Apify Proxy (recommended). You can optionally pin specific proxy groups or a country.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "milk"
  ],
  "sortBy": "relevance",
  "minRating": "0",
  "specialsCategory": "",
  "excludeCategories": [
    "beer-wine-spirits",
    "everyday-market"
  ],
  "specialsOnly": false,
  "includeMarketplace": false,
  "urls": [
    "https://www.woolworths.com.au/shop/productdetails/32731"
  ],
  "fetchDetails": true,
  "fetchReviews": false,
  "maxReviewsPerProduct": 20,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "queries": [
        "milk"
    ],
    "urls": [
        "https://www.woolworths.com.au/shop/productdetails/32731"
    ],
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/woolworths-au-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 = {
    "mode": "search",
    "queries": ["milk"],
    "urls": ["https://www.woolworths.com.au/shop/productdetails/32731"],
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/woolworths-au-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "queries": [
    "milk"
  ],
  "urls": [
    "https://www.woolworths.com.au/shop/productdetails/32731"
  ],
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/woolworths-au-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/woolworths-au-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/FV7ZkTkprylaGz27a/builds/yt4SkoridlemtVg8R/openapi.json
