Woolworths API - Online Price History & Store Data avatar

Woolworths API - Online Price History & Store Data

Pricing

from $0.30 / 1,000 products

Go to Apify Store
Woolworths API - Online Price History & Store Data

Woolworths API - Online Price History & Store Data

Export Woolworths online products, specials, barcodes, nutrition and nightly online price history. Mobile instore returns store prices and aisle, bay, floor and x/y/z from a four-hour cache, with live refresh on demand. No customer proxy or app session setup needed.

Pricing

from $0.30 / 1,000 products

Rating

0.0

(0)

Developer

CrawlPlant

CrawlPlant

Maintained by Community

Actor stats

0

Bookmarked

6

Total users

2

Monthly active users

3 days ago

Last modified

Share

Woolworths online price history and store data

Independent tool, not affiliated with or endorsed by Woolworths Group. It reads only public product and price data.

Get prices, unit prices, specials, barcodes and full product details (description, ingredients, allergens, nutrition, health stars, dietary tags, images) from woolworths.com.au as JSON, CSV, Excel or via API. It also returns online price history: the previous observed price and when we recorded its change, the lowest price in 30 days, the 90-day average, and whether a "special" beats that history. Search by keyword, category, URL, stockcode or barcode (EAN), get each store's own price and in-store availability for any of ~1,140 stores, or export the whole catalogue.

The optional mobile in-store mode adds native aisle, bay, floor and x/y/z data for explicit products in one store. It uses a shared four-hour store cache; missing or expired readings are fetched on our VPS through ScrapeOps AU. Guest tokens renew on the VPS without an emulator or sensor header. Member Price and price-family variants are not included.

Why this one

  • Cached catalogue queries. Web modes can use our nightly catalogue and return cached or partial results with warnings when live requests fail. Freshness depends on maxAgeHours and available data. See Reliability.
  • Fast. Cached queries return in 2-3 seconds; a 1,000-product live search takes about 20 seconds.
  • Nightly online price history. Previous price, when it changed, lowest price in 30 days, 90-day average, and a check whether a "special" is a genuine discount. Tracking since 25 September 2026, updated every night; see Online price history.
  • The whole catalogue in one run. Every product Woolworths sells (~25-30k), straight from the nightly snapshot.
  • Every store, with its own prices. Store prices can differ from the online price: pick stores by number, postcode or all ~1,140, and each store gets its own rows with that store's price and in-store availability. Plus the directory of all Woolworths supermarkets (address, coordinates, opening hours).
  • Clean data. Unit prices are normalised to per kg and per litre, nutrition is parsed, and text is cleaned up.
  • Pay per product, details included. $0.50 per 1,000 products on the Free plan, down to $0.30; product details, price history and deal checks included; the start fee is $0.00005 (a twentieth of a cent). See the price table.
  • See it live. Last night's price moves and specials by category, straight from the harvest: crawlplant.com/woolworths-au.

What can you use it for?

  • Price monitoring: shelf and unit prices of the products you care about, on a schedule.
  • Deal hunting and consumer research: current specials with a check on whether the discount is real.
  • Price-change alerts: products whose price changed since your last run, sent to email, Slack or Sheets.
  • Basket and store comparison: barcodes, MPNs and per-kg prices make matching across stores straightforward.
  • Nutrition and allergen apps: filter by dietary tags, allergens, health stars, sugar or protein.
  • Market and category research: brands, pack sizes, price points, new launches, full-catalogue exports.
  • AI agents: a fast, well-described tool for "find me the cheapest…" style questions (see Use with AI agents).

Quick start

  1. Click Try for free (or Start), keep the default input (search for milk, 100 products) and press Start. A 100-result run costs about $0.05 at the no-discount rate, plus the start fee.
  2. Open the Output tab: a table of products with prices, unit prices and specials. Switch views to Product details or Deals check, or export as JSON, CSV or Excel.
  3. Change the input using one of the ready-to-use examples below, and save it as a task to reuse or schedule it.

Copy to your AI assistant

Paste this into ChatGPT, Claude or any agent so it knows how to use the Actor:

