Source: https://www.doyle.com — /past-auctions/ (closed sales with date) → /auction/search/?au=<id>&pp=96&pn=N&g=1 (server-rendered result cards).
Engine: api (plain HTTPS, honest UA, 10 s crawl-delay per robots.txt). 0 credits.
Records: sale — price = "Sold for $X" (USD, buyer's premium included per the lot pages), saleDate = sale date from the calendar, lot number, estimate in metadata, identifiers.doyle_lot_id.
Categories: sale name → department hint, refined by title keywords (doyleCategory). Unmapped lots are skipped.
Cursor: { doneAuctions: string[] }; backfill walks the calendar oldest→newest and reports progress; done: true when every listed sale is processed.
Smoke: set -a; . ./.env; set +a; pnpm tsx connectors/api/_g7-auctions-na-lib/smoke.ts api/doyle --capture results --limit 2