Woolworths Australia Grocery Prices Scraper
Pricing
from $2.00 / 1,000 results
Woolworths Australia Grocery Prices Scraper
Woolworths Australia product prices, was-prices, unit prices, specials, stock and GTIN from the public website. Woolworths only.
Pricing
from $2.00 / 1,000 results
Rating
0.0
(0)
Developer
dj orona
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
17 days ago
Last modified
Categories
Share
Returns grocery product records from the public Woolworths Australia website. Each record has the name, brand, size, price, was-price, unit price, special and in-stock flags, the barcode (GTIN) where Woolworths shows it, the product URL and the image URL. It's for developers and AI agents doing Australian grocery price tracking, basket costing and promotion monitoring.
Independent tool, not affiliated with or endorsed by Woolworths Group.
Limits:
- Woolworths only. Coles isn't supported. If that changes, this page will say so.
- Prices are what Woolworths' default online store/region shows a logged-out visitor at scrape time. They may differ from your local store or from what you see when logged in. You can't choose a store.
- If Woolworths blocks the request or its robots.txt disallows a path we use, the run stops with a clear status instead of trying to get around it (see "How this Actor behaves").
- Each run is a snapshot. There's no price history.
What you can do with it
- Track the prices of a list of products over time by scheduling runs.
- Cost a shopping basket at Woolworths online prices.
- See which products are on special right now, and by how much (
pricevswasPrice).
Input
Give at least one of searchTerms or productUrls.
| Field | Type | Default | What it does |
|---|---|---|---|
searchTerms | string[] | none (prefill ["milk 2L"]) | Keywords such as milk 2L or weet-bix, up to 100 characters each. Each term returns matching products (24 per page, paged automatically) until maxItems is reached. |
productUrls | string[] | none | Woolworths product URLs (https://www.woolworths.com.au/shop/productdetails/<id>/<slug>) or bare numeric product IDs such as 888137. Query strings are ignored. These run before search terms. |
maxItems | integer | 100 (min 1, max 5000) | Maximum number of products returned across all inputs. This also caps your cost. |
chains | string[] | ["woolworths"] | Only ["woolworths"] is accepted. Anything else gives INVALID_INPUT. |
includeRunReport | boolean | false | For testing. Puts one extra recordType: "run-report" row first in the dataset. That row counts as one charged result, and it's written even when the run ends FAILED, BLOCKED or ROBOTS_BLOCKED. The same report is always saved free as RUN_SUMMARY in the key-value store. Status rows are not charged. |
A product already returned by an earlier input isn't repeated in the same run. Blank entries are ignored. There's no proxy setting and no concurrency setting: the Actor runs from Apify's servers without a proxy and makes one request at a time.
Example input:
{ "searchTerms": ["milk 2L", "weet-bix"], "productUrls": ["888137"], "maxItems": 60 }
Output
One dataset item per product, with exactly these 15 fields. Prices are in AUD. Timestamps are ISO 8601 UTC. A field the site doesn't provide is null, never missing. chain, productId, name, onSpecial, url and scrapedAt are always filled.
| Field | Type | Meaning |
|---|---|---|
chain | string | Always woolworths in this version |
productId | string | Woolworths' product ID (Stockcode) |
gtin | string | null | Barcode (GTIN/EAN, 8–14 digits) when Woolworths provides it, otherwise null. In one sample run on 2026-10-03, 50 of 59 search results had a GTIN. Coverage varies by product. |
name | string | Product name as shown |
brand | string | null | Brand |
size | string | null | Pack size as shown, e.g. 2L, 500g |
price | number | null | Current price in AUD. Null if Woolworths shows no price (or a zero price). |
wasPrice | number | null | Previous price in AUD. Set only when the item is on special (and higher than price), so it's null for most items. |
unitPrice | number | null | Price per unitMeasure in AUD |
unitMeasure | string | null | Unit for unitPrice, e.g. 1L, 100g, 1EA |
onSpecial | boolean | True if Woolworths marks the product as on special |
inStock | boolean | null | Online availability flag as shown, or null if unknown |
url | string | Product page URL (the source), no query string |
image | string | null | Woolworths' product image URL (linked, not copied or re-hosted) |
scrapedAt | string | ISO 8601 UTC time the item was collected |
Real example item from a test run on 2026-10-03 (image and scrapedAt left out of this excerpt):
{"chain": "woolworths","productId": "44528","gtin": "9322042000048","name": "Norco Full Cream Milk","brand": "Norco","size": "2L","price": 4.5,"wasPrice": 5.05,"unitPrice": 2.25,"unitMeasure": "1L","onSpecial": true,"inStock": true,"url": "https://www.woolworths.com.au/shop/productdetails/44528/norco-full-cream-milk"}
A full run report (each input's status, item counts, null counts and errors) is always saved as RUN_SUMMARY in the run's key-value store.
Run statuses
The Actor's status message starts with one of these. RUN_SUMMARY.status in the key-value store holds the same value.
| Status | Exit | Meaning |
|---|---|---|
SUCCEEDED | ok | Every input was processed. Some search terms may have had no results (empty). |
PARTIAL | ok | Some products came back, but at least one input was invalid, not found or errored. See RUN_SUMMARY.cases. |
FAILED | fail | No products, and at least one input failed or an unexpected error happened. |
INVALID_INPUT | fail | Something's wrong with the input. The message names the field. No requests are made. |
ROBOTS_BLOCKED | fail | robots.txt didn't return 200, couldn't be parsed, or now disallows an endpoint we use. No product requests are made. |
BLOCKED | fail | Woolworths returned 403/401/429 or a challenge page 3 times (2 fresh-session retries at least 30 s apart). Products already saved stay in the dataset. |
Per-input statuses in RUN_SUMMARY.cases: ok, empty, partial, error, not_found, invalid_input, skipped_robots, skipped_max_items, blocked, not_run. These statuses are recorded only in RUN_SUMMARY (and in the optional run-report row). They are never written as separate dataset items. An unknown product ID returns not_found with a message. A non-Woolworths URL returns invalid_input with a message. Neither one produces a dataset item. If product lookups fail only because of network errors, the message suggests using searchTerms instead.
Pricing
- $2.00 per 1,000 products returned ($0.002 each). No start fee.
- A result is one product item in the dataset. Status rows are not charged: failed, blocked, not-found and invalid inputs produce no dataset item, so they cost nothing.
- The one exception: with
includeRunReport: true, the report row is a dataset item and counts as one result ($0.002). It's written even when the run endsFAILED,BLOCKEDorROBOTS_BLOCKED, so such a run costs $0.002 plus any products already saved. It isn't written forINVALID_INPUT. - Examples: a default run (up to 100 products) costs at most $0.20. A 20-product run costs $0.04.
maxItemscaps the cost of any run.
Reliability
Release test: build woolworths-v1.3, 2026-10-03, 6 test cases × 20 runs each, every case passed at least 19 of 20 runs. This is a short test window, not a long-term uptime figure.
After release we run a daily check. Please report problems on the Issues tab.
How this Actor behaves (compliance and politeness)
- Public data only. Logged-out GET requests to Woolworths' public search API, the public product page and, as a last resort, the public product JSON endpoint. No login, account, cart, checkout or store/region selection.
- Challenges and blocks stop the run. On a 403 "Access Denied", 401, 429, CAPTCHA or bot-check page, the request is never solved, clicked through or bypassed. The Actor retries at most twice, each time on a fresh session after at least 30 s (60 s for the second retry). It honours Retry-After up to 300 s. After that the run ends with
BLOCKED. This applies to every endpoint. - No cookies, no tokens. The Actor stores, sends and imports no cookies. No CAPTCHA solvers, unblocking APIs or stealth plugins.
- Headers. Standard generic browser-style headers, kept the same for the whole session. A new set is generated only for the fresh-session retries above. No Referer or cookie spoofing.
- No proxy. Runs directly from Apify's servers. No proxy rotation or fallback after a block.
- Rate limits. One request at a time, at least 2.5 s apart, capped by
maxItems. Server or network errors get at most two short retries before the next method is tried. - robots.txt. Read first on every run, and the run stops if it can't be read. Every request path is checked against it right before sending. Redirects are never followed automatically. The site-search page
/shop/searchis never used. - Product data only. Only the 15 fields above. No ratings, reviews, reviewer or customer data.
FAQ
Is Coles supported? No, only Woolworths.
Which store's prices do I get? Woolworths' default online store/region for a logged-out visitor. You can't pick a store in this version.
Why is wasPrice usually null? Woolworths only shows a previous price when an item is on special. Check onSpecial.
Why is gtin sometimes null? Woolworths doesn't provide a barcode for every product. In one sample run on 2026-10-03, 9 of 59 search results had none. Coverage varies by product.
Can I match products with other retailers? The Actor doesn't do that. gtin is included when available, so you can use it for your own matching.
My run ended with BLOCKED or ROBOTS_BLOCKED. Why? Woolworths refused the requests, or its robots.txt no longer allows a path we use. We stop instead of working around it. Products already returned stay in the dataset and are charged as normal. Nothing else is charged, except the run-report row if you turned on includeRunReport.
Historical prices? No. Each run is a snapshot. Schedule runs and keep the datasets.
Is this affiliated with Woolworths? No. It's an independent tool, not affiliated with or endorsed by Woolworths Group. It reads the public website.
Changelog
- woolworths-v1.3.1 (2026-10-03): documentation only (version tag woolworths-v1.3.1, docs). The Reliability section now cites the woolworths-v1.3 release test. No code change; runs still report code version woolworths-v1.3.
- woolworths-v1.3 (2026-10-03): an empty input runs a default search instead of failing (see "Empty input" below).
- First release (2026-10-03): Woolworths only. Search terms, product URLs and bare product IDs.
Builder and compliance notes (lookup order, field mapping, conditions-to-code map): docs/BUILDER_NOTES.md.
Empty input (woolworths-v1.3, 2026-10-03)
If you give neither searchTerms nor productUrls (for example an empty {} input), the run no longer fails with INVALID_INPUT. It searches for milk 2L and returns at most 10 products (or your own maxItems if you set one), which costs at most $0.02 at 10 products. The status message says the default search was used. Any input with search terms or product URLs works exactly as before.