Woolworths & Aldi Price Scraper and Comparison
Pricing
from $1.50 / 1,000 product scrapeds
Woolworths & Aldi Price Scraper and Comparison
Woolworths and Aldi Australia prices in one table: price, was-price, unit price, specials, multibuys, stock, barcode, nutrition and Special Buy dates. Search terms, category or product links. Optional: match the same product at both stores, with a confidence score. No login.
Pricing
from $1.50 / 1,000 product scrapeds
Rating
0.0
(0)
Developer
CHEN-SHU-CHEN
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
What is Woolworths & Aldi Price Scraper and Comparison?
You want to know what a basket costs at Woolworths and at Aldi this week, which items are on special, or how a brand's products sit on the shelf. Checking two websites by hand means copying prices, sizes and unit prices product by product, and the two stores write sizes and unit prices differently.
Type search terms, paste category or product links, or both, and get one row per product from Woolworths and Aldi Australia in the same columns: price, was-price and savings, unit price (also converted to per kg, per litre or per item, so the stores compare directly), special type (half price, multibuy, price drop, member and Rewards offers), in stock, barcode, category, all images, and for Woolworths the ingredients, allergens, Health Star Rating and nutrition that come with every search result. No login is needed.
Turn on compare stores and the Actor also pairs the same product at both stores (same brand, same pack size, matching name) and writes one comparison row per pair: which store is cheaper, by how much, and by unit price, with a match confidence score so you can see why two products were paired.
Because it runs on Apify, you can schedule a weekly price check, call it through the API, connect it to Make, Zapier or Google Sheets, and export as CSV, Excel or JSON.
Coles is not supported. Its site blocks automated reads, so this Actor covers Woolworths and Aldi only, and says so rather than returning half-empty Coles rows.
How to scrape Woolworths and Aldi prices
- Open the Input tab and add Search terms, one per line:
cadbury dairy milk,coca cola 2l,nappies. Each term is searched at both stores unless you pick one. - Optionally paste Category links (
https://www.woolworths.com.au/shop/browse/specials/half-price,https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160) or Product links or codes (https://www.woolworths.com.au/shop/productdetails/44528/..., a stockcode such as44528, or an 18-digit Aldi SKU). - Set Max products per search term or category. 50 is the default.
- Turn on what you need: Specials only, Add Aldi Special Buys, Include product page details, Compare the same product at both stores.
- Click Start. A search term with 50 products per store finishes in a few seconds.
- Open the Output tab. The Products view lists every product, Woolworths vs Aldi the comparison rows, and Inputs and errors anything that returned nothing.
What data does it return?
Every product row has the same columns for both stores. A column a store does not publish is null, never 0.
| Field | Notes |
|---|---|
store, productId, stockcode / sku, url | Woolworths stockcode or Aldi SKU |
barcode, gtinFormat | Woolworths only. Aldi publishes no barcodes |
name, displayName, brand, variety, size | |
sizeAmount, sizeUnit, packCount | The size normalised: 6 x 375mL is 2250, ml, 6 |
price, wasPrice, savings, isOnSpecial, isHalfPrice | wasPrice only when it is above the price |
specialTypes | half_price, multibuy, member_price, rewards_offer, prices_dropped, lower_price_always, special; for Aldi special_buy, lower_prices, super_savers, limited_time_only |
promotionTexts | The store's own tag text: "Save $18.00", "2 for $10.00", "Was $3.30 - 25/08/2026" |
multibuyQuantity, multibuyPrice, multibuyUnitPrice | "2 for $10" is 2, 10.0 |
wasPriceHistory | Woolworths "Prices Dropped" tags parsed: {price: 3.3, date: "2026-08-25", isRange: false} |
unitPrice, unitPriceMeasure, unitPriceText | As the store shows it: 2.7, 100G, $2.70 / 100G |
unitPriceStandard, unitPriceStandardUnit | Per kg, L or each, the same basis for both stores |
instorePrice, instoreWasPrice, instoreIsOnSpecial | Woolworths' in-store price where it differs |
inStock, isAvailable, isPurchasable, supplyLimit, stockMessage | Woolworths. Aldi publishes no stock, so inStock is null there |
isForDelivery, isForCollection, isForExpress, nextAvailabilityDate | Woolworths delivery options |
isSponsored, adStatus, searchRank, organicRank, inputTotalResults | Position in the store's results for your term, with and without sponsored tiles: share of shelf for a brand |
isMarketplace, vendor | Woolworths Everyday Market (third-party sellers) |
categoryPath, departments, categories, subcategories, categoryIds, sap* | |
ingredients, allergensContains, allergensMayContain, allergyStatement, dietaryClaims, healthStarRating, nutrition, description, storageInstructions | Woolworths, from the search result itself |
imageUrl, images, imageUrlMedium, imageUrlSmall, imageDetails | All product images; Aldi at 1500 px |
isSpecialBuy, specialBuyOnSaleDate, specialBuyOnSaleText, isUpcomingSpecialBuy, specialBuyWeek | Aldi Special Buys |
averageRating, ratingCount, reviewCount, ratingBreakdown | null when the store shows no ratings |
fulfilmentStoreId | The Woolworths store the price comes from (see below) |
input, inputType, inputIndex, status, scrapedAt | Which input found the product |
With Include product page details (one extra request per product):
| Field | Store |
|---|---|
nutritionPanel (per serve and per 100 g/mL), servingSize, servingsPerPack | Woolworths |
countryOfOrigin, australianIngredientsPercent, countryOfOriginLabel | Woolworths: "Australian", 100 |
aisle, aisleDepartment, detailImages, videoUrl, directions, warnings | Woolworths |
description, ingredients, allergensContains, allergensMayContain, storageInstructions, directions | Aldi. Aldi fills ingredients and storage on about a third of its products |
detailsStatus says whether that worked for each row.
Comparison rows (rowType: "comparison"): cheapestStore (woolworths, aldi or same), priceDifference, priceDifferencePercent, cheapestByUnitPrice, unitPriceDifference, unitPriceUnit, matchMethod, matchConfidence, nameSimilarity, and each side's id, name, brand, size, price, was-price, unit price, link and image.
Input rows (rowType: "input"): one for each input that produced no product rows, with status (no_results, not_found, invalid_input, blocked, parse_error, partial, not_processed) and error. The outcome of every input, including the successful ones, is also in the key-value store under INPUTS.
Unreadable products: if a store sends a product without an id, name, link or price, or in a shape the parser cannot read, it becomes a free row with status: "parse_error" and the reason in error, and the rest of the input carries on.
How does the Woolworths vs Aldi comparison work?
Aldi publishes no barcodes, so products are matched on what both stores do publish:
- Brand must be the same after tidying case and punctuation:
Cadbury=CADBURY. - Pack size must be the same total amount within 2%:
2L=2 l,1kg=1000 g. A six-pack never matches a single bottle, even at the same volume. - The rest of the name is compared once brand and size are removed, by shared words and character similarity.
Brand and size are gates, not points. A pair that fails either is never matched. Passing both gives 0.6, and the name adds up to 0.4, so matchConfidence runs from 0.6 to 1. Only pairs at or above Minimum match confidence (0.85 by default) are written, and each product is used at most once, best match first.
In practice this pairs the branded products both stores stock (Cadbury, Coca-Cola, Norco), which is where a like-for-like price comparison is honest. Aldi's own brands have no Woolworths twin, so they are not paired with a Woolworths product that is only similar. If you want looser pairs, lower the threshold and check nameSimilarity.
Only products read in the same run are compared, so search the same terms at both stores. A product found by two terms is written once.
How much does it cost to scrape Woolworths and Aldi?
You pay per row, not per minute.
| Event | Charged for | Price | Per 1,000 |
|---|---|---|---|
product-scraped | One product row, from either store | $0.0015 | $1.50 |
product-details | The product page added to that row (Include product page details) | $0.0005 | $0.50 |
comparison-row | One product matched at both stores | $0.005 | $5.00 |
Free, never charged: input rows (no_results, not_found, invalid_input, blocked, parse_error, not_processed), unreadable products (parse_error), a product found again by a second term, and product page details when the input was itself a product link (that page is the request).
Examples: 50 products from each store for 10 terms is 1,000 rows, $1.50. With details, $2.00. Comparison rows only appear for confident matches, typically a handful per branded term.
Apify's free plan includes $5 of usage a month, about 3,300 products.
How do I keep a run from costing more than I expect?
Set a maximum charge for the run when you start it. Before each product, the Actor checks that the budget still covers it and its details together (if details are on), and stops before fetching anything it could not charge; inputs it did not reach are listed as not_processed. Each row is written and charged in one step by Apify's SDK, which writes only what the budget covers, so a row is never charged without being in your dataset. Details are charged only after their row is written, once per product. If Apify cannot record a charge, the run stops its paid work. A run also stops when it is plainly costing more than it earns, for example through a proxy that stalls every request, and shortly before the run's timeout: every request's timeout is cut to the time left, and what was not reached is not_processed.
What happens if Apify restarts a run?
Progress is saved after every input, every 25 products, and whenever Apify announces a migration. A restarted run reads its own dataset, skips every product and comparison row already written, finishes the input it was on without going over its product limit, and carries on, so rows are not written or charged twice. If the dataset cannot be read back, the run stops and fails rather than guess.
Two narrow windows remain. If the run stops between writing a product and charging its details, those details are never charged. And because a write can take a moment to show when the dataset is read back, the row written just before a restart can occasionally be missed, then written and charged a second time. That affects at most one row per restart.
Input example
{"searchTerms": ["cadbury dairy milk", "coca cola 2l"],"stores": "both","maxProductsPerInput": 20,"categoryUrls": ["https://www.woolworths.com.au/shop/browse/specials/half-price"],"specialsOnly": false,"compareStores": true}
Output example
From a real run on 2026-10-18. A comparison row, trimmed:
{"rowType": "comparison","cheapestStore": "aldi","priceDifference": 1.01,"priceDifferencePercent": 33.7,"cheapestByUnitPrice": "aldi","unitPriceDifference": 22.5,"unitPriceUnit": "kg","matchMethod": "brand_size_name","matchConfidence": 1.0,"woolworthsName": "Cadbury Dairy Milk Chocolate Bar 45g","woolworthsPrice": 3.0,"woolworthsUnitPriceStandard": 66.7,"aldiName": "Dairy Milk Chocolate Bar 45g","aldiPrice": 1.99,"aldiUnitPriceStandard": 44.2}
A Woolworths product row, trimmed:
{"store": "woolworths","productId": "814479","name": "Cadbury Dairy Milk Chocolate Block","brand": "Cadbury","size": "180g","price": 5.5,"wasPrice": 8.0,"savings": 2.5,"isOnSpecial": true,"specialTypes": ["special"],"promotionTexts": ["30% Off", "Save $2.50"],"unitPriceText": "$3.06 / 100G","unitPriceStandard": 30.6,"unitPriceStandardUnit": "kg","inStock": true,"barcode": "9300617064879","isSponsored": false,"searchRank": 2,"categoryPath": ["Halloween", "Confectionery", "Chocolate Blocks"],"url": "https://www.woolworths.com.au/shop/productdetails/814479/cadbury-dairy-milk-chocolate-block"}
FAQ
Which store's prices are these?
Woolworths: the prices of Woolworths' default online store (fulfilmentStoreId 1101). Most prices are national, but some fresh and regional items differ by store. Choosing another store needs a logged-in Woolworths account, so there is no postcode setting.
Aldi: Aldi's national in-store (walk-in) prices. Aldi does not sell groceries online, and its prices do not change by store.
So a comparison row puts Woolworths' online price next to Aldi's shelf price.
What does "Specials only" mean at Aldi?
Woolworths filters its own specials. Aldi shows no was-prices at all, so "specials" at Aldi means the products in its own Lower Prices, Super Savers and Limited Time Only groups, and specialTypes says which. Aldi's weekly Special Buys are a separate option, because they are mostly non-food (tools, homewares, clothing) on sale from a given date while stocks last.
What can it not return?
- Coles, whose site blocks automated reads.
- Aldi barcodes and stock levels, which Aldi does not publish.
barcodeandinStockarenullon Aldi rows. - Ratings for most products: Woolworths sends zeros in search results, which are reported as
null, not as a rating of 0. - Prices for a specific Woolworths store or postcode (see above).
Is it legal to scrape supermarket prices?
The Actor reads the same public product data any visitor sees on woolworths.com.au and aldi.com.au, without an account. Check the rules that apply to you and to how you use the data; this is not legal advice.
Why did an input come back blocked?
Woolworths and Aldi both read through Apify's datacenter proxy in every test. If a store refuses a request, the Actor retries on a different IP up to three times, then marks the input blocked. After three blocked inputs in a row it stops, and the rest come back not_processed at no charge. Run again later. Residential and Google SERP proxy groups are not used, because the price per product is set for datacenter traffic.
Other limits worth knowing
- Results come in each store's own relevance order. Woolworths mixes sponsored products into search results; they are kept and flagged
isSponsored, because their position is part of what the shelf looks like. - Both stores' APIs are undocumented and can change without notice. If a store's answer stops making sense, the input is reported
parse_errorrather than filled with guesses. - Up to 1,000 products per input, and 1,000 Special Buys.
Something broke or a number looks wrong
Open an issue on the Issues tab with the input and the run link.