Public Marktplaats.nl listings from category and search pages: title, price (EUR), seller, location, photos, attributes, URL, and optional detail page. No start fee.
OUTPUT.statusMessage now always equals the platform status text on every path (rows, removed-only, empty, errors). One function buildStatusMessage builds the text for both setStatusMessage and OUTPUT. Previously the removed-only path wrote the old '1 listing(s) were removed…' text and the rows path wrote null (red: redlogs/status-sync.red.log; mutation dropping the OUTPUT assignment on the rows path: redlogs/status-sync.mutation.log).
Empty result rule: the throw condition (total === 0 && errors > 0) was already right, but a genuinely empty search (site reports totalResultCount: 0) was recorded as a 'page returned no listings' error, so the run still threw. Such a page is no longer an error and is not refetched: the run ends SUCCEEDED with 0 rows, nothing charged, seed status empty, and status message plus OUTPUT.statusMessage 'No rows: no listings matched the input. Nothing was charged.' No rows with errors>0 still throws (red: redlogs/empty-search.red.log; mutation total === 0 only: redlogs/empty-search.mutation.log).
Run status message: Actor.setStatusMessage (best-effort, wrapped in try/catch) at the end of every run path: completion (Done: N rows. Removed: R (410: a, 404: b).), only removed (No rows: 1 listing removed (410: 1, 404: 0). Nothing was charged.), no results (No rows: no listings found (E errors). Nothing was charged., set before the run fails), and the hard run-time-limit stop. The final Done log line now includes the removed counts. A throwing setStatusMessage never fails the run (red: redlogs/status-message.red.log; mutation: redlogs/status-message.mutation.log).
Review 6665295 fix 3: city on detail rows falls back to the list row's city when the detail page has no structured ad city (e.g. location is country-only), instead of the seller-profile city. QA cases: Genemuiden x2, Harlingen x1. A structured ad city still wins (red: redlogs/review-6665295-fix3.red.log).
Review 6665295 fix 2: HTTP 404 on a detail page is final (not retried). Detail start URL: treated as removed (counted in OUTPUT.removedListings and new OUTPUT.removedBy.notFound404, not pushed, not charged, not an error). List-to-detail: the list-only row is still delivered and charged as before (useful data), and the 404 is counted in OUTPUT.removedBy.notFound404 only; removedListings keeps meaning removed/skipped (red: redlogs/review-6665295-fix2.red.log).
Review 6665295 fix 1: a detail start URL answering HTTP 410 is no longer an error that fails the run. It is counted in OUTPUT.removedListings and new OUTPUT.removedBy.gone410, not pushed, not charged, not retried; a run whose only start URL is removed succeeds with 0 rows. New OUTPUT.statusMessage notice; new .actor/output_schema.json (red: redlogs/review-6665295-fix1.red.log).
Test: a detail page whose first answer is empty and whose refetch answers HTTP 410 is skipped (not pushed, not charged), counted in OUTPUT.removedListings and not in errors. Code already behaved this way; red run is a mutation that removes the refetch 410 check (redlogs/detail-410-refetch.red-mutation.log).
Detail page HTTP 410 (listing removed) is no longer retried or reported as a network error: the listing is skipped (not pushed, not charged), counted in new field OUTPUT.removedListings, and the run continues (red: redlogs/detail-410.red.log).
robots.txt Disallow matches are counted once per request in OUTPUT.robotsDisallowedHits (was counted twice with the real fetcher). Tests: robots.txt 5xx keeps the crawl going; Disallow is warn + count + fetch. Test seam options.request. Crawl-delay, rate limit and 403/429 backoff unchanged.
0.1.0 — 2026-10-26
Initial Marktplaats.nl scraper (list + optional detail) from kijiji-actor template.
PPE draft: listing / listing-detail at $0.0006 each; no start fee (azzouzana $0.0012 ×50%).
Cost gate draft: $0.0798 per 1000 (= $0.0006 × 1000 × 0.133). Prefer list __NEXT_DATA__; keep detail ratio low.
Privacy policy A: keep published seller commerce contact; drop pure private PII; do not call Disallow'd seller-profile APIs.
List from public __NEXT_DATA__ → searchRequestAndResponse.listings; detail from window.__CONFIG__.listing + Product JSON-LD.