Scrape normalized handbag and accessory resale listings from shop.rebag.com — clean designer, condition, price, SKU, image, and spec data per variant, straight from the public storefront feed.
Initial implementation. Scrapes Rebag's public Shopify storefront JSON feed
(shop.rebag.com) via three tiers: collection browse (guessed from a
free-text query or an explicit collectionHandle), a free-text
search/suggest.json fallback (capped at 10 results) when the collection
guess comes back empty, and a full-catalog products.json browse-all mode
when neither is set. Emits one ResultRow per product variant (price, SKU,
availability live at the variant level), with page- and product/variant-level
fault isolation (REQ-10) and run-wide dedup by listing_id (REQ-14). A
zero-match run completes SUCCEEDED with a descriptive status message
(REQ-12) — the org-wide "empty is not a failure" rule, not the older
SystemExit(1)-on-zero-rows pattern used by some earlier Actors in this
fleet.
Wire format live-confirmed against shop.rebag.com twice: once during
spec authoring, and again during implementation, which surfaced and fixed
two real discrepancies between the documented shape and the live response:
search/suggest.json returns a flattened product shape with an
always-empty variants: [] — the initial implementation built rows via
product.variants for this path (borrowed from the primary shape) and
would have silently returned zero rows on every real fallback
invocation. Fixed with a dedicated parser.build_suggest_row for this
endpoint's shape.
The bc-filter-<Condition> tag-fallback (REQ-8, used when a variant's
option1 lacks the " | Item # " separator) took the firstbc-filter- tag it found, but real products carry dozens of bc-filter-*
facet tags (price band, material, location, promo) — verified this
frequently misidentified a non-condition facet as the condition. Fixed
with a live-probed allowlist used only for tag matching; condition
itself stays free-text per spec.md.
PPE: actor-start ($0.05, charged once after input validation succeeds)
and result-row ($0.0012 per pushed row, deduped) — charge event names
verified to match .actor/pay_per_event.json's keys exactly (pinned by a
regression test).
90 tests (uv run pytest), ruff check clean, pyright clean. HTTP via
curl-cffi only (no browser automation — the target has no anti-bot
posture in the live probe).