crawlplant/woolworths-au on Apify: Woolworths Australia (woolworths.com.au) products and prices. Modes (field "mode"):
search (queries: keywords, default), category (categoryUrls: woolworths.com.au/shop/browse/... links), specials (optional
specialsGroups: half-price, buy-more-save-more, lower-shelf-price, seasonal-price, bundles, everyday-low-price), products
(productIds: stockcodes, the number in a product URL), barcodes (barcodes: EAN/GTIN, exact match), urls (any Woolworths
product/search/category/specials link), catalog (every product, ~25-30k, from the nightly snapshot; set maxItems high),
categories (the category tree with URLs), stores (all ~1,140 supermarkets: address, coordinates, opening hours).
instore (explicit numeric productIds, exactly one storeIds value): mobile store price and native aisle/bay/floor/x/y/z;
four-hour cache with live fallback on our VPS. maxAgeHours:0 forces live. No Member Price or
price-family variants. Web filters, history and full-detail enrichment do not apply to instore.
maxItems (default 100) applies per query/category/URL. Stores: storeIds (StoreNo), postcodes (every supermarket in the
postcode; a postcode without one gets a warning naming the nearest store) or allStores turn each product into one row per
store with storeId, storeName, that store's own price (priceScope "store", read live) and inStock = that store's in-store
availability (true / false / null = the store didn't report). Filters: sortBy
(relevance, price_asc, price_desc, unit_price_asc, name, specials_first, newest: sorted by Woolworths before paging;
discount_desc: collected results re-sorted by % off, use with specialsOnly and a high maxItems), specialsOnly,
inStockOnly, minPrice, maxPrice, minRating, brands, minDiscountPercent, excludeCategories, includeMarketplace (Everyday
Market sellers, off by default), dietary (e.g. "Gluten Free", all required), excludeAllergens, minHealthStarRating,
maxSugarPer100g, minProteinPer100g. Online price history: changedSince (ISO date or "last-run" = only price changes since the
previous run with the same input), includeDisappeared (with last-run: extra rows type "disappeared" for products the
previous run returned that are gone), onlyNew, onlyNewSpecials (last 7 days), priceHistoryDays (adds the list of changes).
Web online rows have published product fields and available history: price, wasPrice, unitPrice, pricePerKg, pricePerLitre, isOnSpecial, offerDescription, barcode, brand, size,
categoryPath, ingredients, allergens, nutrition, healthStarRating, dietaryTags, rating, previousPrice, lowestPrice30d,
averagePrice90d and discountCheck.verdict (genuine | questionable | insufficient-history: was the "was" price really
charged before the special?), plus scrapedAt and source (cache | live). Online price history since 2026-09-25; history fields are omitted from store-priced rows. Prices in AUD: the online price, or that store's own price on store rows. Results come from a catalogue refreshed nightly (maxAgeHours, default 36; 0 = always
live); runs return partial/cached data with warnings instead of failing. Cost: $0.50 per 1,000 products (Free plan)
down to $0.30, fetchFullDetails +$0.50 to $0.30 per 1,000, $0.00005 per run start. Small queries finish in seconds, so
the synchronous run-sync-get-dataset-items endpoint works; full-catalogue runs take about a minute.

Modes

ModeWhat you give itWhat you get
search (default)queries: keywordsmatching products, like the site's search
categorycategoryUrls: any woolworths.com.au/shop/browse/... URLthe products in that category
specialsoptional specialsGroupsevery current special (the full list, in seconds); Half Price first, then other promotions
productsproductIds: stockcodes (the number in a product URL)those exact products
barcodesbarcodes: EAN / GTIN numbers from the pack, e.g. 9310199012717the product behind each barcode; barcodes Woolworths doesn't sell are listed in the warnings
urlsurls: any Woolworths product, search, category or specials URL, or a stockcodewhatever each link shows; paste links from your browser
catalognothing (set maxItems high, e.g. 30000)the whole catalogue from the nightly snapshot; filters apply
categoriesnothingdepartments, categories and specials groups with their URLs
storesoptional storeIds / postcodesthe store directory: all ~1,140 Woolworths supermarkets with address, coordinates, phone and this week's opening hours
instoreexplicit productIds and exactly one storeIdmobile store price, promotion wording and native aisle/bay/floor/x/y/z; four-hour cache or live from our VPS

Mobile in-store details (instore)

This opt-in mode uses our VPS API, with a shared cache for each product + store. A reading less than four hours old is returned as source: "cache", retaining its original scrapedAt. Missing or expired readings are fetched live on the VPS through the existing ScrapeOps AU proxy and saved for subsequent requests. maxAgeHours: 0 forces live; positive values are capped at four hours regardless of the web default of 36 hours.

The Actor uses its existing private CACHE_API_URL and CACHE_API_TOKEN. It does not initialize an Apify residential proxy for this mode. Customers supply only product and store IDs; no app credentials belong in input.

