# Changelog of Coles Scraper: Grocery & Unit Prices, Specials (`myth84/coles-product-scraper`) Actor

- **URL**: https://apify.com/myth84/coles-product-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/myth84/coles-product-scraper.md

### Changelog

History of `coles-product-scraper` (folder `actors/coles-products`). Versions are the Apify build numbers.

#### Unreleased

- **Fix: the release control run failed its row count** (brief `docs/cloud-tasks/2026-10-25_coles-fix-release.md`, 2026-10-25). Symptom: the control run `mO4E9lb1SyfFZvmm8` (`inputs/control-scale.json`: "milk", "bread" and the pantry aisle, `maxResults` 300) gave 889 rows for 600 expected. Two causes. The expectation: `scripts/lib/actor.mjs › expectedRows` multiplies `maxResults` by the number of `searchQueries` and `startUrls` only and does not count `categories` (2 × 300 = 600, while the Actor reads 3 searches, 900 at most). The input: "milk" has only 323 products at store 7674 (audit § 3) and a product is written once per store and run, so the long-life milk the pantry aisle wrote first left the "milk" search short of 300 at its last page (deduced from the code and the audit, the run log is not readable from this session). The control input now reads one aisle, `dairy-eggs-fridge` (2,269 products announced in the capture of 2026-10-24), at 600 products, store 7674, residential AU: about 13 pages on new sessions, so the release script expects exactly the rows the Actor gives. Two tests replay `expectedRows` on every committed input against `maxResults` × the searches of the Actor, and require each release input to read one search whose captured first page announces at least `maxResults` products; both fail on the former control input (600 expected, 900 at most). No code change: rows, logs and charges are identical; validation and daily-check inputs, README, prices and example tasks unchanged.

#### 0.1 — 2026-10-25

- First version, built offline on the committed fixtures of the audit (`fixtures/coles/*.2026-10-24.json`, brief `docs/cloud-tasks/2026-10-24_coles-build.md`, PR #103). Output schema `ecommerce-grocery/1.0`, created by this build and shared with the coming ALDI Australia Actor; no personal data.
- Source: the Coles BFF API (`/api/bff/products/search` for keywords, aisles and specials, `/api/bff/products/<id>` for product ids), 48 products per page with the desktop channel header, the public subscription key of the site (read again once per run on a 401). Nothing else is requested: no `/search/`, `/product/`, `/brand/` or `?filter_Special=` URL (robots.txt).
- Reliability: impit with the Chrome profile, a new proxy session for every request, 4 attempts per page, Apify residential proxies with an Australian exit by default. Local decision: after 3 refusals in a row the run switches to free Apify datacenter sessions, and back to the residential pool after 3 refusals in a row there (the brief kept the datacenter pool "for the rest of the run"; it was refused for whole periods during the audit).
- Local decisions: the Apify title is "Coles Scraper: Grocery & Unit Prices, Specials" (the brief's title has 53 characters, above the 50 the Store checks allow; the SEO title keeps it); the unit price comes from `pricing.comparable`, `pricing.unit` being a fallback read per kilo for products sold by weight; `priceIsEstimate` is true for products sold by weight; `storeLocationName`, `storeRegion` and `storePostcode` are filled for the two stores the audit located (7674 Truganina VIC 3029, 313 Karratha WA); the control input reads two keywords and an aisle at 300 products each (with keywords, `specials` filters them, so "two searches and the half-price catalogue" cannot be one input); product ids are not capped by `maxResults`.
- Not verified at build time, to check at the first runs: sort orders `priceDescending` and `relevance`, specials combined with keywords or aisles, sub-aisle requests (`categoryLevel=2`), stores other than 7674 and 313, the `lunchbox` aisle, product links with special characters, the key refresh from `/browse/dairy-eggs-fridge`, the answer of the product route for an unknown id.
- `searchQueries` is **prefilled** with `["milk"]`, not a schema default (review of PR #103, model: the Booking `location` of PR #34): Apify adds the schema defaults to every input, so a run made of `specials`, `categories` or `productIds` alone would also have searched milk (the public task *Coles half-price specials this week* would have listed only half-price milk, *Coles dairy aisle prices* would have charged up to 60 unrequested products). The code still searches milk when nothing else is given (`{}` unchanged); the field descriptions and the README say to clear the prefilled keyword in the form. Tests replay the example tasks and the single-source inputs with the schema defaults added as Apify does.
- Launch price: decided by the user before publication (no pricing file in this version; `readmeVisibility` of the Store checks waits for it).
