# Changelog of ALDI Australia Scraper & Price Tracker (`myth84/aldi-australia-scraper`) Actor

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

### Changelog

History of `aldi-australia-scraper` (folder `actors/aldi-au-products`). Versions are the Apify build numbers.

#### 0.1 — 2026-10-25

- First version, built offline on the committed fixtures of the audit (`fixtures/aldi-au/*.2026-10-24.json`, brief `docs/cloud-tasks/2026-10-24_aldi-au-build.md`, PR #106). Output schema `ecommerce-grocery/1.0`, created by the Coles build (PR #103) and reused as is with the platform `aldi_au`; no personal data.
- Source: the public JSON API of the site, `api.aldi.com.au` only: `/v3/product-search` (keywords, aisles, Special Buys themes, brands, whole range; 60 products per request, `limit` fixed at 60), `/v2/products/<sku>` (product URLs), `/v2/product-category-tree`, `/v2/promotion-tree`, `/v2/service-points` (store by postcode) and `/v2/service-points/<id>`. Never a page of `www.aldi.com.au` (`/results` is disallowed by robots.txt: a search URL of the input is read locally), never an account, cart or order route (`assertAllowedRequestUrl` before every request).
- Reliability: transport `got`, Apify datacenter proxy by default, one sticky proxy session per search; a refused page (403 "Access Denied", any error page, a 200 that is not the JSON of the route, a network error) is tried again on a new proxy session after 2 to 5 s, 4 attempts at most (the generic crawler's own rotation, once per search, is the last one), then the search is given up with an explicit message; an API error (`{ errors }`, e.g. code 3731) is never retried and fails the run.
- Local decisions:
  - `platform` is `aldi_au`, the value of `GROCERY_PLATFORMS` (the brief's mapping section said `aldi-au`, written before the shared schema); `store.json › platform` keeps `aldi-au` (folder of the fixtures and audit).
  - `storeName` is "ALDI" (brief), `storeUrl` the ALDI website.
  - `maxResults` caps each source of the input (a keyword, an aisle, a URL, the Special Buys, the whole range): the themes of the Special Buys and the brands of a source share its cap, so that the runner's charge cap (`maxResults` × sources) never cuts another source. `0` means 5,000.
  - One search per brand: `brandName` is a multi-value facet, but how several values are sent was not verified.
  - The aisle tree is read whenever an aisle is requested, also by key, to check it and to name `searchQuery`; without the tree (refused), aisles given by key or URL are still searched by key.
  - A theme of a Special Buys URL is matched against the tree by key, slug or title (`pet-essentials` → `Pet Essentials`); a theme absent from the tree is searched as given. `/special-buys` alone stands for every theme.
  - `markPromotions` lists the three promotion aisles in full before the searches (about 12 requests, counted in `maxRequests` and `RUN_STATS.extra.preparationRequests`); their products are neither written nor charged for that.
  - Product URLs are read after the searches, one request each, and deduplicated with them; with monitoring they form one basket record per store.
  - `searchQueries` is prefilled with `["milk"]`, not a schema default (lesson of PR #103: Apify adds the schema defaults to every input); `storeId` and `postcode` have neither default nor prefill (a prefilled store id would collide with a postcode), the code uses G452.
  - The refusal markers include the HTML-encoded `errors&#46;edgesuite&#46;net` of the captured page.
  - `priceIsEstimate` is true for packs sold by weight only (`weightType` 1); loose produce (`weightType` 3) is priced per kilo, its unit price read from `perUnitDisplay`.
  - `imageCount` is 0 for a product without images; `saveAmount` is read from `savingsDisplay`, else computed from a was price (both always empty at build time).
  - The control input reads one source, the whole range sorted by name at postcode 6000 (600 products): the release script expects `maxResults` × (`searchQueries` + `startUrls`) rows and does not count `categories` nor `wholeRange` (a second source would give about 700 rows for 600 expected, the Coles control failure fixed by PR #105).
- Not verified at build time, to check at the first runs: success rate on 200 requests or more with rotation and the refusals per IP, the `name_desc` sort order, the store search by postcode outside NSW (6000 in the control input), fresh-produce prices in other states, 256 MB of memory, the answer of `/v2/service-points/<id>` (shape from the audit, no committed fixture), the answers for an unknown sku or store id (404 assumed), several brands in one run.
- Launch price: decided before publication (no pricing file in this version; `readmeVisibility` of the Store checks waits for it).
