substack-export
Export every public post (titles, dates, word counts, full body text) from any Substack publication via its public archive API — no login. Use to pull or back up a newsletter, build an archive page, o
SKILL.mdskills/substack-export/SKILL.md
GitHubSubstack export
Substack exposes a public JSON archive API per publication. No auth, no HTML scraping.
https://<sub>.substack.com/api/v1/archive?sort=new&limit=50&offset=N # metadata, paginated
https://<sub>.substack.com/api/v1/posts/<slug> # full body_html
Run it
python3 substack_export.py --sub <subdomain> [--sub <another>] [--out substack_posts.json]
Per post: {sub, title, subtitle, date, slug, wordcount, id, url, audience, body}, sorted
oldest -> newest, bodies stripped to plain text.
Traps
- The archive caps a page at ~24 rows whatever
limitasks for. A loop that stops when a page is shorter thanlimitsees only the first page (e.g. 23 of 51 posts). The only valid stop is an EMPTY page, and the offset advances by rows returned, never by the limit asked. The script does this. - Only currently-public posts are returned. Posts you archived or unpublished don't appear; for a full history you need your own export (Settings -> Exports) or another copy.
- Preformatted blocks carry hidden editor boilerplate ("Text within this block will maintain
its original spacing when published") inside a
hide-textlabel. The stripper drops it; a naive tag-strip turns it into fake teaser text. - Paywalled posts return only the free preview in
body_htmlunless you're authenticated.