The VPS operator configures legitimate headers and the existing installation's guest bootstrap in ~/.woolworths-mobile-bootstrap.json (mode 600), or private WOOLWORTHS_MOBILE_HEADERS_JSON and WOOLWORTHS_MOBILE_GUEST_JSON, plus the existing AU EXTERNAL_PROXY_URL. The file path can be overridden with WOOLWORTHS_MOBILE_BOOTSTRAP_FILE. Apply migration 033_woolworths_store_cache.sql before enabling the new API. The process shares a guest session, renews before expiry and retries once after HTTP 401; HTTP 403 does not cause a renewal loop. Ordinary TLS verification stays enabled. No device identity, token or sensor is fabricated.

Overlapping requests share live fetches; each upstream request contains at most 20 products in one store. A bounded queue prevents an unlimited backlog. Failed refreshes yield warnings and omit affected rows; expired shelf locations or online prices are never substituted. Revoked installation access and proxy outages still require operator attention. Nightly online collection and online price history remain separate. This cache stores the latest store observation, not a complete historical store-price series.

{"mode":"instore","productIds":["88436","248249"],"storeIds":["1416"],"maxItems":2}

Products are read in batches of at most 20 for that store. postcodes and allStores are unsupported in this mode. Only productIds, storeIds and maxItems select these rows; web filters, URLs/barcodes, history, fetchFullDetails and includeRaw do not apply. maxAgeHours controls freshness up to the four-hour store limit. No raw requests or responses are exported. The In-store mobile details dataset view displays the result. Mobile transfer bytes are not measured and are explicitly marked unknown in the summary.

Output includes productId, storeId, shoppingMode: "INSTORE", priceScope: "store", name, url, imageUrl, price in AUD, unitPriceDescription, wasPriceText, promotionInfo, multiBuyPriceInfo, inStoreAvailabilityInfo, inStoreLocation, scrapedAt and source (cache or live). Prices are converted from integer cents; source descriptions stay text. The availability button's label/style stays as published; no stock quantity or generic boolean is inferred from it.

inStoreLocation.details preserves aisleNumber, aisleSide, bayNumber, bayNumberAlternate, floorNumber, location and x/y/z; displayInfo preserves the source location label/type. Coordinate units are unconfirmed and z is not a shelf number. A missing location or price remains null. These rows are store-scoped observations from cache or live. Failed refreshes do not substitute an online price or an expired cached location. Partial GraphQL errors, mismatched product IDs and unsupported ProductTile responses are rejected. Member Price and price-family variants are not included.

Switch the dataset view to In-store mobile details. For example, a verified reading of product 88436 at store 1416 on 13 October 2026 returned the following fields (illustrative historical reading, not a guaranteed current price):

{
"productId": "88436",
"storeId": "1416",
"shoppingMode": "INSTORE",
"priceScope": "store",
"name": "Dairy Farmers Full Cream Milk 2L",
"price": 4.7,
"unitPriceDescription": "$2.35 per 1l",
"inStoreLocation": {
"details": {
"aisleNumber": 100, "aisleSide": "left", "bayNumber": 1,
"bayNumberAlternate": null, "floorNumber": 1, "location": "DAIRY",
"x": 3507.67, "y": 4834.62, "z": 97.19
},
"displayInfo": {"locationText": "Dairy", "locationType": "AVAILABLE"}
},
"inStoreAvailabilityInfo": {"button": {"label": "In Stock", "style": "INSTOCK"}},
"source": "live"
}

aisleNumber=100 is preserved exactly alongside the Dairy label; it is not translated into a conventional numbered aisle.

Ready-to-use examples

Paste any of these into the JSON tab of the input (or send them through the API).

1. Price check for a shopping list: the exact products you buy, by stockcode.

{ "mode": "products", "productIds": ["88436", "888140", "6073909"] }

2. Cheapest coffee per kg: a search sorted by unit price, within a price range.

{ "queries": ["coffee beans"], "sortBy": "unit_price_asc", "minPrice": 5, "maxPrice": 40, "maxItems": 50 }

3. Everything in a category, cheapest first:

{ "mode": "category", "categoryUrls": ["https://www.woolworths.com.au/shop/browse/dairy-eggs-fridge/milk"], "sortBy": "price_asc", "maxItems": 500 }

4. This week's half-price specials:

{ "mode": "specials", "specialsGroups": ["half-price"], "maxItems": 2000 }

