# Woolworths Scraper — Prices, Specials, Barcodes & Nutrition (`logiover/woolworths-scraper`) Actor

Woolworths Australia scraper: export woolworths.com.au products with price, was price, specials, half-price & multibuy deals, unit price, GTIN barcode, ingredients, allergens, nutrition, health star rating and ratings. Search, categories, specials or the whole ~25,000-product range. No login.

- **URL**: https://apify.com/logiover/woolworths-scraper.md
- **Developed by:** [Logiover](https://apify.com/logiover) (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.30 / 1,000 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/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

## Woolworths Scraper

**Every product on woolworths.com.au as clean data — shelf price, was price, specials, half-price and multibuy deals, unit (cup) price, GTIN barcode, ingredients, allergens, nutrition, Health Star Rating, country of origin and customer ratings — exported to JSON, CSV or Excel.**

> Independent tool. Not affiliated with, endorsed by, or sponsored by Woolworths Group Limited or Woolworths Supermarkets. "Woolworths" is a trademark of its owner and is used here only to describe the website this Actor reads.

**Woolworths Scraper** is a fast, dependable **Woolworths API alternative** for anyone who needs **Australian grocery prices** as data instead of web pages. It lets you **scrape woolworths.com.au** — the online store of Woolworths Supermarkets Australia — by keyword, by category URL, by stockcode or barcode, or take the **whole Woolworths range of about 25,000 products in a single run**, with **no login, no cookies, no browser and no API key**. Use it as a **Woolworths price tracker**, a daily feed of **Woolworths specials and half-price data**, a **Woolworths barcode (GTIN) database**, a source of **product ingredients & allergens data**, or the backbone of an **Australian grocery prices dataset**, and **export to CSV, Excel or JSON** (or XML, HTML and RSS) straight from the Apify dataset, the API or any of the integrations below.

Each row is one product with 78 fields: the price the shelf shows and the price it was, savings in dollars and percent, the special type (half price, lower shelf price, everyday low price, seasonal price, multibuy), the multibuy deal ("2 for $8.00"), the **cup price** ("$2.20 / 1L"), the 13-digit **barcode (EAN/GTIN)**, brand, pack size, images, availability and purchase limit, the full **department > category > subcategory** path plus Woolworths' internal merchandise hierarchy, ingredients, allergens ("contains" and "may contain"), dietary and lifestyle claims, the **Health Star Rating**, a parsed **nutrition panel** per 100 g and per serve, **star ratings with review counts**, and — one switch away — the Australian **country-of-origin label**. Optional **store-level prices** by postcode cover the regions where Woolworths prices differ. It is built for **supermarket price monitoring in Australia** — FMCG brands, category managers, price-comparison apps, nutrition apps, analysts, economists and journalists who track grocery prices week after week — and it uses the same field names as our Coles dataset, so both supermarkets stack into one table.

***

### Table of contents

- [At a glance](#at-a-glance)
- [What you get](#what-you-get)
- [Who uses it — 15 workflows](#who-uses-it--15-workflows)
- [Cookbook — 16 ready-to-run inputs](#cookbook--16-ready-to-run-inputs)
- [Tutorial: your first run in the Apify Console](#tutorial-your-first-run-in-the-apify-console)
- [Tutorial: run it from the API](#tutorial-run-it-from-the-api)
- [Input reference](#input-reference)
- [Output data dictionary](#output-data-dictionary)
- [Output example](#output-example)
- [Scale and speed — measured](#scale-and-speed--measured)
- [How Woolworths prices, specials, unit prices and Health Star Ratings work](#how-woolworths-prices-specials-unit-prices-and-health-star-ratings-work)
- [What you pay](#what-you-pay)
- [Integrations](#integrations)
- [Performance tips](#performance-tips)
- [Troubleshooting](#troubleshooting)
- [Honest limits](#honest-limits)
- [Compared with checking prices by hand](#compared-with-checking-prices-by-hand)
- [FAQ](#faq)
- [Glossary](#glossary)
- [Is it legal to scrape Woolworths?](#is-it-legal-to-scrape-woolworths)
- [Changelog](#changelog)
- [Support](#support)

***

### At a glance

| | |
|---|---|
| **Source** | woolworths.com.au — Woolworths Supermarkets Australia, read through the same JSON the website itself loads |
| **Coverage** | Woolworths' own range: **24,990 unique products** in one run (measured 2026-10-18). Optional: the Everyday Market marketplace (third-party sellers, about 1.19 million listings on 2026-10-18) |
| **What to scrape** | Search terms · category URLs or names · the whole catalogue · all specials & offers · half-price specials · stockcodes, barcodes or product URLs |
| **Speed** | 1,000 products in **18 s** with the default settings, **74 s** with the country-of-origin option · the whole range in **124 s** |
| **Fields** | 78 per product: price, was price, savings, specials, multibuy, cup price, barcode, brand, size, images, availability, purchase limit, categories, ingredients, allergens, dietary claims, Health Star Rating, nutrition per 100 g and per serve, country of origin, ratings, store |
| **Store prices** | Optional, by postcode or store number (prices differ in some regions — measured below) |
| **Output** | Apify dataset → JSON, CSV, Excel, XML, HTML, RSS · REST API · webhooks · Make, Zapier, n8n, Google Sheets, Airtable, BI tools, warehouses, MCP |
| **Needs** | Nothing: no Woolworths account, no login, no cookies, no proxy setup |
| **Price** | Pay per product — from **$1.30 per 1,000 products**, compute and proxies included ([What you pay](#what-you-pay)) |

All numbers on this page were measured on the Apify platform on **2026-10-18** unless the line says otherwise.

***

### What you get

One row per product. Fields that do not apply are `null` — never an empty string or a guessed zero.

| Group | Fields | Example values |
|---|---|---|
| **Identity** | `productId`, `stockcode`, `barcode`, `retailer`, `name`, `brand`, `title`, `size`, `variety`, `url`, `imageUrl`, `imageUrls` | `33557` · `9300639500041` · `Devondale` · `Devondale Pure Full Cream Long Life Milk 1L` · `1L` |
| **Price** | `price`, `wasPrice`, `savings`, `savingsPercent`, `instorePrice` | `2.5` · `5` · `2.5` · `50` |
| **Unit (cup) price** | `unitPrice`, `unitOfMeasure`, `unitPriceText`, `isSoldByWeight` | `0.25` · `100G` · `$0.25 / 100G` · `false` |
| **Specials & offers** | `isOnSpecial`, `isHalfPrice`, `isMultibuy`, `isOnlineOnly`, `specialType`, `promoLabel`, `multibuyQuantity`, `multibuyPrice`, `multibuyLabel`, `multibuyUnitPrice`, `priceNote`, `offerId` | `HALF_PRICE` · `Half price · Save $2.50` · `2 for $8.00` · `Was $6.60 - 25/08/2026` |
| **Availability** | `isAvailable`, `isInStock`, `purchaseLimit`, `isNew`, `isAgeRestricted`, `deliveryRestriction` | `true` · `36` · `Remote Orders Restricted Goods` |
| **Ratings** | `rating`, `ratingCount`, `reviewCount` | `4.45` · `11` · `11` |
| **Categories** | `department`, `category`, `subCategory`, `categoryPath`, `allCategoryPaths`, `departmentId`, `categoryId`, `subCategoryId` | `Dairy, Eggs & Fridge > Milk > Long Life Milk` |
| **Merchandise hierarchy** | `merchandiseDepartment`, `merchandiseCategory`, `merchandiseSubCategory`, `merchandiseSegment` | `FRESH CONVENIENCE` · `DAIRY - MILK` · `WHOLE 1.5L TO 2L` |
| **Food data** | `description`, `ingredients`, `allergens`, `allergensMayContain`, `allergenFreeFrom`, `lifestyle`, `lifestyleClaims`, `healthStarRating`, `servingSize`, `servingsPerPackage`, `nutritionPer100g`, `nutritionPerServing`, `nutritionText`, `storageInstructions` | `Milk \| Gluten \| Soy` · `Gluten Free \| Vegan` · `4.5` · `{"energyKj":262,"proteinG":3.2}` |
| **Country of origin** (option) | `countryOfOrigin`, `countryOfOriginStatement`, `australianIngredientsPercent` | `Australia` · `Made in Australia from at least 95% Australian ingredients` · `95` |
| **Marketplace** | `isMarketplaceProduct`, `sellerName` | `true` · `KG Superstore` |
| **Store & provenance** | `storeId`, `storeName`, `storeState`, `source`, `scrapedAt` | `5652` · `Darwin City` · `NT` · `search: milk` |

The Output tab has four ready-made views: **Overview**, **Specials & offers**, **Ingredients & nutrition** and **Catalogue data**.

***

### Who uses it — 15 workflows

#### 1. FMCG brands and key-account teams

Run your brand names (or the category URLs you sell into) every Wednesday morning, when Woolworths' weekly specials change, and compare `price`, `wasPrice`, `promoLabel` and `multibuyLabel` week over week: which competitor went half price, for how long, and at what cup price.

#### 2. Category managers and retail buyers

Map a whole category — every product in `Dairy, Eggs & Fridge > Milk` with `size`, `unitPrice` per litre, private label vs branded and `merchandiseSegment` — and build the price ladder and share of shelf by brand in one pivot table.

#### 3. Price-comparison and grocery apps

Pull the **whole catalogue** nightly or weekly, key it on `barcode` and join it with Coles data (same field names) to show the cheaper supermarket per product and per unit; `imageUrl`, `title` and `url` are ready for product cards.

#### 4. Nutrition, allergen and diet apps

Food rows carry `ingredients`, `allergens`, `allergensMayContain`, `allergenFreeFrom`, `lifestyle`, `healthStarRating` and a parsed nutrition panel (`nutritionPer100g.sugarsG`, `.sodiumMg` …): "gluten free and Health Star Rating ≥ 4" is one line of SQL.

#### 5. Dietitians, meal planners and recipe sites

Search the ingredients your recipes need ("rolled oats", "greek yoghurt") and attach a live price and cup price to every recipe line — a costed weekly meal plan, refreshed on every run.

#### 6. Retail and market analysts

Measure promotional intensity — share of each brand's range on special, average `savingsPercent`, how often half price returns — from a weekly **All specials & offers** run (about 7,000 products in under a minute).

#### 7. Economists and inflation trackers

Snapshot a fixed basket by **stockcode** (or the whole range) weekly: `unitPrice` normalises pack sizes, `wasPrice` separates promotions from shelf-price changes, `merchandiseCategory` maps to CPI-style classes.

#### 8. Journalists and consumer advocates

Check "Prices dropped" claims against Woolworths' own notes (`priceNote`: `Was $6.60 - 25/08/2026`), spot shrinkflation by tracking `size` and `unitPrice`, and compare regions with the **store prices** option.

#### 9. Marketplace sellers and Everyday Market partners

Turn on **Include Everyday Market** for marketplace listings with `sellerName`, price and category — competitor pricing for third-party sellers.

#### 10. AI, RAG and shopping-assistant builders

Load structured product knowledge into a vector store, or call the Actor as a tool through the Apify MCP server: "vegan snacks under $4 with Health Star Rating 4+" becomes one query.

#### 11. Public-health and academic researchers

Study the food environment: Health Star Ratings, sugar and sodium per 100 g by category, "may contain" warnings, and the share of Australian ingredients (`australianIngredientsPercent`).

#### 12. Market research and consumer insight agencies

Share of shelf by brand, price-point clusters, launches (`isNew`) and assortment changes between snapshots — one dataset per week.

#### 13. Supply-chain, procurement and wholesale teams

Watch `purchaseLimit` (lowered when stock is tight), `isAvailable` and `deliveryRestriction` to spot supply problems by category early.

#### 14. Data engineers and BI teams

Schedule the catalogue into BigQuery, Snowflake, Postgres or S3 via the API or a webhook — a daily `woolworths_products` table keyed on `productId` and `barcode`.

#### 15. Regional pricing analysts

Prices differ between some Woolworths stores. Set **postcode** (e.g. `0800` for Darwin, `6000` for Perth) and compare the store's prices with the default online prices — on 2026-10-18 109 of 191 milk products stocked at Darwin City were priced differently, and a Sydney Metro store charged more for 12 of 119 sampled products.

***

### Cookbook — 16 ready-to-run inputs

Paste any of these into the **JSON** tab of the input form, or send them as the body of an API call. Fields you leave out use their defaults.

#### Recipe 1 — The whole Woolworths catalogue, every week

```json
{
  "mode": "catalog",
  "maxItems": 0
}
```

Every department, de-duplicated by stockcode: 24,990 products in 124 s on 2026-10-18. Schedule it weekly (Wednesday, after the specials change) — see [Recipe 11](#recipe-11--price-change-tracking-with-a-schedule).

#### Recipe 2 — Everything, including the country-of-origin label

```json
{
  "mode": "catalog",
  "maxItems": 0,
  "includeDetails": true
}
```

Adds `countryOfOrigin`, `countryOfOriginStatement` and `australianIngredientsPercent`, and the shown price of products that are out of stock online, with one product-page request per product. Measured on 2026-10-18: 10,471 products from four departments in 704 s (about 15 products per second); at that rate the whole range takes roughly 28 minutes.

#### Recipe 3 — One category

```json
{
  "mode": "categories",
  "categoryUrls": ["https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk"],
  "maxItems": 0
}
```

Any department, category or subcategory URL from your browser works.

#### Recipe 4 — Several categories by name

```json
{
  "mode": "categories",
  "categoryUrls": ["Long Life Milk", "Coffee", "Breakfast Cereal"],
  "maxItems": 3000
}
```

Names are matched against Woolworths' live category tree; an inexact name or old URL uses the closest category and `SUMMARY` says which.

#### Recipe 5 — Search terms (top results by relevance)

```json
{
  "mode": "search",
  "searchTerms": ["coffee beans", "oat milk", "gluten free bread"],
  "maxItems": 300,
  "sortBy": "relevance"
}
```

Each term gets a fair share of `maxItems`; spare room goes to terms with more results.

#### Recipe 6 — Every product on special or in an offer

```json
{
  "mode": "specials",
  "maxItems": 0
}
```

Woolworths' own "Specials" filter in every department: 6,976 products in 54 s on 2026-10-18 — price cuts, half price, multibuys, lower shelf prices and online-only deals.

#### Recipe 7 — Half-price specials only

```json
{
  "mode": "halfPrice",
  "maxItems": 0
}
```

The Half Price page: 1,844 products in 14 s on 2026-10-18.

#### Recipe 8 — A brand's whole range

```json
{
  "mode": "search",
  "searchTerms": ["Devondale"],
  "maxItems": 0
}
```

Woolworths' search matches brand names; keep rows whose `brand` equals the brand to drop stray matches.

#### Recipe 9 — Barcode (GTIN) lookup

```json
{
  "mode": "products",
  "productIds": ["9300639500041", "9310053108327", "41554572308"]
}
```

EAN-13, UPC and GTIN-8/14 codes are matched exactly; barcodes Woolworths does not sell are listed in `SUMMARY`.

#### Recipe 10 — A fixed basket by stockcode or product URL

```json
{
  "mode": "products",
  "productIds": [
    "33557",
    "406949",
    "https://www.woolworths.com.au/shop/productdetails/614440/riverina-reduced-fat-fetta-cheese"
  ]
}
```

Stockcodes (the number in the product URL) are read 50 per request — a 500-product basket takes seconds.

#### Recipe 11 — Price-change tracking with a schedule

1. Save one of the inputs above as a **task** (Console → the Actor → *Save as a new task*).
2. Add a **schedule** to the task, e.g. `0 7 * * 3` (07:00 every Wednesday) in your time zone.
3. After each run, compare the new dataset with the previous one on `productId`: a changed `price` is a price change, a changed `wasPrice` with `isOnSpecial: false` is a shelf-price change, and `isOnSpecial` flipping is a promotion starting or ending. The Python snippet in [Integrations](#python) shows the comparison.

#### Recipe 12 — Prices at a particular store (Darwin, Perth, a Metro store …)

```json
{
  "mode": "categories",
  "categoryUrls": ["https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk"],
  "postcode": "0800",
  "maxItems": 0
}
```

The nearest Woolworths pick-up store to the postcode is used (here Darwin City, NT, store 5652); `price`, `wasPrice`, specials and the cup price are that store's, and products the store does not stock are left out. Use `storeId` instead of `postcode` if you know the store number.

#### Recipe 13 — A nutrition dataset

```json
{
  "mode": "catalog",
  "maxItems": 0,
  "includeRatings": false
}
```

Then keep rows where `nutritionPer100g` is not empty: 46.1% of the range carries a parsed nutrition panel (energy, protein, fat, saturated fat, carbohydrate, sugars, sodium and, where printed, fibre, trans fat, calcium, potassium …).

#### Recipe 14 — Gluten-free (or nut-free, vegan …) shopping list

```json
{
  "mode": "search",
  "searchTerms": ["gluten free"],
  "maxItems": 0
}
```

Filter on `allergenFreeFrom` contains `Gluten Free` or `lifestyle` contains `Gluten Free`, and check `allergensMayContain` for traces.

#### Recipe 15 — Merge with Coles data

Run this Actor and our Coles scraper with comparable inputs, then stack or join the two datasets: the core fields share their names — `productId`, `name`, `brand`, `title`, `size`, `url`, `imageUrl`, `barcode`, `price`, `wasPrice`, `savings`, `isOnSpecial`, `isHalfPrice`, `isMultibuy`, `multibuyLabel`, `unitPrice`, `unitOfMeasure`, `unitPriceText`, `isAvailable`, `categoryPath`, `ingredients`, `allergens`, `scrapedAt`. Join on `barcode` for the same product at both supermarkets; compare `unitPrice` (same `unitOfMeasure`) across pack sizes.

#### Recipe 16 — Everyday Market marketplace listings

```json
{
  "mode": "categories",
  "categoryUrls": ["https://www.woolworths.com.au/shop/browse/everyday-market/toys-games/toys"],
  "includeEverydayMarket": true,
  "maxItems": 0
}
```

Marketplace rows have `isMarketplaceProduct: true` and the `sellerName`. Woolworths answers at most 10,000 products per category; bigger ones are split automatically (see [Scale and speed](#scale-and-speed--measured)).

***

### Tutorial: your first run in the Apify Console

1. **Open the Actor** and click **Try for free** (or **Start** if you already use it). A free Apify account is enough.
2. **Choose what to scrape** in *What to scrape*: Search terms, Category URLs, Whole catalog, All specials & offers, Half-price specials only, or Products by stockcode / barcode / URL.
3. **Fill in the matching field** — e.g. type `milk` into *Search terms*, or paste a category URL from woolworths.com.au into *Category URLs*.
4. **Set Max results.** The form starts at 50 so a first run takes seconds; `0` means no limit.
5. Optional: tick **Specials only**, turn on **Include country of origin**, or enter a **postcode** for store prices.
6. Click **Start**. Rows appear in the **Output** tab while the run is going; the status line shows progress ("Saved 800/1,000 products · 28 pages · 59 s").
7. **Export**: Output → *Export* → CSV, Excel (XLSX), JSON, XML, HTML or RSS. Pick a view (Overview, Specials & offers, Ingredients & nutrition, Catalogue data) to export only those columns.
8. **Automate**: *Save as a new task*, then add a schedule and, if you like, a Google Sheets, Slack or webhook integration from the task's *Integrations* tab.

### Tutorial: run it from the API

1. Get your **API token** from Apify Console → Settings → API & Integrations.
2. **Small runs (up to a few thousand products)** — one synchronous call returns the rows:

```bash
curl -X POST "https://api.apify.com/v2/acts/logiover~woolworths-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN&format=json" \
  -H "Content-Type: application/json" \
  -d '{"mode":"search","searchTerms":["milk"],"maxItems":200}'
```

Use `&format=csv` or `&format=xlsx` for a file instead of JSON.
3\. **Big runs (the whole catalogue)** — start the run, then fetch the dataset when it has finished (or let a webhook tell you):

```bash
curl -X POST "https://api.apify.com/v2/acts/logiover~woolworths-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"catalog","maxItems":0}'
## -> data.id (run id) and data.defaultDatasetId
curl "https://api.apify.com/v2/actor-runs/RUN_ID?token=YOUR_TOKEN"            # status: RUNNING / SUCCEEDED
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&token=YOUR_TOKEN" > woolworths.csv
```

4. The run's **SUMMARY** record (key-value store) explains the outcome: rows saved, matching products on Woolworths, pages read, why the run stopped, notes about inputs. Read it with `GET /v2/actor-runs/RUN_ID/key-value-store/records/SUMMARY`.

***

### Input reference

| Field | Type | Default | What it does |
|---|---|---|---|
| `mode` | string | `search` | `search` · `categories` · `catalog` · `specials` · `halfPrice` · `products` (see the cookbook). |
| `searchTerms` | array of strings | `[]` | Keywords as typed on woolworths.com.au. One search per term. |
| `categoryUrls` | array of strings | `[]` | Category URLs (`/shop/browse/...`), category names (`Long Life Milk`), search URLs (`/shop/search/products?searchTerm=...`) or product URLs. |
| `productIds` | array of strings | `[]` | Stockcodes (`33557`), barcodes (`9300639500041`) or product URLs. |
| `maxItems` | integer | `1000` | Unique products to save across the run. `0` = no limit. |
| `specialsOnly` | boolean | `false` | Only products on special or in an offer (Woolworths' own Specials filter), in any mode. |
| `includeUnavailable` | boolean | `true` | Keep products that are out of stock online right now (about 4% of the range). |
| `includeEverydayMarket` | boolean | `false` | Add the Everyday Market marketplace (third-party sellers). Off = products sold by Woolworths. |
| `sortBy` | string | `relevance` | Order for a search when only its top is taken: `relevance`, `name`, `priceAsc`, `priceDesc`. A search read to the end is always read in name order (no product skipped). |
| `includeRatings` | boolean | `true` | Star rating, rating count and review count (one extra request per 150 products). |
| `includeDetails` | boolean | `false` | Country-of-origin label, Australian-ingredients %, and the shown price of products that are out of stock online (one extra request per product, about 4× the run time). |
| `postcode` | string | — | 4-digit postcode: prices, specials and availability of the nearest Woolworths pick-up store. |
| `storeId` | string | — | A Woolworths store number instead of a postcode. |
| `proxyConfiguration` | object | Apify Proxy | Connection settings. The default works; no residential proxy is needed. |

Validation: an unknown mode falls back to `search`; a postcode must be 4 digits and a store number 1–5 digits; inputs that match nothing are reported in the run's `SUMMARY` and status message instead of silently widening the search.

***

### Output data dictionary

Fill rates were measured on the whole-catalogue run of 2026-10-18 with the default settings (24,990 products); the three country-of-origin fields, which need the option, on the 10,471-product run with the option on (four departments: Pantry, Beauty, Personal Care, Health & Wellness). Fields that only apply to some products (multibuy, marketplace, store) are explained in the "Meaning" column.

| Field | Type | Meaning | Filled | Example |
|---|---|---|---|---|
| `productId` | string | Woolworths stockcode as text — the stable product key (same name as in our Coles rows). | 100.0% | `717730` |
| `stockcode` | number | Woolworths stockcode, the number in the product URL. | 100.0% | `717730` |
| `retailer` | string | Always `Woolworths` — handy when stacking supermarkets. | 100.0% | `Woolworths` |
| `name` | string | Product name without the pack size. | 100.0% | `Ayam Nasi Goreng Paste` |
| `brand` | string | Brand (`Woolworths` / `Macro` for private label). | 99.3% | `Ayam` |
| `title` | string | Full display name including the size, as on the shelf tile. | 100.0% | `Ayam Nasi Goreng Paste 185g` |
| `size` | string | Pack size as Woolworths writes it (`1L`, `6 pack`, `per 100g`, `each`). | 100.0% | `185g` |
| `variety` | string | Variety / flavour where Woolworths lists one. | 74.5% | `Paste` |
| `url` | string | Product page on woolworths.com.au. | 100.0% | — |
| `imageUrl` | string | Main product image (large). | 100.0% | — |
| `imageUrls` | array | All product images (large), main image first. | 100.0% | — |
| `barcode` | string | GTIN / EAN barcode (13 digits for most products; 8, 12 or 14 for some; empty for marketplace listings). | 100.0% | `9556041612753` |
| `price` | number | Current price in AUD for the listed size (per 100 g etc. for products sold by weight). | 95.6% | `4` |
| `wasPrice` | number | Regular price; equals `price` when the product is not on special. | 100.0% | `4` |
| `savings` | number | Saving in AUD while on special. | 23.5% | `1` |
| `savingsPercent` | number | Saving as a percentage of `wasPrice`. | 23.5% | `25` |
| `instorePrice` | number | Price Woolworths lists for buying in store (same as `price` for almost all products). | 95.6% | `4` |
| `isOnSpecial` | boolean | On special (temporary price cut). | 100.0% | `false` |
| `isHalfPrice` | boolean | Woolworths half-price special. | 100.0% | `false` |
| `isMultibuy` | boolean | A quantity deal applies ("2 for $8.00"). | 100.0% | `false` |
| `isOnlineOnly` | boolean | Online-only special. | 100.0% | `false` |
| `specialType` | string | `HALF_PRICE`, `SPECIAL`, `LOWER_SHELF_PRICE`, `EVERYDAY_LOW_PRICE`, `SEASONAL_PRICE` or `MULTIBUY`. | 43.4% | `EVERYDAY_LOW_PRICE` |
| `promoLabel` | string | The offer label as shown ("Half price · Save $2.50", "Lower shelf price"). | 38.7% | `Everyday low price` |
| `multibuyQuantity` | number | Quantity of the multibuy deal. | 4.7% | `2` |
| `multibuyPrice` | number | Price for that quantity. | 4.7% | `17.6` |
| `multibuyLabel` | string | Multibuy text, e.g. "2 for $8.00". | 4.7% | `2 for $17.60` |
| `multibuyUnitPrice` | string | Unit price when bought on the multibuy deal. | 4.4% | `$3.52/100G` |
| `priceNote` | string | Price note under the price: previous price and date ("Was $6.60 - 25/08/2026"), container-deposit bonus, etc. | 8.5% | `Was $5 - 11/11/2025` |
| `offerId` | string | Woolworths offer id shared by all products in the same promotion. | 29.5% | `25405369` |
| `unitPrice` | number | Cup (unit) price in AUD per `unitOfMeasure`. | 82.1% | `2.16` |
| `unitOfMeasure` | string | Unit of the cup price: `100G`, `100ML`, `1L`, `1KG`, `1EA`, `100EA` … | 82.1% | `100G` |
| `unitPriceText` | string | Cup price as displayed ("$0.25 / 100G"). | 82.1% | `$2.16 / 100G` |
| `isSoldByWeight` | boolean | Sold by weight (deli, loose produce): price is for the listed size, unit price per kg. | 100.0% | `false` |
| `isAvailable` | boolean | Can be bought online right now. | 100.0% | `true` |
| `isInStock` | boolean | In stock online. | 100.0% | `true` |
| `purchaseLimit` | number | Maximum quantity per order (kg for products sold by weight). | 100.0% | `7` |
| `isNew` | boolean | Marked as new by Woolworths. | 100.0% | `false` |
| `isAgeRestricted` | boolean | Liquor or other age-restricted product. | 100.0% | `false` |
| `deliveryRestriction` | string | Delivery restriction message ("Remote Orders Restricted Goods", "Afternoon delivery & pick up"). | 4.9% | `Remote Orders Restricted Goods` |
| `isMarketplaceProduct` | boolean | Everyday Market listing from a third-party seller. | 100.0% | `false` |
| `sellerName` | string | Marketplace seller (marketplace listings only). | 0.0% | — |
| `rating` | number | Average star rating 1–5 (rated products only). | 33.8% | `5` |
| `reviewCount` | number | Number of written reviews (0 when none). | 100.0% | `0` |
| `ratingCount` | number | Number of star ratings (0 when none). | 100.0% | `0` |
| `department` | string | Department (aisle group) on woolworths.com.au, the product's home aisle. | 100.0% | `Pantry` |
| `category` | string | Category within the department. | 100.0% | `International Foods` |
| `subCategory` | string | Subcategory within the category. | 100.0% | `Asian` |
| `categoryPath` | string | `Department > Category > Subcategory`. | 100.0% | `Pantry > International Foods >…` |
| `allCategoryPaths` | string | Every aisle the product is listed in, pipe-separated (when more than one). | 47.4% | `Pantry > International Foods >…` |
| `departmentId` | string | Woolworths node id of the department. | 100.0% | `1_39FD49C` |
| `categoryId` | string | Woolworths node id of the category. | 100.0% | `1_53601CD` |
| `subCategoryId` | string | Woolworths node id of the subcategory. | 100.0% | `1_A43B787` |
| `merchandiseDepartment` | string | Woolworths internal merchandise department. | 99.5% | `GROCERIES` |
| `merchandiseCategory` | string | Internal merchandise category. | 99.5% | `ETHNIC / GOURMET FOOD` |
| `merchandiseSubCategory` | string | Internal merchandise subcategory. | 99.5% | `ASIAN FOODS` |
| `merchandiseSegment` | string | Internal merchandise segment (finest level). | 99.5% | `SPICE / PASTES` |
| `description` | string | Product description as plain text. | 93.2% | `Traditionally used as a base f…` |
| `ingredients` | string | Ingredients list. | 66.9% | `Onion, Water, Soybean Oil, Dar…` |
| `allergens` | string | Allergens the product contains, pipe-separated. | 32.6% | `Soy` |
| `allergensMayContain` | string | "May contain" (traces) allergens. | 21.3% | `Peanuts` |
| `allergenFreeFrom` | string | Free-from claims ("Gluten Free | Egg Free"). | 46.0% | `Dairy Free \| Egg Free \| Fish…` |
| `lifestyle` | string | Dietary and lifestyle statements ("Gluten Free | Vegan | Low Fat"). | 47.9% | `Vegan \| Vegetarian` |
| `lifestyleClaims` | string | Certified lifestyle claims ("Halal", "Kosher", "Organic", "Vegan"). | 13.2% | `Vegan` |
| `healthStarRating` | number | Health Star Rating 0.5–5. | 24.7% | `3.5` |
| `servingSize` | string | Serving size from the nutrition panel. | 45.6% | `46 g` |
| `servingsPerPackage` | number | Servings per pack. | 45.2% | `4` |
| `nutritionPer100g` | object | Nutrition per 100 g / 100 mL (keys listed below the table). | 46.1% | `{"carbohydrateG": 14.9, "energ…` |
| `nutritionPerServing` | object | Nutrition per serving. | 45.9% | `{"carbohydrateG": 6.9, "choles…` |
| `nutritionText` | string | The nutrition panel as readable text. | 47.0% | `Energy 395kJ per serve, 858kJ …` |
| `storageInstructions` | string | Storage instructions. | 49.7% | `Refrigerate after opening and …` |
| `countryOfOrigin` | string | Country of origin (from the label; Australia, New Zealand, China …). | 59.9% | `Malaysia` |
| `countryOfOriginStatement` | string | Country-of-origin label text. | 64.5% | `Made in Malaysia` |
| `australianIngredientsPercent` | number | Share of Australian ingredients stated on the label. | 32.6% | `0` |
| `storeId` | string | Woolworths store whose prices the row carries (`1101` = the default online store). | 100.0% | `1101` |
| `storeName` | string | Store name when a postcode or store number was given. | 0.0% | — |
| `storeState` | string | State of that store. | 0.0% | — |
| `source` | string | Which search, category or list found the product. | 100.0% | `category: Pantry` |
| `scrapedAt` | string | When the row was collected (ISO 8601, UTC). | 100.0% | — |

`nutritionPer100g` and `nutritionPerServing` are objects with any of these numeric keys (units in the name): `energyKj`, `energyKcal`, `proteinG`, `fatTotalG`, `fatSaturatedG`, `fatTransG`, `fatPolyunsaturatedG`, `fatMonounsaturatedG`, `carbohydrateG`, `sugarsG`, `dietaryFibreG`, `sodiumMg`, `calciumMg`, `potassiumMg`, `cholesterolMg`, `caffeineMg`, `glutenMg`, `lactoseG`. A value printed as "less than" on the pack (e.g. `<1g`) is left out of the object and kept in `nutritionText`.

***

### Output example

A real row from the 2026-10-18 run with the country-of-origin option on (long texts shortened; empty fields, ids and the per-serve panel left out):

```json
{
  "productId": "13674",
  "stockcode": 13674,
  "name": "Continental Classics Cup A Soup Mild Chicken Curry",
  "brand": "Continental",
  "title": "Continental Classics Cup A Soup Mild Chicken Curry 58g",
  "size": "58g",
  "variety": "Chicken Curry",
  "url": "https://www.woolworths.com.au/shop/productdetails/13674/continental-classics-cup-a-soup-mild-chicken-curry",
  "imageUrl": "https://cdn1.woolworths.media/content/wowproductimages/large/013674.jpg",
  "imageUrls": ["https://cdn1.woolworths.media/content/wowproductimages/large/013674.jpg", "…"],
  "barcode": "9300667005938",
  "price": 1.8,
  "wasPrice": 3,
  "savings": 1.2,
  "savingsPercent": 40,
  "instorePrice": 1.8,
  "isOnSpecial": true,
  "isHalfPrice": false,
  "isMultibuy": false,
  "isOnlineOnly": false,
  "specialType": "SPECIAL",
  "promoLabel": "Save $1.20",
  "unitPrice": 3.1,
  "unitOfMeasure": "100G",
  "unitPriceText": "$3.10 / 100G",
  "isSoldByWeight": false,
  "isAvailable": true,
  "isInStock": true,
  "purchaseLimit": 11,
  "isNew": false,
  "isAgeRestricted": false,
  "isMarketplaceProduct": false,
  "rating": 5,
  "reviewCount": 1,
  "ratingCount": 1,
  "department": "Pantry",
  "category": "Canned Food & Instant Meals",
  "subCategory": "Canned Soup & Soup Ingredients",
  "categoryPath": "Pantry > Canned Food & Instant Meals > Canned Soup & Soup Ingredients",
  "merchandiseDepartment": "GROCERIES",
  "merchandiseCategory": "PREPARED FOODS",
  "merchandiseSubCategory": "AMBIENT SOUPS",
  "merchandiseSegment": "PACKET SOUP",
  "description": "CONTINENTAL LOTS-A-NOODLES SOUP MILD CHICKEN CURRY 58 GR\n\nTreat yourself to Continental…",
  "ingredients": "Ingredients: Noodles^ (45%) (wheat flour, gluten), starch (corn, potato), creamer veget…",
  "allergens": "Milk | Wheat",
  "allergensMayContain": "Crustacean | Egg | Peanuts | Sesame | Soy | Lupin",
  "allergenFreeFrom": "Fish Free | No Artificial Colours or Flavours",
  "lifestyle": "Low Fat | Low Sugar",
  "healthStarRating": 3.5,
  "servingSize": "250 mL",
  "servingsPerPackage": 2,
  "nutritionPer100g": {"carbohydrateG": 7.7, "dietaryFibreG": 0.2, "energyKj": 194, "fatSaturatedG": 1, "fatTotalG": 1.1, "proteinG": 1, "sodiumMg": 265, "sugarsG": 0.6},
  "countryOfOrigin": "Australia",
  "countryOfOriginStatement": "Made in Australia from at least 48% Australian ingredients",
  "australianIngredientsPercent": 48,
  "storeId": "1101",
  "scrapedAt": "2026-10-18T20:59:37.419Z"
}
```

**CSV / Excel columns** (in this order): productId, stockcode, retailer, name, brand, title, size, variety, url, imageUrl, imageUrls, barcode, price, wasPrice, savings, savingsPercent, instorePrice, isOnSpecial, isHalfPrice, isMultibuy, isOnlineOnly, specialType, promoLabel, multibuyQuantity, multibuyPrice, multibuyLabel, multibuyUnitPrice, priceNote, offerId, unitPrice, unitOfMeasure, unitPriceText, isSoldByWeight, isAvailable, isInStock, purchaseLimit, isNew, isAgeRestricted, deliveryRestriction, isMarketplaceProduct, sellerName, rating, reviewCount, ratingCount, department, category, subCategory, categoryPath, allCategoryPaths, departmentId, categoryId, subCategoryId, merchandiseDepartment, merchandiseCategory, merchandiseSubCategory, merchandiseSegment, description, ingredients, allergens, allergensMayContain, allergenFreeFrom, lifestyle, lifestyleClaims, healthStarRating, servingSize, servingsPerPackage, nutritionPer100g, nutritionPerServing, nutritionText, storageInstructions, countryOfOrigin, countryOfOriginStatement, australianIngredientsPercent, storeId, storeName, storeState, source, scrapedAt. Arrays (`imageUrls`) and objects (`nutritionPer100g`, `nutritionPerServing`) are flattened by Apify's CSV export into numbered or dotted columns (`imageUrls/0`, `nutritionPer100g/sugarsG`).

***

### Scale and speed — measured

| Run (Apify platform, 2026-10-18) | Products | Time |
|---|---|---|
| First run with the form's starting values (search "milk", 50 products, default settings) | 50 | **5.6 s** |
| 1,000 products, whole-catalogue mode, default settings | 1,000 | **18 s** |
| 1,000 products, whole-catalogue mode, country-of-origin option on | 1,000 | **74 s** |
| Search "chocolate", country-of-origin option on — every result | 951 | **80 s** |
| All specials & offers | 6,976 | **54 s** |
| Half-price specials | 1,844 | **14 s** |
| **The whole Woolworths range**, default settings | **24,990**, 0 duplicates | **124 s** |
| Four departments (Pantry, Beauty, Personal Care, Health & Wellness), country-of-origin option on | **10,471**, 0 duplicates | **704 s** |
| Store prices: milk category at Darwin City (postcode 0800) | 191 of 284 listed | **28 s** |
| Everyday Market "Toys" category (Woolworths reports 13,206) | 13,228 (measured from a laptop against the live site, not on the platform) | **126 s** |

**How big is Woolworths?** On 2026-10-18 the Woolworths-sold range was 24,990 unique products; department pages list 37,241 entries because "Lunch Box", "Back to School" or "Dinner" re-list products from the core aisles. The Everyday Market marketplace adds about 1.19 million third-party listings.

**What makes it fast and complete.** Woolworths serves 36 products per page and at most 10,000 per category or search. The Actor reads in name order (the relevance order shifts between pages: two full passes over Pantry missed 47 and 17 products, name order none), skips sponsored repeats, splits marketplace categories above 10,000 and reads any query still above it from both ends (A→Z, Z→A: up to 20,000). Requests are spread over several connections at a steady pace; the 2026-10-18 whole-catalogue run made 1,299 requests and every one was answered.

**Fill rates that matter** (whole range, 2026-10-18): barcode 100.0%, price 95.6%, unit price 82.1%, brand 99.3%, category path 100.0%, description 93.2%, ingredients 66.9%, lifestyle claims 47.9%, nutrition panel 46.1%, allergens 32.6%, ratings 33.8%, Health Star Rating 24.7%; with the country-of-origin option, a label on 64.5% of the four-department run. Food fields are naturally empty for non-food products (cleaning, beauty, pet, homewares); ratings exist only for products customers have rated.

***

### How Woolworths prices, specials, unit prices and Health Star Ratings work

**Shelf price, was price and savings.** `price` is today's price on woolworths.com.au; on special, `wasPrice` is the regular price and `savings` / `savingsPercent` the difference, otherwise `wasPrice` equals `price`.

**Specials types.** `specialType` names the offer Woolworths shows on the product:

- `HALF_PRICE` — Woolworths' Half Price specials (`isHalfPrice: true`). The weekly catalogue specials start on Wednesdays.
- `SPECIAL` — any other temporary price cut ("Save $1.50").
- `LOWER_SHELF_PRICE` — a lasting price reduction ("Prices dropped"); `priceNote` often carries the previous price and the date it changed: `Was $6.60 - 25/08/2026` or `Range was $20 - 11/08/2026`.
- `EVERYDAY_LOW_PRICE` — Woolworths' "Low Price" (always-low) products.
- `SEASONAL_PRICE` — a seasonal price reduction.
- `MULTIBUY` — a quantity deal; `multibuyLabel` `2 for $8.00`, `multibuyQuantity` `2`, `multibuyPrice` `8`, `multibuyUnitPrice` `$1.96/100G`.

`isOnlineOnly` marks online-only specials. `promoLabel` is the label text as shown ("Half price · Save $2.50", "Lower shelf price", "Everyday low price").

**Cup price (unit price).** Australian supermarkets must show a comparable unit price: `unitPrice` per `unitOfMeasure` (`100G`, `1L`, `1KG`, `1EA` …) and `unitPriceText` (`$0.25 / 100G`) compare pack sizes — the cheapest pack is not always the cheapest per 100 g.

**Products sold by weight.** Deli and loose items sold by weight (`isSoldByWeight: true`, sizes like `per 100g`) carry the shelf price for that size (`$1.99` per 100 g, as on the product page) in `price`, and the price per kilogram in `unitPrice` (`$19.90 / 1KG`). Their `purchaseLimit` is in kilograms.

**Out-of-stock products.** About 4% of the range is temporarily unavailable online at any time (`isAvailable: false`). Category pages hide their price, so `price` is empty for them by default; with **Include country of origin** on, the Actor takes it from the product's own page, which still shows it.

**Health Star Rating.** The Australian/New Zealand front-of-pack rating from 0.5 to 5 stars (`healthStarRating`), printed on many packaged foods; higher is healthier within a category.

**Country of origin.** Australia's country-of-origin food labels — "Made in Australia from at least 90% Australian ingredients", "Grown in Australia", "Made in China" — come in `countryOfOriginStatement`; `countryOfOrigin` is the country and `australianIngredientsPercent` the share of Australian ingredients where the label states it.

**Store prices.** Woolworths prices are the same in most stores, but not everywhere. We priced the same 200 randomly chosen products at seven stores on 2026-10-18 and compared them with the default online prices taken at the same moment:

| Store (state) | Products the store stocks | Priced differently | Direction |
|---|---|---|---|
| Pyrmont Metro (NSW) | 119 | 12 (10.1%) | all higher |
| Darwin City (NT) | 167 | 15 (9.0%) | all higher |
| Melbourne Square, Southbank (VIC) | 162 | 6 (3.7%) | mostly higher |
| Subiaco Square (WA) | 175 | 4 (2.3%) | mixed |
| Channel Court (TAS) | 157 | 3 (1.9%) | mixed |
| Mount Sheridan, Cairns (QLD) | 165 | 1 (0.6%) | lower |
| Town Hall, Sydney (NSW) | 99 | 0 | — |

Fresh and chilled categories differ much more: in the milk aisle, 109 of the 191 products Darwin City stocks were priced differently. Without a postcode or store number you get woolworths.com.au's default online prices.

**Sponsored products.** Sponsored tiles repeat products listed in their own aisle, so the Actor skips them and every product appears once.

***

### What you pay

**A fixed price per product saved** — compute, proxies and the extra requests for ratings and country of origin are included. There is no monthly rental, and you do not pay for duplicates, sponsored tiles or products that were filtered out.

| Apify plan | Price per 1,000 products |
|---|---|
| Free | $2.00 |
| Bronze | $1.77 |
| Silver | $1.53 |
| Gold, Platinum, Diamond | $1.30 |

Examples: 1,000 products = **$2.00** on the Free plan or **$1.30** on Gold · 10,000 products = **$20.00** / **$13.00** · 100,000 products (e.g. four weekly catalogue snapshots) = **$200** / **$130** · **the whole Woolworths range** (24,990 products on 2026-10-18) ≈ **$50.00** on Free or **$32.50** on Gold.

#### Maximum cost per run

Every pay-per-result run has a **Maximum cost per run** (in the run options; Apify also applies one when you set none). The Actor reads it when it starts, saves exactly as many products as it covers and then stops cleanly with a message such as *"Saved 25 products — this run's maximum cost ($0.05) covers 25 results at the current price. Raise 'Maximum cost per run' to get more."* The run still ends as **Succeeded**, `SUMMARY` records `stoppedBy: "MAX_COST"`, and you pay only for the products saved. For a whole-catalogue export, set the limit above the examples above.

***

### Integrations

#### API (curl)

```bash
curl -X POST "https://api.apify.com/v2/acts/logiover~woolworths-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"specials","maxItems":500}'
```

#### Node.js (apify-client)

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('logiover/woolworths-scraper').call({
    mode: 'categories',
    categoryUrls: ['https://www.woolworths.com.au/shop/browse/pantry/coffee'],
    maxItems: 0,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const cheapest = items.filter((p) => p.unitPrice).sort((a, b) => a.unitPrice - b.unitPrice).slice(0, 10);
console.log(cheapest.map((p) => `${p.title}: ${p.unitPriceText}`));
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("logiover/woolworths-scraper").call(run_input={"mode": "specials", "maxItems": 0})
specials = list(client.dataset(run["defaultDatasetId"]).iterate_items())

## price-change tracking: compare with last week's run (saved as a dict productId -> price)
import json, pathlib
last = json.loads(pathlib.Path("last_week.json").read_text()) if pathlib.Path("last_week.json").exists() else {}
for p in specials:
    old = last.get(p["productId"])
    if old is not None and old != p["price"]:
        print(f'{p["title"]}: {old} -> {p["price"]} ({p["promoLabel"]})')
pathlib.Path("last_week.json").write_text(json.dumps({p["productId"]: p["price"] for p in specials}))
```

#### Make, Zapier and n8n

Use the Apify app / node: **Run an Actor** (`logiover/woolworths-scraper`, your input JSON), then **Get dataset items** and map `title`, `price`, `wasPrice`, `promoLabel`, `barcode` … into Sheets, Airtable, Slack, email or a database. n8n's Apify node plus a Schedule Trigger is a no-code weekly price feed.

#### Google Sheets and Airtable

Add the **Google Sheets** integration to a task to append or replace rows after every run. For Airtable use Make, Zapier or n8n, keyed on `productId`.

#### BI tools (Power BI, Tableau, Looker Studio, Excel)

Point the tool at the dataset's CSV/JSON URL (`https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&token=...`) or at your warehouse table; fields are typed and consistently named.

#### Webhooks and scheduling

Schedule a task (cron, any time zone) and add a webhook on **Run succeeded**; your endpoint gets the dataset id and pulls the rows. Typical cadence: specials every Wednesday, the catalogue weekly, a basket daily.

#### Data warehouses (BigQuery, Snowflake, Postgres, S3)

Load each run's dataset with a webhook-triggered job (or the S3 / Google Drive integrations); key a history table on `productId` + `scrapedAt`.

#### MCP and AI agents

The Actor works through the Apify MCP server, so Claude, ChatGPT or your own agent can call it as a tool ("what is half price at Woolworths this week in snacks?") and receive structured rows.

***

### Performance tips

- **Keep "Include country of origin" off for bulk price exports**: the whole range takes about two minutes; with the option on it needs one extra request per product (roughly 28 minutes for the whole range).
- **Use the right mode.** "All specials & offers" (about 7,000 products) is much smaller than the whole catalogue; a category URL is faster than a broad search.
- **Leave the proxy setting at its default.** The Actor already spreads requests over several connections at a pace Woolworths accepts; residential proxies only add cost.
- **Set `maxItems` to what you need** — the Actor never fetches more pages than the remaining number of products can use.
- **Ratings cost almost nothing** (one request per 150 products); keep them on unless you never use them.
- **Store prices need one request per product** — scope them to the categories you monitor.
- **Schedule instead of re-running by hand**, and use the CSV export URL directly in your tools.

***

### Troubleshooting

Every run ends with a status message and a `SUMMARY` record (`outcome`, rows saved, products matching on Woolworths, pages read, notes, connections).

| Outcome / message | What happened | What to do |
|---|---|---|
| **COMPLETED** — "every matching product read" | Everything that matched was saved. | — |
| **COMPLETED** — "Saved N products (max results reached)" | `maxItems` reached before the end. | Raise `maxItems` or set `0`. |
| **COMPLETED**, `stoppedBy: MAX_COST` — "maximum cost ($X) covers N results" | The Maximum cost per run was reached; clean stop. | Raise *Maximum cost per run*. |
| **PARTIAL** | Products were saved, but some pages could not be read after retries (`pagesSkipped` > 0). | Re-run; the missing pages are usually there next time. |
| **EMPTY_RESULT** — "the search ran fine but returned 0 results" | Woolworths answered and nothing matched. | Check spelling, try a broader term, turn off *Specials only*. |
| **BAD_INPUT** (run fails) | No usable input: e.g. no search term, no category URL that exists, no valid stockcode or barcode, a malformed postcode. | Read the message — it names the input that failed. |
| **BLOCKED** / **FETCH_FAILED** (run fails) | Woolworths could not be reached or refused every connection after retries — never an empty "success". | Retry in a few minutes; keep the default proxy setting. |
| **INCOMPLETE_NO_ROWS** (run fails) | Nothing was saved and some pages failed, so an empty result cannot be confirmed. | Re-run. |
| Note: "… not an exact Woolworths category path any more — using …" | An old URL was matched to the closest current category. | Paste the current URL to be exact. |
| Note: "N of M stockcode(s)/barcode(s) are not sold on woolworths.com.au" | Not (or no longer) listed. | Remove them from the basket. |
| Note: "Skipped 'Front of Store'" | The tobacco aisle is shown to logged-in shoppers only. | — |
| Log: "… above Woolworths' 10,000-per-query limit — reading it A→Z and Z→A" | A huge marketplace category or search. | Narrow it for coverage above 20,000. |
| "Saved X/Y products · N pages" stays put for a while | Product-detail requests (country of origin, store prices) are running. | Wait, or turn the option off. |

***

### Honest limits

- **One category or search answers at most 10,000 products.** Only the Everyday Market marketplace gets there; the Actor splits by sub-category and reads from both ends, which reaches up to 20,000 per leaf category or search. Woolworths' own departments are all well below the limit.
- **Searches above 10,000 report "10,000".** Woolworths never says the real total of a huge marketplace search; the Actor reads up to 20,000 and lists the search under `queriesAboveTheCap` in `SUMMARY`.
- **Member-only prices are not public.** Everyday Rewards member prices and personalised offers are shown only to signed-in members; the Actor reports the public price everyone sees.
- **Store prices** need one product-detail request per product; without a store you get woolworths.com.au's default online price.
- **Country of origin, ingredients, allergens, nutrition and Health Star Ratings exist only where Woolworths publishes them** — mostly packaged food. Non-food products have none.
- **Marketplace listings have no barcode** in Woolworths' data.
- **Ratings** exist only for products customers have rated (33.8% of the range); unrated products have `ratingCount: 0`.
- **Prices change** (specials weekly); a dataset is a snapshot at `scrapedAt`.

***

### Compared with checking prices by hand

| | This Actor | Checking woolworths.com.au by hand |
|---|---|---|
| Products per hour | The whole range (24,990) in about two minutes | A few hundred, with care |
| Specials, half price, multibuy, previous-price notes | Structured fields | Read from badges |
| Unit (cup) price | Number + unit, sortable | Text on each tile |
| Barcode (GTIN), merchandise hierarchy | Included | Not shown on the page |
| Ingredients, allergens, nutrition panel, Health Star Rating | Structured fields | Open every product page |
| Country-of-origin label | Statement, country, Australian % | Image on the product page |
| Repeatable on a schedule | Yes, with history | No |
| Export to CSV / Excel / JSON / API | Yes | Copy and paste |

***

### FAQ

#### Is there an official Woolworths API?

Woolworths offers no public product or price API. This Actor reads the public JSON that woolworths.com.au loads in your browser and returns a structured dataset — a Woolworths API alternative without an account or key.

#### How do I scrape woolworths.com.au?

Choose *What to scrape* (search terms, category URLs, the whole catalogue, specials, half price or a product list), press Start and export CSV, Excel or JSON. No code needed; use the API to automate.

#### Do I need a Woolworths account, login or cookies?

No. Everything the Actor reads is public. You need only an Apify account to run it.

#### Do I need proxies?

No setup. The default setting adds Apify Proxy sessions to the direct connection to keep a steady pace; residential proxies are not needed.

#### How many products does Woolworths sell online?

On 2026-10-18 the Woolworths-sold range was 24,990 unique products; the Everyday Market marketplace adds about 1.19 million third-party listings.

#### Can I download the entire Woolworths product list with prices?

Yes: mode **Whole catalog** with `maxItems: 0` — 24,990 products in 124 s on 2026-10-18. With the country-of-origin option on, expect roughly 28 minutes.

#### How do I track Woolworths prices over time?

Schedule a task and compare runs on `productId` ([Recipe 11](#recipe-11--price-change-tracking-with-a-schedule)).

#### When do Woolworths specials change?

On Wednesdays. Schedule specials or catalogue runs for Wednesday morning, Australian time.

#### How do I get only Woolworths half-price products?

Use mode **Half-price specials only** — 1,844 products on 2026-10-18. In other modes, filter on `isHalfPrice: true`.

#### Can I get all Woolworths specials and catalogue deals?

Yes: mode **All specials & offers** (6,976 products on 2026-10-18); `specialType` names each deal.

#### What is the Woolworths cup price and does the Actor include it?

The comparable unit price (per 100 g, litre, kg or item): `unitPrice`, `unitOfMeasure`, `unitPriceText` — 82.1% of the range.

#### Can I get Woolworths product barcodes (EAN / GTIN)?

Yes — `barcode` is filled for 100.0% of Woolworths-sold products (EAN-13 in most cases). You can also look products up *by* barcode in mode **Products**.

#### Can I search Woolworths by barcode?

Yes — barcodes in `productIds` (mode **Products**) are matched exactly ([Recipe 9](#recipe-9--barcode-gtin-lookup)).

#### Does it include ingredients and allergens?

Yes: `ingredients` (66.9% of the range), `allergens`, `allergensMayContain` and `allergenFreeFrom`.

#### Does it include nutrition information?

Yes: per 100 g and per serve, plus serving size and `nutritionText` — 46.1% of the range.

#### Does it include the Health Star Rating?

Yes, `healthStarRating` (0.5–5) wherever Woolworths shows one (24.7% of the range, mostly packaged food).

#### Does it include country of origin ("Made in Australia")?

Yes, with **Include country of origin** on: `countryOfOriginStatement` (the label text), `countryOfOrigin` and `australianIngredientsPercent`. On our 2026-10-18 four-department run 64.5% of products had a label (non-food products have none).

#### Does it include customer reviews and ratings?

It includes the star rating (`rating`), `ratingCount` and `reviewCount` for every rated product (33.8% of the range). Review texts are not exported.

#### Are Woolworths prices the same in every store?

Mostly, not always. In our 2026-10-18 sample of 200 products, up to 10% of what a store stocks was priced differently — highest at a Sydney Metro store and in Darwin, near zero at a large Sydney CBD store — and in Darwin's milk aisle 109 of 191 products differed. Set `postcode` or `storeId` to get a particular store's prices.

#### How do I get prices for my local Woolworths store?

Enter your postcode (or a store number); rows then carry that store's prices, specials and availability, with `storeName` and `storeState`.

#### Can I scrape a specific Woolworths category?

Yes: mode **Category URLs** with the category URL or simply its name.

#### Can I get every product of one brand?

Yes — see [Recipe 8](#recipe-8--a-brands-whole-range).

#### How fast is it?

1,000 products in 18 s with the default settings and 74 s with the country-of-origin option; the whole range in 124 s. Measured 2026-10-18.

#### How much does it cost to scrape the whole Woolworths catalogue?

About $50.00 on the Apify Free plan and $32.50 on Gold and higher (24,990 products at $2.00 or $1.30 per 1,000). Compute and proxies are included.

#### Can I export Woolworths data to Excel or Google Sheets?

Yes: Excel/CSV from the Output tab, or the Google Sheets integration after every run.

#### Can I combine Woolworths and Coles prices?

Yes. Our Coles dataset uses the same field names (`price`, `wasPrice`, `unitPrice`, `unitOfMeasure`, `isOnSpecial`, `barcode`, `brand`, `categoryPath` …), so you can stack both into one table and join on `barcode`. See [Recipe 15](#recipe-15--merge-with-coles-data).

#### Does it scrape Everyday Market (marketplace) products?

Optionally (**Include Everyday Market**); rows carry `isMarketplaceProduct` and `sellerName`.

#### Why do some products have no price?

They are temporarily unavailable online (`isAvailable: false`, about 4% of the range); category pages hide their price. Turn on *Include country of origin* and the Actor takes the price shown on the product's own page.

#### Why did my run stop before Max results?

Either nothing more matched ("every matching product read") or the run reached its Maximum cost per run (the status says how many results it covers).

#### What happens if Woolworths is down or blocks the run?

It retries on fresh connections; if nothing works the run **fails** with the reason in the status message and `SUMMARY` — never an empty "success".

#### Can I use the data for an app or a commercial product?

It is public product and price information; check Woolworths' terms and your local law ([legality](#is-it-legal-to-scrape-woolworths)).

#### Does it work for Woolworths New Zealand?

No. This Actor reads woolworths.com.au (Australia). Woolworths New Zealand runs a different website.

#### I need a field or a filter that is missing. Can you add it?

Yes — open an issue on the Actor's Issues tab and describe what you need.

***

### Glossary

- **Stockcode** — Woolworths' product number, the number in the product URL; our `productId` / `stockcode`.
- **GTIN / EAN / barcode** — the global product number under the barcode (EAN-13 for most Australian products); `barcode`.
- **Cup price** — Woolworths' term for the comparable unit price ("$0.25 / 100G"); `unitPrice`, `unitOfMeasure`, `unitPriceText`.
- **Was price** — the regular price before a special; `wasPrice`.
- **Half Price** — the weekly half-price specials; `isHalfPrice`.
- **Prices Dropped / Lower shelf price** — a lasting price cut, often with the old price and date; `LOWER_SHELF_PRICE`, `priceNote`.
- **Low Price / Everyday low price** — products kept at a low price; `EVERYDAY_LOW_PRICE`.
- **Multibuy** — a quantity deal ("2 for $8.00"); `isMultibuy`, `multibuyLabel`.
- **Online-only special** — a deal valid on woolworths.com.au only; `isOnlineOnly`.
- **EDR special / member price** — Everyday Rewards member-only prices, shown to signed-in members only, so not part of the public data.
- **Supply limit** — maximum quantity per order; `purchaseLimit` (kg for products sold by weight).
- **Health Star Rating (HSR)** — the 0.5–5 star front-of-pack rating used in Australia and New Zealand.
- **Country-of-origin label** — Australia's food origin label ("Made in Australia from at least 90% Australian ingredients").
- **Merchandise hierarchy** — Woolworths' internal buying hierarchy; the `merchandise*` fields.
- **Everyday Market** — Woolworths' marketplace of third-party sellers.
- **Sponsored product** — a paid placement repeated on listing pages; skipped so every product appears once.

***

### Is it legal to scrape Woolworths?

This Actor reads only publicly available product information — names, prices, specials, product attributes and aggregated ratings that any visitor sees on woolworths.com.au without logging in. It does not collect personal data: no customer accounts, no names of reviewers, no review texts. Product and price information is business information published to be seen. You are responsible for using the data in line with Woolworths' terms, Australian law (including the Australian Consumer Law and the Privacy Act where it applies), the GDPR or CCPA if they apply to you, and your own policies; if in doubt, ask a lawyer.

**Trademark and affiliation disclaimer:** This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Woolworths Group Limited, Woolworths Supermarkets or any of their subsidiaries. All trademarks mentioned are the property of their respective owners.

***

### Changelog

- **1.0 (2026-10-18)** — First release: search, category, whole-catalogue, specials, half-price and stockcode / barcode modes; complete name-order reads with splitting past the 10,000-per-query limit; 78 fields incl. specials, cup price, barcode, ingredients, allergens, nutrition, Health Star Rating and ratings; optional country of origin and store prices; Everyday Market option; polite multi-connection pacing; clean stop at the maximum cost per run.

***

### Support

Questions, bugs or a feature request? Open an issue on the **Issues** tab — we reply **within 24 hours**. If a run did something unexpected, include the run link so we can see its log and `SUMMARY`.

# Changelog

This Actor's version history is a separate document: https://apify.com/logiover/woolworths-scraper/changelog.md

# Actor input Schema

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

Search terms: products matching your keywords. Category URLs: every product in the categories you paste. Whole catalog: every Woolworths department (~25,000 products). All specials: every product on special or in an offer. Half-price specials: the Half Price page. Products: a list of stockcodes or product URLs (price monitoring).

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

Keywords as you would type them on woolworths.com.au, e.g. "milk", "coffee beans", "gluten free bread". Each term is one search. Used when "What to scrape" is Search terms.

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

Woolworths category pages, e.g. https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk or https://www.woolworths.com.au/shop/browse/specials/half-price. A category name such as "Long Life Milk" works too. Search URLs (/shop/search/products?searchTerm=...) and product URLs are accepted here as well.

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

Woolworths stockcodes (the number in the product URL, e.g. 33557), barcodes / GTINs (EAN-13, UPC, e.g. 9300639500041) or product URLs (https://www.woolworths.com.au/shop/productdetails/33557/...). Stockcodes are read 50 at a time; each barcode is one exact lookup. Ideal for monitoring the prices of a fixed basket. Used when "What to scrape" is Products.

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

Stop after this many unique products across all searches / categories. 0 = no limit (the whole range is ~25,000 products).

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

Only products on special or in an offer (Woolworths' own "Specials" filter), in any mode.

## `includeUnavailable` (type: `boolean`):

Woolworths also lists products that are temporarily out of stock online (about 4% of the range, shown without a current price). Turn off to keep only products you can buy now.

## `includeEverydayMarket` (type: `boolean`):

Everyday Market is Woolworths' marketplace of third-party sellers (over a million listings: homewares, toys, electronics...). Off = only products sold by Woolworths itself, which is what grocery price monitoring needs. On = marketplace listings too, with the seller name. Woolworths answers at most 10,000 products per category, so huge marketplace categories are split into sub-categories.

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

Order of search results when you take only the top of a long list. When a search is read to the end, the Actor reads it in name order instead so that no product is skipped (Woolworths' relevance order shifts between pages).

## `includeRatings` (type: `boolean`):

Adds the star rating, rating count and review count from woolworths.com.au's reviews (one extra request per 150 products — almost free).

## `includeDetails` (type: `boolean`):

Reads each product's detail page for the Australian country-of-origin label: the statement ("Made in Australia from at least 90% Australian ingredients", "Grown in Australia", "Made in China"), the country and the Australian-ingredients percentage, and the shown price of products that are out of stock online. One extra request per product: 1,000 products take about 75 seconds instead of about 20.

## `postcode` (type: `string`):

Optional. Woolworths prices differ between some stores (measured: about 5–12% of products, e.g. Darwin and Perth). Enter a 4-digit postcode (e.g. 0800) to get the prices and specials of the nearest Woolworths pick-up store. Leave empty for woolworths.com.au's default online prices. Needs one product-detail request per product.

## `storeId` (type: `string`):

Optional. A Woolworths store number (e.g. 1248 = Town Hall, Sydney) instead of a postcode. Prices, specials and availability are then that store's.

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

The Actor starts on a direct connection and adds Apify Proxy sessions (one IP each) only to keep a fast, polite pace. Datacenter proxies are enough; residential proxies are not needed.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "milk"
  ],
  "categoryUrls": [],
  "productIds": [],
  "maxItems": 50,
  "specialsOnly": false,
  "includeUnavailable": true,
  "includeEverydayMarket": false,
  "sortBy": "relevance",
  "includeRatings": true,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All products collected in this run.

# 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"
    ],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("logiover/woolworths-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 = {
    "searchTerms": ["milk"],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("logiover/woolworths-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 '{
  "searchTerms": [
    "milk"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call logiover/woolworths-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,logiover/woolworths-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/aBorm42VVEmkHdH72/builds/mvNpOjFoeKWYxnz8o/openapi.json
