# Changelog of Kickstarter Campaigns Scraper (`devilscrapes/kickstarter-campaigns-scraper`) Actor

- **URL**: https://apify.com/devilscrapes/kickstarter-campaigns-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/devilscrapes/kickstarter-campaigns-scraper.md

## Kickstarter Campaigns Scraper — Changelog

### 0.1.0 — 2026-08-02

- Implemented the real crawler (T02-T11 per
  `docs/specs/kickstarter-campaigns-scraper/tasks.md`), replacing the
  scaffold's placeholder `src/models.py` / `src/scraper.py`:
  - `src/models.py` — real `ActorInput` (category default `None`,
    `CategorySlug` / `SortLiteral` `Literal` types), `ResultRow`, nested
    `RewardTier`, and `CATEGORY_IDS` — all 15 slugs live-verified
    against `discover/advanced.json?category_id=` (T04, zero
    corrections needed; see `docs/specs/kickstarter-campaigns-scraper/notes.md`).
  - `src/parser.py` (new) — pure `parse_listing_page` (goal/pledged/
    percent-funded USD derivation, including the non-USD-currency and
    "derive when absent" paths) and `parse_detail_payload`
    (`window.current_project` regex + `html.unescape()` +
    `.encode().decode("unicode_escape")` + `json.loads()`, never raises
    — REQ-8).
  - `src/client.py` (new) — `KickstarterSession`: one curl-cffi
    `AsyncSession` per run, mandatory one-time cookie warmup GET to
    `/discover/advanced` (REQ-3), retry/backoff + browser-profile +
    proxy-session rotation on `403/408/429/503` (REQ-6 unioned with the
    repo-wide anti-blocking default — see `client.py`'s module
    docstring), `MAX_RETRIES=5`. `fetch_listing_page` raises on
    exhaustion; `fetch_project_detail` never raises (asymmetric
    contract, mirrors `mercari-sold-listings/src/client.py`).
  - `src/main.py` — rewritten: no separate `scraper.py` module (per
    design.md's "No Crawlee router is used"); the pagination loop lives
    in a testable `run()` async generator fed a structural
    `KickstarterFetcher` Protocol. Handles `max_items` budget
    trimming, `has_more` pagination stop, bounded-concurrency
    (`DETAIL_FETCH_CONCURRENCY=5`) detail-page enrichment when
    `include_reward_tiers=true`, batched `push_data` +
    `result-row`/`actor-start` PPE charges, and the zero-row
    fail-loud/`SystemExit(1)` contract (REQ-6, REQ-10).
  - `src/scraper.py` removed — not part of design.md's file plan.
- Added `tests/fixtures/advanced_page1.json` / `advanced_page2_empty.json`
  / `project_detail.html` / `project_detail_malformed.html` (real,
  trimmed Kickstarter recon data, 2026-08-02) and 55 unit/wiring tests
  across `test_models.py`, `test_parser.py`, `test_client.py`,
  `test_main.py`, plus an opt-in `test_integration.py` (`-m smoke`).
  `uv run pytest -q` and `uv run ruff check src/ tests/` both green.
- `tests/fixtures/input.qa.json` unchanged and still validates against
  the real `ActorInput.model_validate`.

### 0.0.1 — 2026-08-02

- Scaffolded: `.actor/` (`actor.json` version `0.0`,
  `categories: ["LEAD_GENERATION", "MARKETING"]`, `seoTitle` /
  `seoDescription` set, `input_schema.json` / `dataset_schema.json` /
  `output_schema.json` stubs matching `design.md` field-for-field,
  `pay_per_event.json` with the spec's two events (`actor-start`
  $0.02, `result-row` $0.0024), `Dockerfile`), `src/main.py` wiring
  (reads input, resolves proxy, pushes one clearly-marked placeholder
  record via `{"placeholder": true, ...}`, charges `result-row` — not
  the scaffolder's generic `result` slug — and exits clean),
  `src/__init__.py` / `src/__main__.py`, test scaffold
  (`tests/test_input.py`), the QA fixture (`tests/fixtures/
  input.qa.json`, `category=technology`/`maxItems=3`/
  `includeRewardTiers=true`), README skeleton, `scaffold.json`.
- `.actor/icon.svg` deferred to `actor-icon-designer` (Kickstarter
  brand mark inside the canonical Devil Scrapes container).
- Added throwaway placeholder `src/models.py` (bare `ActorInput`/
  `ResultRow`, no `RewardTier` nested model or `CATEGORY_IDS` map yet)
  and `src/scraper.py` (yields one placeholder `ResultRow` instead of
  raising `NotImplementedError`, so the scaffold satisfies the
  boot/read-input/write-record/exit-clean contract end to end). Both
  are fully replaced, not extended, by the implement phase's
  `src/models.py` (real `RewardTier` + `CATEGORY_IDS`) and
  `src/scraper.py` (real pagination loop) per
  `docs/specs/kickstarter-campaigns-scraper/design.md` and
  `tasks.md`.
- Real crawler logic (`src/client.py`, `src/parser.py`, the real
  `src/models.py` / `src/scraper.py`) is not implemented yet — see
  `docs/specs/kickstarter-campaigns-scraper/tasks.md`.
