AU Grocery Price Comparison - Cheapest retailer + Price History
Pricing
from $3.00 / 1,000 product prices
AU Grocery Price Comparison - Cheapest retailer + Price History
Prices a shopping list across every Australian retailer holding each item, names the cheapest and dearest shop with the saving worked out, judges today's price against that product's own history, and totals the whole basket per retailer against buying each item wherever it is cheapest.
Pricing
from $3.00 / 1,000 product prices
Rating
5.0
(1)
Developer
GatherWorks
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
12 days ago
Last modified
Categories
Share
๐ AU Grocery Price Comparison โ Cheapest Retailer + Price History
Name the products. Get back where each one is cheapest, how much you save by not buying it at the dearest shop, and whether today's price is actually a good one.
Swept daily, kept since March 2024. Coles, Woolworths and ALDI are re-priced every day and every price is retained โ 30 million observations and counting โ so you get today's shelf price and the two and a half years behind it. Every figure carries the date it was seen, and every product can be judged against what it has actually cost.
๐ What is it?
Give it a shopping list. For each item it finds the product, prices it at every retailer holding it, then does the part you would otherwise do by hand:
- ๐ฅ names the cheapest shop and the dearest, and works out the gap
- ๐ฐ๏ธ places today's cheapest price in that product's own price range over 7 days to 2 years
- ๐งบ totals the whole list per retailer, and tells you what one trip costs against cherry-picking
โก What can it do?
- ๐ช Compare across retailers in one call. Coles, Woolworths, ALDI and marketplace sellers sit in the same database, so a single item comes back priced at all of them together.
- ๐ธ Quantify the saving.
spreadCentsandsavingPctsay what the difference is actually worth, per item. - ๐ Say whether it is a good price.
verdictisgood-price,typicalorpoor-priceโ judged against the low, median and high that product has really held, not against a guess. - ๐งบ Price the whole basket. Which single shop is cheapest for everything, and how much more that is than buying each item wherever it is cheapest.
- ๐ Swept daily. The major chains are re-priced every day, so a row is typically hours old, not
weeks โ and
observedandstaleDayssay exactly how old rather than leaving you to assume. - ๐ Date every price. A price with no timestamp is a claim, not a fact.
- โ๏ธ Refuse a comparison it cannot make honestly. See below โ this is the part most price tools get wrong.
๐ฏ It refuses rather than guesses
Three refusals are built in, and each protects you from a number that looks authoritative and is not:
| Refusal | Why |
|---|---|
| ๐ซ A price you cannot act on never wins | A tracker's own "lowest across retailers" figure is a real observation and not a shop. Naming it "cheapest" would send you to a checkout that does not exist. |
| โฑ๏ธ Prices seen days apart are flagged, not merged | comparable: false and observationGapDays ride on the row. Two prices seen a week apart are two facts, not a comparison. |
| ๐คท A thin history gets no verdict | If the database holds no price for most of the window, verdict is unknown and verdictBasis says exactly how much was missing. A confident call off one data point is not one we have earned. |
โจ๏ธ Inputs
| Input | Type | Default | What it does |
|---|---|---|---|
๐ queries | string[] | โ | Required. One product name per line, matched against product names in the database |
๐ข matchesPerQuery | integer | 3 | Distinct products to price per name. "milk" matches many; "Weet-Bix 375g" matches one. Each is charged |
๐ market | string | "AU" | ISO 3166-1 alpha-2. AU is the only market with coverage; another code returns no matches rather than guessing |
๐ง maxItems | integer | (none) | Hard cap on products priced across the run |
๐ priceHistory | off/30d/90d | 30d | Verdict + history bar. Add-on |
โ๏ธ unitPrice | boolean | true | Price per 100g/100ml. Add-on |
๐ listingHistory | boolean | false | Every listing ever seen. Add-on |
๐ท๏ธ stockCodes | boolean | false | Retailer SKUs. Add-on |
๐ฅฃ nutrition | boolean | false | Nutrition panel. Add-on |
๐ What data does it return?
One row per product, with every retailer's offer nested inside it.
| Field | Notes |
|---|---|
๐ท๏ธ name, brand, gtin, productRef | productRef is stable โ keep it to re-price the same product later without searching again |
๐ฆ size, sizeBase, sizeKnown | The pack size as the shop printed it (375g, 2 x 1L), plus that quantity normalised. sizeKnown is false when no shop published one โ see below |
๐ฅ bestRetailer, bestPriceCents, bestListing | Where it is cheapest, and the page it was seen on |
๐ฐ dearestRetailer, dearestPriceCents | The other end of the range |
๐ธ spreadCents, savingPct | What shopping around is worth on this item |
๐ช retailerCount | How many shops the figure is drawn from |
๐
bestObserved, bestStaleDays | When that price was seen, and how long ago |
โ๏ธ comparable, observationGapDays | Whether the compared prices were seen close enough together to mean anything |
๐ฉ bannerPresent | True when a banner (e.g. IGA) is involved, whose stores price independently |
๐ verdict, verdictBasis | good-price / typical / poor-price / unknown, and the reasoning |
๐ windowLowCents, windowMedianCents, windowHighCents | The range this product has actually held |
๐ vsMedianPct, vsLowCents, priceDirection, priceChanges | How today sits against that range, and which way it is moving |
๐ฌ windowCoveragePct | How much of the window the database can actually account for |
๐งฎ sourcesConfirming | How many independent sources stand behind the price โ a count, so you can weigh the evidence |
โ ๏ธ disputedListings, widestDisputeCents | Listings where sources disagreed on the price. Where they do, at least one is wrong, and you should know |
๐ historyBar[] | (history add-on) The price series as intervals, ready to chart. carried marks an interval the price was assumed rather than seen |
๐ฏ windowDearerPct | (history add-on) Share of the window, by time, the price sat above where it stands now. 89 means it was dearer than this 89% of the time |
โณ daysAtCurrentPrice, longestHeldCents, longestHeldDays | (history add-on) How long today's price has held, and the price that held longest |
๐ lastChangeCents, lastChangePct | (history add-on) The most recent move, and how big it was |
โ๏ธ per100Cents, measure, cheapestPackIsCheapestUnit, estimated | (unit price add-on) |
๐ listingHistory[] | (listing add-on) Every listing: link, firstSeen, lastSeen, observations, stillListed |
๐ท๏ธ stockCodes[] | (stock code add-on) Each retailer's SKU |
๐ฅฃ nutrition | (nutrition add-on) available: false with a reason where none is held |
๐งพ offers[] | Every retailer: price, listing, observed, staleDays, corroboration |
โ ๏ธ disclaimer | Carried onto every row, not left in an envelope |
Plus one basket row per run, also saved to the key-value store as BASKET-SUMMARY:
| Field | Notes |
|---|---|
๐งบ byRetailer[] | Every retailer: how many of your items they carry, the total for those, and missing โ the items they do not stock, because a cheap-looking basket is usually a short one |
๐ฅ cheapestFullBasket | The cheapest single shop that carries everything โ null if none does |
๐บ๏ธ bestCoverage | Who came closest, when no single shop carries the lot |
โ๏ธ cherryPickTotalCents | The total if you buy each item wherever it is cheapest |
๐ singleStorePremiumCents | What one trip costs you over cherry-picking. This is the number that decides whether a second stop is worth it |
๐ซ What it does not return
- Live prices. Every row is an recorded observation. The retailer may have changed it since โ hence the disclaimer on every row.
- Stock levels you can trust.
inStockis oftennull. It is passed through, never inferred. - A verdict it cannot support. Sparse history returns
unknown, on purpose. - Which price tracker the observation came from. Sources are not disclosed; the retailer you would actually buy from is.
๐ Real output
From {"queries": ["weet-bix breakfast cereal"], "priceHistory": "90d", "unitPrice": true} โ a real
run against live data, not an illustration:
{"name": "Weet-bix Breakfast Cereal","size": "575g","sizeBase": 575,"sizeKnown": true,"gtin": "9300652805048","productRef": "prod_HpNtNhDVHVvxx2YGfXF_SA","retailerCount": 2,"bestRetailer": "coles","bestPriceCents": 350,"bestListing": "https://coles.com.au/product/2001784","sourcesConfirming": 3,"dearestRetailer": "woolworths","dearestPriceCents": 500,"spreadCents": 150,"savingPct": 30,"comparable": true,"observationGapDays": 0,"disputedListings": 2,"widestDisputeCents": 90,"verdict": "typical","verdictBasis": "Judged against a 250c-500c range over 90d, 98.6% covered.","windowLowCents": 250,"windowMedianCents": 500,"windowHighCents": 500,"windowDearerPct": 86,"daysAtCurrentPrice": 0,"lastChangeCents": -150,"priceDirection": "volatile","priceChanges": 15,"cheapestByUnitRetailer": "coles","per100Cents": 61,"measure": "mass","cheapestPackIsCheapestUnit": true,"estimated": false,"disclaimer": "Not a live observation, confirm with retailer"}
Read that row out loud and it says something no single shop can tell you:
- ๐ฅ The same 575g box is $3.50 at Coles and $5.00 at Woolworths on the same day โ 30% apart, and
comparable: truewithobservationGapDays: 0means both prices were seen in the same sweep, so the gap is real rather than an artefact of looking on different days. - ๐ฆ
size: "575g"is what makes that a comparison at all. Same pack, both shops. - โ๏ธ 61c per 100g, and the cheapest box is also the cheapest per gram โ which is not always true.
- ๐ $3.50 is
typical, not a bargain. It beats the $5.00 median, but this product has been as low as $2.50 in 90 days across 15 price changes, so the verdict declines to call it a good price. - ๐งฎ Three sources confirm the winning price, and โ ๏ธ two listings have sources that disagree, by as much as 90c. At least one of those is wrong, and you get told rather than handed one confident number.
๐งฉ Add-ons
Everything below is opt-in and separately priced. Leave them off and you pay for prices only. An add-on is charged only when the lookup actually answered โ a product with no nutrition on file is never billed for nutrition.
| Add-on | What it adds | Price |
|---|---|---|
| ๐ Price history โ 1 month | Verdict (good / typical / poor) against the range the product actually held, plus a chartable history bar | ๐ต $0.006 per product |
| ๐ Price history โ 3 months | The same, through a full promotion cycle โ a price that looks good against a month often reads as typical against a quarter | ๐ต $0.012 per product |
| โ๏ธ Unit price | Price per 100g / 100ml at each retailer, and whether the cheapest pack is also the cheapest per unit | ๐ต $0.004 per product |
| ๐ Listing history | Every listing ever seen: the link, first and last sighting, how many observations stand behind it, whether it is still listed | ๐ต $0.005 per product |
| ๐ท๏ธ Retailer stock codes | Each retailer's own SKU, for matching to your catalogue or a supplier feed | ๐ต $0.004 per product |
| ๐ฅฃ Nutrition | The nutrition panel where one is held. Batched, so one charge covers up to ten products | ๐ต $0.01 per batch |
๐ฆ Why pack size is on every row
A price gap is not a comparison until you know the two packs are the same size. $3.50 against $5.00 means nothing if one box is 375g and the other is 575g โ and until recently that field was not published at all, which made every cross-retailer figure weaker than it looked.
size is what the shop printed. It is never computed: a size is derivable by
dividing a shelf price by a per-100g price, and that returns 266.67g for a box
printed 266g โ a number no shop wrote, in a field you would reasonably read as
the shop's own words. Where nobody published one, sizeKnown is false and
size is absent rather than guessed.
๐ Included at no extra charge
These ride along with calls you have already paid for, so they cost nothing on top:
- ๐งฎ How many sources confirm each price. Three independent sightings is a different quality of evidence from one, and now you can see which you have.
- โ ๏ธ Where sources disagree.
disputedListingsandwidestDisputeCentsflag listings whose sources report different prices. At least one of them is wrong โ worth knowing before you act on a thin margin. In live testing this fired on a real product with a 90c spread between claims. - ๐ฏ
windowDearerPctwith any history add-on: the share of the window the price sat above today's. One number, no interpretation needed. - โณ How long the current price has held, the price that held longest, and the size of the last move โ all free with history.
- ๐งบ What each retailer does not stock, in the basket row. A retailer looking cheap because they carry four of your ten items is the trap this closes.
- ๐ฆ Pack size, so a price comparison is a comparison. Free on every row, add-on or not.
โ๏ธ Why the unit price add-on earns its keep
The cheapest box is regularly not the cheapest buy. cheapestPackIsCheapestUnit answers that
outright instead of leaving you to divide by pack size in your head โ and where a size was estimated
rather than published, estimated: true says so, because a unit price built on a guessed divisor is
not the same claim as one built on a printed weight.
๐ฐ How much does it cost?
Pay-per-event. You are charged for products actually priced and saved โ never for a name that matched nothing, and never for a failed lookup.
| Base event | Price |
|---|---|
| โถ๏ธ Run start | ๐ต $0.00005 |
| ๐ช Product priced across retailers | ๐ต $0.005 |
| ๐งบ Basket comparison (once per run) | ๐ต $0.02 |
You are never charged for work you cannot read. Every row is billed at the moment it is saved, so a charge cannot exist without a row in your dataset. A name that matched nothing, a lookup that failed, an add-on with no answer to give โ none of them bill. And if you stop the run, or it reaches the spending limit you set, whatever was already priced is saved and billed before it exits, rather than being thrown away after the work was already done.
Worked examples:
| What you run | Cost |
|---|---|
| ๐ 20 products, prices only โ every add-on off | ๐ต $0.12 |
| ๐ฅ 20 products + 1-month history | ๐ต $0.24 |
| ๐ 20 products + 1-month history + unit price | ๐ต $0.32 |
| ๐ฆ 20 products + 3-month history + unit price + listings + codes | ๐ต $0.62 |
| ๐ฑ 20 products, everything on | ๐ต $0.64 |
A weekly 20-item shop with history and unit prices runs about $1.28 a month. Prices only is $0.48 a month.
๐ Against the alternatives
Price history is the part nobody else sells. Dedicated price-intelligence platforms put it behind their top tier โ Prisync unlocks price history on its $399/month plan โ and the per-result scrapers do not have it at all: the closest one tells you to build history yourself by scheduling repeated runs, which means you start accumulating from today.
| This Actor | Per-result scrapers | Price-intelligence SaaS | |
|---|---|---|---|
| ๐ Price history | โ back to March 2024 | โ starts when you do | โ top tier only |
| ๐ช Across retailers in one call | โ | โ one retailer per Actor | โ |
| โ๏ธ Unit price comparison | โ | โ | โ sometimes |
| ๐งบ Basket vs cherry-picking | โ | โ | โ |
| ๐ต Minimum spend | none | none | ๐ธ $99โ$399 / month |
Below roughly 800 products a month, this is cheaper than the entry SaaS tier โ and you are not buying capacity you will not use.
โ FAQ
How fresh is the data?
The major chains are swept every day. In practice a Coles, Woolworths or ALDI price is a few hours
to a day old, and observed and staleDays tell you precisely which โ a price is never presented
as current when it is not. Smaller retailers and marketplace sellers are swept less often, and their
staleDays says so rather than hiding it.
Why is staleDays not always zero, then?
Because the sweep runs once a day, and because it records when it looked. A two-day-old price is
disclosed as two days old rather than dressed up as live. That disclosure is the point: a price with
no timestamp is a claim, not a fact.
Why did I get verdict: "unknown"?
The database holds too little of that window to judge fairly โ verdictBasis says how much was
missing. Try a longer trendWindow, or a more common product.
Why does one product show only one retailer?
Only one retailer in the database holds it. retailerCount is always the honest count, and
savingPct is 0 rather than an invented figure.
Why is cheapestFullBasket null?
No single retailer carries every item on your list. bestCoverage names who came closest.
Can I re-price the same product later?
Yes โ keep productRef. It is stable and reveals nothing about where the observation came from.
Do I pay for an add-on that found nothing? No. Add-ons are charged only when the lookup answered. Nutrition for a product that has none, or a history window too sparse to judge, costs you nothing.
Why is size missing on some products?
Because no shop published one for that product. It is never computed โ a size divided out of a
per-100g price returns 266.67g for a box printed 266g, and that is not the shop's own words.
sizeKnown: false says so plainly.
savingPct looks huge โ is it real?
Check size and comparable first. A large gap between different-sized packs is not a saving, and
comparable: false means the two prices were seen days apart. Both fields are on every row so you
never have to take a percentage on trust.
Is this hitting Coles and Woolworths when I run it? No โ and that is why it is fast and why it can answer questions a live scrape cannot. Prices are swept daily into a database that keeps every observation, so one call returns every retailer at once plus years of history behind each. A live scraper can tell you today's price at one shop; it cannot tell you that $3.50 is typical rather than a bargain.