5. New specials only, every Wednesday: schedule this weekly to get what just went on special.

{ "mode": "specials", "onlyNewSpecials": true, "maxItems": 2000 }

6. Price-drop alerts since your last run: schedule it daily; each run returns only products whose price changed.

{ "mode": "products", "productIds": ["88436", "888140", "6073909"], "changedSince": "last-run" }

7. Gluten-free snacks with less than 5 g sugar per 100 g:

{ "queries": ["snacks"], "dietary": ["Gluten Free"], "maxSugarPer100g": 5, "maxItems": 100 }

8. Allergy-safe: no peanuts or tree nuts, at least 4 health stars:

{ "queries": ["muesli bars"], "excludeAllergens": ["Peanut", "Tree Nut"], "minHealthStarRating": 4 }

9. The whole catalogue (analytics, BI, price comparison sites): served from the nightly snapshot in seconds.

{ "mode": "catalog", "maxItems": 30000 }

10. New products this week:

{ "mode": "catalog", "onlyNew": true, "maxItems": 30000 }

11. Online price history for a few products (up to 90 days of available observations):

{ "mode": "products", "productIds": ["88436", "888140"], "priceHistoryDays": 90 }

12. Links straight from the browser:

{ "mode": "urls", "urls": [
"https://www.woolworths.com.au/shop/productdetails/88436/dairy-farmers-full-cream-milk",
"https://www.woolworths.com.au/shop/search/products?searchTerm=tim%20tam",
"https://www.woolworths.com.au/shop/browse/specials/half-price"
] }

13. Everyday Market (third-party sellers) with seller and delivery terms:

{ "queries": ["kettle"], "includeMarketplace": true, "sortBy": "price_asc", "maxItems": 50 }

How to…

Scrape Woolworths half-price specials

Use example 4 (mode: specials, specialsGroups: ["half-price"]). Add minDiscountPercent: 40 to keep only big discounts (or sortBy: discount_desc with a high maxItems to rank them).

Get this week's Woolworths catalogue as data

The weekly catalogue changes on Wednesday. { "mode": "specials", "maxItems": 10000 } returns every product on special right now, with shelf price, was price, special type and unit price; schedule it every Wednesday morning (Sydney time), or use example 5 to get only what went on special this week. This is the catalogue's products as data, not the printed PDF.

Check if a Woolworths special is a real discount

Every product on special has discountCheck.verdict: genuine means the "was" price was really charged for at least 28 of the 90 days before the special; questionable means it wasn't (for example, the price went up just before the "discount"). The verdict is given once a product has 28 days of price history (see Online price history).

Look up Woolworths products by barcode

Use mode: barcodes with the EAN/GTIN numbers from the packs. Each barcode returns its exact product (with the stockcode, so later runs can use mode: products); barcodes Woolworths doesn't sell are listed in the run warnings.

{ "mode": "barcodes", "barcodes": ["9310199012717", "9300633556150"] }

Get Woolworths prices per kg or per litre

Every product has pricePerKg / pricePerLitre (or pricePerItem), normalised from Woolworths' unit prices. Sort a search by unit price with sortBy: unit_price_asc (example 2).

Track a Woolworths product's price over time

Schedule example 6 (changedSince: "last-run") to get only price changes, or add priceHistoryDays: 90 for each product's price history (example 11). See Price alerts in 5 minutes.

Check Woolworths stock in any store

Add postcodes (every Woolworths in that postcode) or storeIds (StoreNo from mode: stores) to a web product mode (example 16). Each product then comes once per store with storeId, storeName, that store's own price and inStock = in-store availability at that store: true (available), false (not available) or null (the store didn't report).

Get every Woolworths store location and opening hours

mode: stores (example 17) returns all ~1,140 supermarkets with address, coordinates, phone and this week's opening hours. Filter it with postcodes or storeIds.

Export the whole Woolworths catalogue

mode: catalog with maxItems: 30000 (example 9) returns every product from the nightly snapshot in about a minute.

14. Newest products in a category first:

{ "mode": "category", "categoryUrls": ["https://www.woolworths.com.au/shop/browse/snacks-confectionery"], "sortBy": "newest", "maxItems": 100 }

15. Daily watch of your products, including ones that were delisted:

{ "mode": "products", "productIds": ["88436", "888140", "6073909"], "changedSince": "last-run", "includeDisappeared": true }

16. Is it on the shelf at the Woolworths stores in my postcode? (in-store availability):

