#!/usr/bin/env python3 """Read a Korean catalog segment and keep only ordinary sale listings. APIAUTO_KEY=... python outreach/articles/02-catalog-demo.py --brand Kia --year-min 2019 The script is read-only. It demonstrates cursor pagination and client-side sell_type filtering; it is not a background sync worker or an availability guarantee. Each returned car consumes one API record. """ import argparse import json import os import sys import urllib.error import urllib.parse import urllib.request BASE = os.environ.get("APIAUTO_URL", "https://api.apiauto.space").rstrip("/") def get(path: str, **params: object) -> dict: key = os.environ.get("APIAUTO_KEY") if not key: raise RuntimeError("Set APIAUTO_KEY in the environment") query = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None}) request = urllib.request.Request( f"{BASE}{path}?{query}" if query else f"{BASE}{path}", headers={"X-API-Key": key, "User-Agent": "apiauto-article-02-demo"}, ) with urllib.request.urlopen(request, timeout=30) as response: return json.load(response) def ordinary_sale(car: dict) -> bool: """Unknown deal types are withheld until somebody reviews them.""" return ( car.get("is_active") is True and (car.get("raw") or {}).get("sell_type") == "normal" and bool(car.get("price_usd")) and float(car["price_usd"]) > 0 ) def find_sales(brand: str, year_min: int, wanted: int, page_size: int) -> tuple[list[dict], int]: sales: list[dict] = [] cursor = None read = 0 while len(sales) < wanted: page = get( "/v1/cars", market="kr", brand=brand, year_min=year_min, sort="price", limit=page_size, cursor=cursor, ) items = page["items"] read += len(items) sales.extend(car for car in items if ordinary_sale(car)) cursor = page.get("next_cursor") if not cursor: break return sales[:wanted], read def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--brand", default="Kia") parser.add_argument("--year-min", type=int, default=2019) parser.add_argument("--wanted", type=int, default=20) parser.add_argument("--page-size", type=int, default=20, choices=range(1, 101), metavar="1..100") args = parser.parse_args() if args.wanted < 1: parser.error("--wanted must be positive") try: sales, read = find_sales(args.brand, args.year_min, args.wanted, args.page_size) except (RuntimeError, urllib.error.HTTPError, urllib.error.URLError, TimeoutError) as exc: print(f"Request failed: {exc}", file=sys.stderr) return 1 print(f"Read {read} cards; found {len(sales)} ordinary sale listings.") for car in sales: print(f"{car['id']} {car['title']} ${car['price_usd']} {car.get('listing_url') or ''}") return 0 if __name__ == "__main__": raise SystemExit(main())