# Changelog of Kleinanzeigen Scraper — German Classified Ads & Prices (`logiover/kleinanzeigen-de-classifieds-scraper`) Actor

- **URL**: https://apify.com/logiover/kleinanzeigen-de-classifieds-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/logiover/kleinanzeigen-de-classifieds-scraper.md

## Changelog

### 2026-09-24

- **Fixed zero-row runs: the JSON endpoint this Actor called no longer exists.** Asking the search URL for JSON used to return the site's view model under `result.resultList`. It now answers HTTP 200 with HTML whatever the request asks for, and `resultList` appears zero times in the response. Verified on 2026-09-24.
- **What the parser reads now.** The page is a server-side render that still hands its impression tracker the whole result set, so the Actor reads the ad previews out of that — the objects have exactly the shape the JSON API used to return, which is why **every output column is preserved, including `sellerId`, `sellerType`, `isCommercial` and `shopUrl`** that a rendered card does not show.
- **Added a fallback that reads the rendered cards**, anchored on the `data-adid` and `data-href` attributes, on each card's own schema.org block and on the shape of the text (a five-digit postcode, a `Heute, 07:12` stamp) rather than on class names, which on this site are generated. Measured on six live pages it recovers 127 of 127 ads. In that degraded mode `sellerId`, `shopUrl` and `hasVideo` are empty and `sellerType`/`isCommercial` are only filled for shop sellers; the primary path fills all of them.
- **`shopUrl` is now an absolute link.** It was being written as the site-relative path (`/pro/example`), which was not usable as a link in an export.
- **`shippingAvailable` is now `false` rather than blank** when an ad offers no shipping. The source always publishes the field, so reporting "no" as "unknown" was deleting the commonest value in the column.
- `imageCount` falls back to the photo-count badge on the card when a row comes from the fallback reader.
- No column was dropped and none is permanently empty.
- Hardened the empty-run guard against the dataset's own reporting lag: a zero row count is now re-checked before a run is failed, so a crawl that pushes its last rows at the end can no longer be thrown away as empty.
- **Fixed runs that finished successfully with an empty dataset.** A crawl that is blocked and a search that genuinely has no results both produce an empty file; the Actor now fails with an explicit `NO_ROWS` error and writes a `SUMMARY` record instead of reporting success.
- Diagnosis: Kleinanzeigen intermittently refuses German residential IPs.
- Proxy configuration now defaults to the **UNBLOCKER** group, which was measured fetching this source reliably where datacenter and residential IPs were refused.

### 2026-09-23

- Fleet-wide quality audit. Verified end to end against live data and re-checked the input schema, the output columns and the run configuration.
- Run reliability reviewed: 100.0% of public runs succeeded in the last 30 days.
- Input schema, output schema and pricing configuration reviewed.

### 2026-09-01

- Fleet-wide health check. Verified against this Actor's real run history: 30-day success rate, output row counts, per-field fill rates, and peak memory against the configured memory limit.
- Reviewed for the failure patterns that have cost this fleet runs — unguarded proxy setup, retry loops that can outlast the run's own time budget, and full-page HTML parsing that can exhaust a small container.
- No change to input, output fields or scraping logic.

### 1.0 (2026-08-15)

- First release. Covers 16 top-level categories with keyword and price filters, private/commercial seller split and per-category attributes.