{ "mode": "products", "productIds": ["84628", "36066"], "postcodes": ["3150", "6150", "7000"] }

17. Every Woolworths supermarket with opening hours:

{ "mode": "stores", "maxItems": 2000 }

18. Mobile price and aisle location in Double Bay (four-hour cache or live):

{ "mode": "instore", "productIds": ["88436", "248249"], "storeIds": ["1416"], "maxItems": 2 }

Input options

Filters and sorting (web product modes; not instore):

  • sortBy: relevance, price_asc, price_desc, unit_price_asc, name, specials_first, newest, discount_desc (discount_desc re-sorts the collected results by % off; combine it with specialsOnly and a high maxItems). Sorting is done by Woolworths before paging, so you get the true cheapest products, not just a re-ordered first page.
  • specialsOnly, inStockOnly, minPrice / maxPrice, minRating (customer stars), excludeCategories (department names or slugs), brands (exact brand names), minDiscountPercent (e.g. 40 = only specials at least 40% below the was-price).
  • includeMarketplace: off by default, so only products sold by Woolworths are returned (third-party Everyday Market listings are excluded at the source). When it is on, marketplace items include seller (name, dispatch time, delivery days and fee, returns) and mpn.
  • specialsGroups (specials mode): half-price, buy-more-save-more, lower-shelf-price, seasonal-price, bundles, everyday-low-price.

Health and nutrition (web product modes): dietary (e.g. ["Gluten Free", "Vegan"], all required), excludeAllergens (e.g. ["Peanut", "Milk"]), minHealthStarRating, maxSugarPer100g, minProteinPer100g. Products without the data a filter needs are left out, so you never get a product we can't confirm.

Price history (web product modes, from our nightly online price observations):

  • changedSince: only products whose price changed since a date (2026-09-01) or since your previous run (last-run).
  • includeDisappeared (with changedSince: "last-run"): also returns rows with type: "disappeared" for products your previous run returned that are gone now (delisted, or no longer matching your filters), with their last price and when they were last seen.
  • onlyNewSpecials: only products that went on special in the last 7 days (Woolworths specials change on Wednesdays).
  • onlyNew: only products that appeared at Woolworths in the last 7 days.
  • priceHistoryDays: add the list of price changes from the last N days.

Stores (web modes except categories; instore uses the separate rules above):

  • storeIds: Woolworths store numbers (StoreNo, from mode: stores), postcodes: every supermarket in each postcode (a postcode without a Woolworths gives no rows; the warnings name its nearest store and distance, to add to storeIds), allStores: every supermarket. Each product becomes one row per store with that store's in-store availability (inStock, null when Woolworths doesn't report it). maxItems counts products, so rows = products x stores.
  • Prices on store rows are that store's own prices (priceScope: "store"), read live: one request per product and store. A product the store doesn't sell has price: null. Price history fields (previousPrice, priceHistory, ...) describe the online price, so store-priced rows leave them out.
  • Availability is always live: about one request per product and store (nearby stores share answers).
  • inStockOnly refers to Woolworths' default online store, not to the store rows.
  • Each store row is charged as one product: 100 products x 3 postcodes = 300 rows; with allStores, 100 products = ~114,000 rows.

