Scrape used-car listings from Cars.bg, Bulgaria's largest car marketplace — make, model, year, price in EUR and BGN, mileage, fuel type, gearbox, engine power, body type, color, seller, and photos. Export to JSON or CSV; enrich each listing from its detail page.
Root cause: CEO report flagged 69% 30-day customer success rate (9/29
FAILED), unchanged day-over-day. Reproduced live via 5 throwaway cloud
runs against latest build (0.3.1): 3/5 FAILED, every one of the 5 retry
attempts on page 1 got HTTP 403, regardless of proxy session rotation
or browser-impersonation rotation — logs show identical
RuntimeError: Cars.bg returned no usable HTML on page 1 after retries.
The residential-proxy guard from 0.3.0 was necessary but not sufficient:
even fresh RESIDENTIAL IPs get 403'd when a session jumps straight to
carslist.php with no prior request.
Fix: warm up each fetch session with a GET to the Cars.bg landing page
(https://www.cars.bg/) before the real request, on the same session so
cookies from the warm-up carry over — the anti-blocking-stack rule
("cookie-gated endpoints: landing page first, then the real request on
the same session") already mandated in DEVILSCRAPES-CONTEXT.md but not
implemented here. A failed warm-up is logged and non-fatal; the real
request is still attempted.
Add tests/test_fetch_warmup.py (3 tests): warm-up precedes the deep
fetch, warm-up is skipped when the target is the landing page, and a
broken warm-up doesn't block the real request.
Add tests/test_proxy_session_id.py (publish Gate 9 regression test —
was missing even though _new_session_id() was already regex-compliant).
Force-residential proxy guard: _resolve_proxy_config now always requests
the RESIDENTIAL group regardless of what the customer passes in
proxyConfiguration. If apifyProxyGroups is absent, empty, or contains
only datacenter groups, it is silently upgraded to ["RESIDENTIAL"].
Fail fast on unavailable proxy: instead of falling back to direct/datacenter
routing (which causes HTTP 403/503 on Cars.bg), the Actor now raises a
RuntimeError with an actionable message if create_proxy_configuration
returns None or raises. This surfaces the issue immediately rather than
producing empty datasets.
Add tests/test_proxy_guard.py with 7 unit tests covering the guard logic
and the fail-fast RuntimeError path.
_ensure_residential_groups extracted as a pure sync helper (testable
without the Apify SDK).
0.2.0 — 2026-06-05
Switch enrichDetails default from true to false (listing-only mode).
Cars.bg detail pages return HTTP 403 on many FREE-tier residential IPs;
keeping enrichment off-by-default prevents Apify auto-QA from timing out
with zero rows and being flagged "Under maintenance".
Update README to accurately describe enrichDetails as opt-in with a
rate-limit warning. Input table, example input JSON, Limitations section,
and FAQ entry all updated.
Fix test assertions to reflect enrich_details=False default.
Bump actor.json version 0.1 → 0.2 (Gate 10 requirement).