Skip to content

feat(subscribers): add pagination to stats command - #10

Open
griffincapmisc wants to merge 2 commits into
griffinwork40:mainfrom
griffincapmisc:main
Open

feat(subscribers): add pagination to stats command#10
griffincapmisc wants to merge 2 commits into
griffinwork40:mainfrom
griffincapmisc:main

Conversation

@griffincapmisc

Copy link
Copy Markdown

Syncs the pagination feature from griffincapmisc#1.

Changes

  • subscribers stats now auto-paginates to fetch all subscribers (not just first 25)
  • Added --offset / --limit CLI flags for manual paging
  • Progress logging to stderr every 500 records
  • 7 subscriber-specific tests (294/294 total passing)

Behavior

  • substack subscribers stats → fetches all ~22,329 subscribers across ~893 pages
  • substack subscribers stats --limit 25 → single-page (original behavior)
  • substack subscribers stats --offset 100 --limit 50 → manual paging

griffincapmisc and others added 2 commits August 22, 2026 14:41
Co-authored-by: Capital Mischief <capitalmischief@CapitalacStudio.lan>
Add `substack subscribers export` — a new command that triggers Substack's
async CSV export job, polls until complete, and downloads the result. This is
significantly faster than paginating `subscriber-stats` 25–100 rows at a time
for large subscriber lists (gap griffinwork40#4 in FEATURE-GAPS.md).

## What changed

### client.py
- Add `SubstackClient.download(url: str) -> bytes`: fetches an absolute URL
  (e.g. pre-signed S3 link) and returns raw bytes. Throttled, raises
  SubstackApiError on non-2xx or transport errors.

### read.py
- Add `trigger_csv_export(client)`: POST /api/v1/publication_export with
  empty body {}. Returns the raw API response (job object).
  TODO: body shape unverified — empty {} tried first per spec; see comment if
  server returns 400.
- Add `poll_csv_export(client, timeout_s=300)`: GET /api/v1/publication_export
  in a polling loop until a complete job with download_url appears. Tolerates
  bare-list, {exports:[...]}, and single-job-dict response shapes. Raises
  SubstackApiError on terminal failure or timeout.
- Add `download_csv(client, download_url)`: delegates to client.download().
- Add `@subscribers_app.command("export")` CLI command:
    `substack subscribers export [--output FILE] [--timeout N]`
  Writes CSV to stdout by default; --output FILE writes to disk + emits JSON
  confirmation. Progress printed to stderr throughout.

### tests/test_read_csv_export.py (new, 15 tests)
- trigger: POST body shape, API error propagation
- poll: immediate-complete, envelope shapes, multi-job recency, pending-then-
  complete retry, failed-job terminal, timeout, unexpected-shape errors
- download: happy path, 404 propagation
- CLI: stdout path, --output FILE path, trigger-failure exit-1

## Test results
332 passed, 0 failed (full suite)

## Confidence note
POST /api/v1/publication_export endpoint is community-confirmed; the trigger
body shape ({}) and exact response field names (status, download_url) are
**medium confidence** — unverified against a live response. Re-probe with
DevTools capture on the Substack dashboard export page before relying on this
in production. See FEATURE-GAPS.md griffinwork40#4 and references/substack-api.md for
the full confidence note.

Co-authored-by: Capital Mischief <capitalmischief@CapitalacStudio.lan>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant