# Changelog of Lelong.my Scraper | Malaysia Online Marketplace (`haketa/lelong-my-scraper`) Actor

- **URL**: https://apify.com/haketa/lelong-my-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/haketa/lelong-my-scraper.md

## Changelog

All notable changes to the **Lelong.my Scraper** Apify actor are documented here.

### 1.1.0 — 2026-11-02

#### Fixed — intermittent grid-less responses no longer truncate large sweeps

- Lelong occasionally serves a valid `200` of a search URL (`/catalog/all/list?TheKeyword=…`) **without the product grid** — a header/footer "shell" page. Because that shell is a real 200 with a >1 KB body, the previous logic accepted it, recorded "0 listings", and after **3 such pages in a row aborted the entire multi-keyword run** — so broad jobs stopped far below `maxRecords`.
- The actor now detects a grid-less 200 and **re-fetches the same URL up to 3×** with freshly generated headers; the product grid reliably reappears on a retry.
- A term that remains empty after re-fetching is now **skipped**, and the sweep continues to the remaining keywords/categories. Only **consecutive hard fetch failures** (non-200 / timeouts — a genuine Lelong outage) trip the run-abort guard.

#### Verified (fresh run, 2026-11-02)

- Broad multi-category + multi-keyword sweep → **160+ unique records in ~90 s** at `maxConcurrency: 8`, **no Apify Proxy** (Lelong has no Cloudflare/WAF).
- **Core/identity field coverage ~100%**: `title`, `listingUrl`, `price`, `currency`, `listingId`, `image`, `images`, `brand`, `category`, `condition`, `availability`, `shopName`, `listingType`, `description`, `viewCount`, `postedYearMonth`, `sourceUrl`, `scrapedAt`.
- **0 duplicates** on `listingUrl`; **0 value bugs** (`[object Object]`, scheme-less URLs, wrong types); prices clean (RM 2.50–27,405).

#### Docs

- README refreshed to the gold standard: replaced fabricated output examples with **real fresh-run records** (a branded `Item` watch and a `Promoted` listing with a full image gallery), corrected the field reference to honestly mark data-dependent fields (`model`, `shopUrl`, `shopLocation`, `soldCount`) as *present when available*, added an explicit **Pagination & Scale** section, and fixed the stale "`Actor.fail()` on zero records" description (the actor sets a descriptive status message and exits).

### 1.0.4 — 2026-10-25

- **Search migration.** Lelong retired inline listings on the legacy `/kx/{keyword}.htm` route (now an SEO shell) and moved search to `/catalog/all/list?TheKeyword=…`. Re-pointed keyword/category search to the live endpoint.
- Added a `data-id` / `data-link` / `data-name` / `data-price` **catalog-card parser** so stubs are rich (title, price, image, listing ID) even with detail enrichment off.
- Recover `listingId` for the newer `I…` / letter-wrapped ID format; populate `category` from `Product.category`; normalize currency `RM` → `MYR`.
- Legacy `/kx/` Start URLs are transparently rewritten to the live catalog search.

### 1.0.0 — 2026-05-18

- Initial public release as **Lelong.my Scraper** — HTTP-only via `got-scraping` + `cheerio`, JSON-LD `Product`/`ItemList` primary extraction with three fallback strategies, detail-page enrichment, 3-attempt retry with exponential backoff, descriptive status message on zero records.

### 0.9.0 — 2026-05

- Internal pivot from `carlist-my-scraper` (Carlist.my sits behind Cloudflare Bot Management Pro, which blocked every approach); architecture re-targeted at Lelong.my.

### 0.5.0 — 2026-04

- Internal pivot from `mudah-scraper` (Mudah.my WAF became impassable without browser automation + residential proxies); short-lived Carlist.my attempt.