Other options:

  • maxItems: products per query / category / URL, after filters (default 100). For specials, products and catalog it applies per run.
  • maxAgeHours: how old our web data may be (default 36; anything read before this week's specials change on Wednesday is fetched live; 0 = always fetch live). Mobile instore caps positive values at 4 hours; 0 forces live through our VPS.
  • fetchFullDetails: add country of origin, the nutrition panel exactly as printed and all images. It takes one extra request per product and is charged per product.
  • includeRaw: add the original Woolworths payload (every attribute, including ones we don't map).

Example output

{
"store": "woolworths",
"productId": "88436",
"barcode": "9310199012717",
"name": "Dairy Farmers Full Cream Milk 2L",
"brand": "Dairy Farmers",
"size": "2L",
"url": "https://www.woolworths.com.au/shop/productdetails/88436/dairy-farmers-full-cream-milk-full-cream-milk",
"price": 4.7,
"wasPrice": 4.7,
"unitPrice": 2.35,
"unitMeasure": "1L",
"pricePerKg": null,
"pricePerLitre": 2.35,
"isOnSpecial": false,
"specialType": null,
"inStock": true,
"categoryPath": ["Dairy, Eggs & Fridge", "Milk", "Full Cream Milk"],
"description": "Dairy Farmers Full Cream Milk 2L is Australian owned, made and loved. ...",
"ingredients": "Milk.",
"allergens": { "contains": ["Milk"], "mayContain": [] },
"nutrition": {
"servingSize": "250.0 ML",
"servingsPerPack": "8.0",
"items": [{ "name": "Energy", "per100": "259.0kJ", "perServing": "648.0kJ" }, { "name": "Protein", "per100": "3.4g", "perServing": "8.5g" }]
},
"healthStarRating": 4,
"dietaryTags": ["Gluten Free", "Low Salt", "Source of Protein", "Vegetarian"],
"countryOfOrigin": "Australia",
"images": ["https://cdn0.woolworths.media/content/wowproductimages/large/088436.jpg", "..."],
"rating": { "average": 4.6, "count": 132 },
"isMarketplace": false,
"previousPrice": 4.95,
"priceChangedAt": "2026-10-14T15:31:02.000Z",
"firstSeenAt": "2026-09-26T15:05:01.000Z",
"lowestPrice30d": 4.7,
"averagePrice90d": 4.86,
"specialStartedAt": null,
"discountCheck": null,
"specialCycle": null,
"scrapedAt": "2026-10-20T15:45:14.223Z",
"source": "cache"
}

Output fields (web modes; mobile instore fields are listed above)

GroupFields
Identitystore, productId (stockcode), barcode (EAN), mpn, name, brand, size, url, imageUrl, images, categoryPath
Priceprice, wasPrice, unitPrice + unitMeasure, pricePerKg, pricePerLitre, pricePerItem, inStock
SpecialsisOnSpecial, specialType, offerDescription (multi-buy offer, e.g. "2 for $5.00"), specialStartedAt, discountCheck, specialCycle
HistorypreviousPrice, priceChangedAt, lowestPrice30d, averagePrice90d, firstSeenAt, priceHistory (with priceHistoryDays)
Product detailsdescription, ingredients, allergens, nutrition, healthStarRating, dietaryTags, storage, manufacturer, countryOfOrigin*
Reviews & sellerrating, isMarketplace, seller (marketplace only)
Store rowsstoreId, storeName, storeSuburb, storeState, storePostcode, priceScope (store = that store's own price); only with storeIds / postcodes / allStores
FreshnessscrapedAt, source (cache or live)
Store directory (mode: stores)type (store), site, storeId, name, brand, address, suburb, state, postcode, latitude, longitude, phone, tradingHours (the coming week, days + hours), scrapedAt

* countryOfOrigin needs fetchFullDetails. All prices are in AUD, as numbers. Detail fields are null (or empty lists) when Woolworths has no data for a product.

Deal check for specials: discountCheck.verdict is:

  • genuine when the "was" price was actually charged for at least 28 of the 90 days before the special;
  • questionable when it was not (for example, when the price was raised shortly before the "discount");
  • insufficient-history when the product has less than 28 days of price history.

specialCycle shows how often a product goes on special and when the next one is expected, once we have seen 3 of its specials.

Price alerts in 5 minutes

  1. Save a task with the input you want to watch (examples 5 and 6 above are good starting points).
  2. Add a schedule (for example every Wednesday morning, when specials change, or daily for price drops).
  3. In the task's Integrations tab, send results to email, Slack, Google Sheets, Zapier, Make, n8n or a webhook.

With changedSince: "last-run", each run returns only what changed since the previous one, so quiet days cost nothing.

Run it through the API

cURL (runs synchronously and returns the items):

curl -X POST "https://api.apify.com/v2/acts/crawlplant~woolworths-au/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"queries": ["milk"], "maxItems": 20}'

JavaScript (npm install apify-client):

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('crawlplant/woolworths-au').call({ mode: 'specials', specialsGroups: ['half-price'], maxItems: 200 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((p) => `${p.name}: $${p.price} (was $${p.wasPrice})`));

Python (pip install apify-client):

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("crawlplant/woolworths-au").call(run_input={"queries": ["coffee"], "sortBy": "unit_price_asc", "maxItems": 20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["name"], item["pricePerKg"])

Use with AI agents

The Actor works as a tool in the Apify MCP server: Claude, ChatGPT, Cursor, VS Code, LangChain, CrewAI, OpenAI Agents and others. Small queries are answered from the cache in seconds, and every output field is described, so agents understand the data without extra prompting.

To give an assistant only this tool, use this server URL:

https://mcp.apify.com?tools=crawlplant/woolworths-au

Example for Claude Desktop (claude_desktop_config.json):

{ "mcpServers": { "woolworths": { "command": "npx", "args": ["mcp-remote", "https://mcp.apify.com?tools=crawlplant/woolworths-au"] } } }

Try prompts like "find the cheapest gluten-free pasta per kg at Woolworths", "which Woolworths half-price specials are genuine this week?" or "what did milk cost at Woolworths over the last 90 days?".

Use it when you need Woolworths prices, specials and price history (including whether a special is a real discount), product details such as barcodes, allergens and nutrition, or each store's own price and in-store availability, for a few products or the whole catalogue.

Not for Coles products (use the Coles Australia Scraper) or the printed catalogue PDF: the weekly catalogue comes as product rows.

How much does it cost to scrape Woolworths?

Pay per result: you pay for dataset rows returned. The table below uses USD for Actor fees; product prices are AUD. Web rates stay unchanged. Mobile instore uses the base Product fee plus the store-data add-on below, with the same total for cache and live readings. The mobile total is not charged again as a third event. Runtime platform usage is included; customers need no proxy configuration. The add-on is charged only once it is active in the run’s Apify pricing. Until then, mobile rows keep the existing base rate. Check the Pricing tab for the effective date of any scheduled price change. Apify may charge standard storage access after the run; see the Actor's Pricing tab for the active billing configuration.

EventNo discountBronzeSilverGold / Platinum / Diamond
Product (per 1,000 results)$0.50$0.40$0.35$0.30
Store-data add-on (per 1,000 mobile rows, when active)$1.00$0.80$0.70$0.60
Mobile total: base + add-on (per 1,000 rows)$1.50$1.20$1.05$0.90
Full product details (per 1,000, only with fetchFullDetails)$0.50$0.40$0.35$0.30
Actor start (per run)$0.00005$0.00005$0.00005$0.00005

Product details, online price history and deal checks are included in the product price. A failed or empty run costs only the start fee. Store rows (one per product and store) and store directory rows count as results too. instore returns one row per successful product/store reading and does not trigger the product-details add-on. Missing rows and failed attempts incur no result fee. The Actor checks the combined base + store-data fee against your run budget before writing a row; a budget limit returns affordable fresh rows with a warning. For the default 512 MB run, the start fee is $0.00005; larger memory can increase the number of start events.

Example at the no-discount rateResultsResult fee (plus start)
Default run: search milk100$0.05
All of this week's specials~5,000~$2.50
Whole catalogue~25,000-30,000$12.50-15.00
Store directory~1,140$0.57
10 products in the stores of 3 postcodes~30-60$0.02-0.03
Daily watch with changedSince: "last-run"only the changesoften $0.00
Mobile in-store example, 2 products in 1 store2$0.001

Set a maximum cost per run in the run options to stop a large run at your budget.

Reliability

  • Most queries are answered from our nightly catalogue in seconds, so a run doesn't depend on Woolworths answering at that moment.
  • If a live request doesn't come through, the run still finishes with the latest data and a note in its summary. An empty run costs only the start fee.
  • Automated checks test the Actor, the data and its freshness every 3 hours, around the clock.

Mobile instore uses current store cache first, then live on our VPS. Store data collection has one 30-second budget across batches, queueing, session renewal and live reads; on expiry it returns available fresh records with a warning. Session or transport failures produce warnings and omit affected rows when no current reading exists. Expired store readings and online prices are never substituted.

Online price history

We have tracked Woolworths online catalogue prices since 25 September 2026, and each night records the changes we observe. This does not capture every intraday price move. Online product rows carry previousPrice, priceChangedAt, lowestPrice30d and averagePrice90d; priceHistoryDays adds the full list of changes. discountCheck rates a special once the product has 28 days of history, and onlyNew / onlyNewSpecials look at the last 7 days. Store-mode prices and aisle locations do not carry online price history.

Data freshness

Every item has scrapedAt (when the price was read from Woolworths) and source:

  • cache: from our database, no older than your maxAgeHours. The online catalogue is refreshed every night (Sydney time); mobile store readings are refreshed on demand with a four-hour maximum cache age.
  • live: fetched from Woolworths during your run.

If a live request doesn't come through, the run still finishes with the latest cached data and a note in the run's OUTPUT record. This stale-data fallback applies to web modes. Mobile instore returns cache younger than four hours (or your lower maxAgeHours) and otherwise fetches live on the VPS; failures never extend its TTL.

Troubleshooting

  • Fewer results than expected: filters (price, dietary, allergens, ratings) leave out products that don't match or have no data. The OUTPUT record in the run's key-value store lists any warnings, e.g. when up to 30 pages were scanned without enough matches.
  • "category not found": run mode categories to get the exact category URLs, then paste one into categoryUrls.
  • previousPrice is null: we have not seen that product's price change since we started tracking it.
  • Results differ slightly between two identical runs: Woolworths' relevance order shifts between requests. Sort by name or a price for exact, repeatable results.
  • A stockcode isn't returned: the product no longer exists; it is listed in the run warnings and not charged.
  • Mobile API unavailable: instore requires the operator-configured VPS endpoint. Check OUTPUT.warnings; operators should verify the store-cache migration, private guest bootstrap and ScrapeOps AU connection on the VPS. Secrets belong in private environment configuration, never JSON input.
  • No mobile aisle location or price: source fields may be null. A failed request yields no row; check OUTPUT.warnings.
  • No Member Price or variants: these fields are not provided by the implemented mobile mode.

More Australian data

  • Coles Australia Scraper: Coles prices, specials, price history and the prices and stock of any of ~878 stores; same output format, match products by barcode.
  • Australian Bank Rates: every interest rate published by 240+ Australian banks and lenders under the Consumer Data Right, from term deposits to home loans, with the best rate per bank and a daily rate-change feed.
  • Australian Energy Plans: every electricity and gas plan published by 40 retailers under the Consumer Data Right, for any postcode, with solar feed-in tariffs, an estimated annual bill and a daily plan-change feed.

FAQ

Can I check stock in my local Woolworths?

Yes. Add postcodes, storeIds or allStores and every product comes once per store with that store's in-store availability and that store's own price (priceScope: "store").

How much does it cost to scrape Woolworths?

$0.50 per 1,000 products on the Free plan, down to $0.30 on higher plans, plus $0.00005 per run. The default run costs $0.05 and the whole catalogue about $12.50-15.00. See the price table.

Is a Woolworths special a real discount?

Check discountCheck.verdict on the product: genuine or questionable, based on what the product really cost before the special. See Check if a Woolworths special is a real discount.

When do Woolworths specials change?

Every Wednesday. Schedule a specials run on Wednesday morning (Sydney time) to track each new week.

Why are some products missing price?

The product is currently unavailable online (on a store row: that store doesn't sell it).

How is this different from other Woolworths scrapers?

Runs finish reliably, results are served from a nightly snapshot in seconds, and every product carries price history, a genuine-discount check, normalised unit prices and parsed nutrition. There is also a full-catalogue mode, barcode lookup and a store directory. The opt-in mobile mode additionally preserves native aisle, bay and floor data when the source returns it.

Does it collect account data?

The output contains product, price and store information. Mobile mode uses operator-supplied private session credentials to make requests but does not export those credentials or account profiles. The tool is independent of Woolworths Group.

Questions, a missing field or a source you need? Open an issue on this Actor: we reply within a day. If it saved you time, a short review helps others find it.

Good to know

  • With filters, up to 30 result pages (~1,000 products) are scanned per query or category; the run says so when that cap is hit.
  • Relevance order on Woolworths shifts slightly between requests, so very large relevance-sorted searches can differ by a few products between runs. Sort by name or price for exact, repeatable results.

Run summary

The key-value store record OUTPUT shows items returned (cache vs live), invalid records skipped, request statistics, extra events charged and any warnings. For instore, proxy.mobile additionally separates transport/client failures, non-200 HTTP responses and rejected HTTP-200 JSON/GraphQL/product responses. It counts validated product rows, rows with a price and rows with native location, and records the last validated reading time. Upstream request counters come from the VPS; cache hits need no upstream request. Transfer remains unmeasured. Identifier-free numeric mobile metrics are also sent through the existing usage telemetry on configured platform runs, including configuration failures before requests.

Privacy

The Actor returns product and store data. Mobile session credentials are used only for requests and are excluded from dataset output and HTTP error logs. Keep them in private environment configuration rather than Actor input. To decide which features to build next, each run sends the developer usage statistics: the mode, which options were used (e.g. "sortBy: price_asc", "specialsOnly: on", "2 queries"), result counts and duration. It never sends your search terms, URLs, product ids, results or any account details. Your Apify account id is replaced on arrival by a one-way keyed hash (the id itself is never stored), used only to count how many different accounts use the Actor.