Coles Australia Scraper - Prices, Specials, Barcodes
Pricing
from $0.50 / 1,000 product rows
Coles Australia Scraper - Prices, Specials, Barcodes
Watch Coles Australia prices at one real store and get back only what moved: price cuts, stock, promotions, unit price. Or scrape outright by keyword, aisle or specials. Pin any run to a store by postcode, suburb or store id, because Coles prices are not national. Barcode and nutrition on request.
Pricing
from $0.50 / 1,000 product rows
Rating
0.0
(0)
Developer
SR
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Coles Australia Scraper
Pull live product data out of the Coles Australia grocery catalogue at a real store: name, brand, pack size, current price in Australian dollars, the price before the promotion, unit price, stock, image and the product's own barcode. Search by keyword, browse any aisle, take the whole specials catalogue in one run, or read a full product record including ingredients, allergens, nutrition panel and country of origin.
Coles prices are not national. Give this actor a postcode, a suburb or a store id and every row comes back priced at that shop, with the store stamped on it. That matters more than it sounds: see below.
Or watch instead of scraping. Name a watchlist, schedule the run, and from the second run on you get back only the products whose price, stock, promotion or unit price moved at that shop, with what they used to cost alongside what they cost now.
No account, no cookies to paste, no browser to babysit. Give it a search term and it returns rows.
What you get
| Field | What it holds |
|---|---|
product_id | Coles' own id. Stable, and the key you join runs on |
name, brand, size | "Full Cream Milk", "Coles", "3L" |
price | Current price in AUD. Null, never zero, when Coles publishes none |
was_price | Price before the promotion. Null when nothing is on promotion |
save_amount, save_percent, save_statement | The saving, three ways Coles states it |
is_on_special, promotion_type, multi_buy_promotion | What kind of deal is running |
unit_price, unit_measure, unit_price_label | $1.65, 1L, "$1.65/ 1L" |
is_available, availability_type, available_quantity | Whether Coles will actually sell it to you |
is_sponsored | True when Coles served the row as a paid placement |
retail_limit, promotional_limit, is_age_restricted | Purchase limits |
department, category, aisle | Where it sits in the shop |
variations_total | How many variants of the product Coles lists |
image, url | Image and product page |
store_id, store_name, store_suburb, store_state, store_postcode | Which shop these prices were read at |
is_store_priced | False means the row is a national catalogue figure, not a shelf price |
With Add barcode, ingredients and nutrition switched on, every row also
carries gtin, ingredients, allergen_statement, dietary_claims,
nutrition, storage_instructions, pack_dimensions, country_of_origin,
country_of_origin_statement, long_description, min_guarantee and
last_updated.
The four things it does
Search by keyword. One or more terms, each run separately, each row recording which term found it. "milk" returns 146 products, "chocolate" returns 670, and the actor pages through them 48 at a time until it reaches your limit.
Browse a category. A whole department, dairy-eggs-fridge, or one aisle
inside it, dairy-eggs-fridge/milk. The milk aisle holds 197 products. Top
levels include fruit-vegetables, meat-seafood, bakery, deli, pantry,
frozen, drinks, chips-chocolates-snacks, health-beauty, cleaning-laundry,
baby, pet and home-garden. A path that does not exist is reported as such
rather than quietly returning a different aisle.
Product detail by id or URL. Paste product ids, or paste product URLs and the id gets read off the end of them. This is the operation that returns the barcode and the nutrition panel, neither of which appears in a listing row.
Everything on special. The current promotions catalogue, 7.591 products at the time of writing, paged the same way as a search.
You can narrow a search or a browse to one kind of offer: half price, multi buy or online only. It is applied by Coles rather than by filtering after the fact, so it is a genuine narrowing. "chocolate" returns 670 products with no filter and 77 with half price on.
Prices differ by store, and that is the point
Measured on one afternoon, same product ids, same two-minute window, only the store changed:
| Product | Sydney | Darwin | Karratha WA |
|---|---|---|---|
| Tim Tam Original 200g | $3.00, was $6.00 | $3.00, was $6.30 | $3.00, was $6.42 |
| Bananas, loose | $4.90/kg | $4.90/kg | $4.50/kg |
| a2 Full Cream UHT 1L | $3.90 | $4.05 | $4.17 |
| Coles Full Cream Milk 3L | $4.95 | not sold | $4.95 |
| White sliced bread | $4.60 | $4.60 | $4.60, and not sold in Burnie TAS |
The range moves further than the prices do, and store format moves it most. A search for "milk" returned 128 products in one Victorian supermarket, 121 in Karratha, 118 in Broken Hill, 110 in Darwin and 84 at a small-format Coles Central in the Sydney CBD. The specials catalogue ran from 2.567 products at that express store to 7.066 in Broken Hill.
So a promotion tracker that ignores the store will report a markdown that is not running in half the country, and a price comparison will quote a figure nobody is charged.
Leave the store empty and you get Coles' national catalogue instead. That is
a real thing and sometimes what you want, but it is not a shelf price: the
national listing for "milk" is 146 products, larger than every store measured,
and the national specials catalogue is 7.591 against 2.567 to 7.066 in real
shops. Runs with no store say so in the summary and every row carries
is_store_priced: false, so it is never something you find out later.
A store that cannot be found stops the run. It does not quietly fall back to national figures, because handing you national prices under a store label would be exactly the kind of confident wrong answer the rest of this page is about.
If you have been told that store-specific Coles pricing cannot be pinned from a scraper, that Coles resolves it from where the request comes from and there is no way to override it: that is not true, and the table above is what overriding it looks like.
Watch mode: only what moved, at the shop you buy from
Turn on Watch for changes, give the watchlist a name, and schedule the run. From the second run on you get back only the products that moved, with what they used to cost sitting next to what they cost now.
The comparison is done at your store. That matters more here than anywhere else on this page: a price change in a national catalogue is a change to a figure nobody is charged, while a price change at the shop you actually buy from is the thing you wanted to know. Same for promotions, which run in some states and not others, and for range, because a product can be dropped from your store while the national listing still carries it.
Five kinds of change, each with its own flag
A row that changed because the product went out of stock is a different alert
from a price cut, and one merged changed flag makes both of them useless. So
every row says which dimensions moved, and you can pick which ones count:
| Dimension | What it catches | On by default |
|---|---|---|
price | What you pay moved | yes |
availability | In stock, out of stock, or the price withdrawn entirely | yes |
promotion | A multi-buy or half-price week starting or ending | yes |
unit_price | The price per kg or per litre moved | yes |
was_price | The advertised saving moved while the price held | no |
Two of those are worth spelling out, because a price-only watcher misses both.
A promotion can start without the price moving. "2 for $8" leaves the shelf
price exactly where it was. Anything watching price alone reports UNCHANGED
for the entire promotion.
A pack can shrink instead of getting dearer. 3L at $4.95 becomes 2.5L at
$4.95 and price is identical, while unit_price goes from $1.65/L to
$1.98/L. That is the whole event, and it only shows up if something is watching
the unit price.
Each row carries price_changed, availability_changed, promotion_changed,
unit_price_changed and was_price_changed separately, a changes list of
everything that moved, and one change_type headline for the biggest of them.
Stock outranks price in that headline, because a price on something you cannot
buy is not a price.
The first run tells you it is the first run
A watchlist with no history cannot know whether anything changed. It has not measured "no changes", it has measured nothing, and those are different.
So a first run returns every row marked change_type: BASELINE with
is_change: false, and the summary reports changesDetected: null rather than
0. "Only return rows that changed" is ignored on that run, on purpose, so
you can see the watchlist matched the products you meant before you schedule it.
The summary says all of this in words.
Returning everything labelled NEW would fire an alert for every product you already knew about on day one. Returning nothing would leave you a successful run with an empty dataset and no way to tell it from a breakage. Neither is worth the ten rows it saves.
What it refuses to tell you
It will not compare two different stores. A baseline captured at one store against a run at another would report almost every product as changed, and every one of those alerts would be wrong. The watchlist records its store and refuses the comparison, then starts again at the store this run used, and says so.
It will not report a withdrawn product as free. Coles answers a product it
has stopped selling with a complete record and no price at all. Against a
baseline of $4.95 that is a 100% markdown to anyone who subtracts, and "milk now
free" arriving as a price alert is the worst thing this feature could do. A
price comparison here needs a real number on both sides: a price disappearing is
reported as PRICE_WITHDRAWN and a price reappearing as PRICE_RESTORED, both
under stock rather than price, and neither ever produces a delta.
It will not call a product discontinued on a hunch. If you watch a fixed
list of product ids and one stops resolving, that is DELISTED and you get told
once. If you watch a search term, a product missing from this run has almost
certainly moved off page one rather than out of the range, so it is counted in
the summary and no alert is invented for it. A run that also hit fetch errors
never claims a delisting at all.
It will not call a failed run quiet. If a run retrieves no products, the summary says the comparison could not be made. It does not say nothing changed, and it does not say your whole list was discontinued.
Where the watchlist lives
In your Apify account, under the name you gave it, created by the actor on
its first run and kept indefinitely. We never hold it and cannot see what you
watch. Every run also writes the same record to its own key-value store as
watchlist_state, so if the saved copy is ever unreachable you can paste that
back in as baseline and carry on without losing your history. The summary
always names which of the two the run actually compared against.
What it deliberately does not do
It will not pretend to look products up by barcode. Coles does not index
barcodes, and it does not say so. Feed its search the GTIN printed on its own
milk bottle and you get fifteen products headed by a paper towel at $5.50 and a
tin of dog food at $14.00. Real names, real prices, HTTP 200, no error anywhere.
Any scraper that offers a barcode input against Coles without reading the flag
Coles sets on that response will tell you, with total confidence, that your milk
barcode is dog food. So there is no barcode-lookup operation here, because there
is no barcode lookup at Coles. The barcode itself is published on each product record and is returned
whenever you ask for detail, which is a different and honest thing. If you need
to go from a barcode to a product, run detail over the ids you already have and
match on gtin at your end.
It will not offer a sort order. Coles accepts a sort parameter and ignores it. Price ascending and price descending returned identical first rows in testing and the response kept reporting relevance ordering either way. Shipping a sort control would be shipping a button that does nothing, so there isn't one. Sort the dataset after the run instead.
The part that matters: it does not hand you a confident wrong answer
Coles has several ways of saying "no" that arrive as a successful response with products in it. Each one was found by testing for it, and each one is guarded.
A query it cannot match returns substitutes, not nothing. Search for
zzzqqqxxwwv and Coles returns two products, the first being a hayfever tablet.
Search for asdkjhasdkjhasd and it returns a children's matching game. These are
well-formed rows with real names and real prices, and nothing about the row
itself looks wrong. Coles does flag the response, and this actor reads that flag:
a term that matched nothing comes back as a row with found: false and a reason,
never as an unrelated product.
A product Coles no longer sells keeps its record and loses its price. Ask for a product that never existed and you get a clean "not found". Ask for one Coles has stopped selling and you get a complete record - name, brand, barcode, images
- with the entire price object set to null. Anything that treats a missing price
as zero publishes a free product. Prices here are only ever read through a helper
that returns null, never zero, and the row carries
is_available: falseso you can filter on the fact rather than infer it from a suspicious number.
Sponsored rows repeat across pages. Coles injects paid placements on top of the 48-per-page grid, so page one of a search carries more than 48 products. Those extras are not stable between pages: three products on page two and five on page three of one test had already been returned earlier. Rows are deduplicated by product id, and every row tells you whether it was a paid placement, so you can drop them or keep them as you like.
Coles advertises a filter it then refuses. Its own response lists an "all specials" filter with a count next to it. Requesting that filter returns a 404, every time. The specials operation uses the dedicated promotions catalogue instead, which is why it works.
Every run writes a summary record saying how many products came back, how many
had a price, how many were paid placements, how many are no longer available, and
which of your search terms Coles could not match. If something went thin, the
summary says so rather than leaving you to notice.
Input
| Input | Meaning |
|---|---|
operation | search, category, product or specials |
store | A postcode, a suburb, or a Coles store id. Prices the whole run at that shop |
monitor_mode | off, or changes to compare this run against the last one |
watchlist_name | Keeps separate watchlists apart. One name per store and per set of products |
track | Which of price, availability, promotion, unit_price, was_price count as a change |
only_changed | Return just the rows that moved. Ignored on a first run |
baseline | Optional. A saved watchlist_state record to compare against directly |
queries | Search terms, one per line |
categories | Category paths such as dairy-eggs-fridge/milk |
product_ids | Product ids or full product URLs |
special_offer | any, half_price, multi_buy or online_only |
with_details | Adds barcode, ingredients, nutrition and origin. One extra request per product |
max_results | How many products in total. A page carries 48 |
max_pages | Page ceiling per term or category |
retries | Re-asks for a page that did not come back |
What people use it for
Price monitoring against your own range or a competitor's, at the stores you
actually care about rather than at a national average nobody pays. Regional
price-gap analysis: run the same basket at a metropolitan store and a remote one
and the difference falls straight out. Promotion tracking, because was_price
and save_percent make a half-price week visible without watching the catalogue
by hand, and because the store field tells you where the markdown is actually
running. Range-gap analysis: browse an aisle, compare the
product ids against your own list, and the difference is the gap. Unit-price
comparison across pack sizes, which unit_price and unit_measure make
arithmetic rather than guesswork. Product content, because the detail operation
returns the ingredients and nutrition panel Coles prints on the pack.
Prices are Coles' online prices in AUD for the store you selected. Promotions change weekly, so treat a run as a snapshot with a timestamp rather than a permanent fact.
Notes
Empty results are a successful run, not a failure: if Coles has nothing for your
term, you get zero rows and a summary that explains why, not a crash. Errors
carry a code and a message and land in the errors record.
This is an unofficial scraper. It is not affiliated with, endorsed by or connected to Coles Group Limited. Use the data in line with Coles' terms and with the law that applies to you.