cd /news/developer-tools/show-hn-substack-skill-api-agent-ref… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-37910] src=github.com β†— pub= topic=developer-tools verified=true sentiment=Β· neutral

Show HN: Substack Skill API / Agent Reference

A developer released a comprehensive, verified reference for Substack's undocumented internal API, mapping 129 endpoints through 14 rounds of probing. The open-source repository includes an OpenAPI 3.1 spec, authentication guide, and client SDK generators, enabling AI agents and developers to interact with Substack's JSON API for reading posts, creating drafts, and managing publications.

read5 min views6 publishedJun 24, 2026
Show HN: Substack Skill API / Agent Reference
Image: source

The most complete map of Substack's undocumented internal API.

129 verified endpoints, captured by driving the live API through 14 rounds of probing and write-side capture.

A practical, verified reference for Substack's undocumented internal API. Every endpoint here has been tested against the live API. Designed for humans and for AI agents (see SKILL.md).

⚠️ Unofficial.Substack doesn't publish or support this API. Endpoints can change without notice. Treat this as a working notebook, not a contract.

The Substack web app speaks to a JSON API at https://substack.com/api/v1/*

and per-publication subdomains at https://<sub>.substack.com/api/v1/*

. The web app uses it for everything: reading posts, creating drafts, publishing, managing subscribers, sending chat threads, configuring recommendations. With a session cookie, you can drive the same API from any script.

Existing community work covers parts of this surface:

NHagar/substack_apiβ€” Python, read-focusedma2za/python-substackβ€” Python, full CRUDjakub-k-slys/substack-apiβ€” TypeScript (archived)JPres-Projects/Substack-APIβ€” Python, draft + publish

This repo is intended to be the canonical endpoint reference these clients converge on. Submit a PR with anything new you find.

COOKIE='s%3A...your.connect.sid.value...'

curl -H "Cookie: connect.sid=$COOKIE; substack.sid=$COOKIE" \
  https://substack.com/api/v1/user/profile/self | jq .handle

curl -X POST \
  -H "Cookie: connect.sid=$COOKIE; substack.sid=$COOKIE" \
  -H "Content-Type: application/json" \
  -d '{"draft_title":"Hello","draft_subtitle":"From the API","draft_body":"<p>Hi.</p>","type":"newsletter"}' \
  https://yourname.substack.com/api/v1/drafts

β€” every verified endpoint with body shapes, query params, and sample responsesENDPOINTS.md

β€”openapi.yaml

OpenAPI 3.1 spec(125 operations, 51 schemas) β€” scaffold a typed client in any languageβ€” getting the cookie, format, rotation, sending it from server-side codeAUTH.md

β€” Claude Agent SDK skill manifestSKILL.md

β€” drop-in curl scriptsexamples/curl/

β€” minimal typed clientexamples/typescript/

Drop-in commands for popular generators:

npx openapi-typescript openapi.yaml -o substack-types.ts

npx @openapitools/openapi-generator-cli generate -i openapi.yaml -g typescript-axios -o ./sdk-ts

npx @openapitools/openapi-generator-cli generate -i openapi.yaml -g python -o ./sdk-py

npx @openapitools/openapi-generator-cli generate -i openapi.yaml -g go -o ./sdk-go

Substack's endpoints split across two host families (account + per-pub) and the spec models both as servers

β€” set subdomain

when configuring the client.

Read side (passive β€” works with just a cookie):

  • Authentication, account discovery, public profile, blocked users
  • Publication CRUD (read), settings, sections, tags, recommendations, Stripe status, pledge tiers
  • Drafts, scheduled posts, published posts, post management counts
  • Per-post stats (31 engagement fields including open rate, CTR, daily breakdowns, referrers)
  • Publication-wide analytics (subscribers, ARR, network attribution, growth sources/events timeseries)
  • Subscribers list (filter/sort/paginate), DMs/messages inbox, activity feed
  • Reader inbox ( /inbox/top

), Notes feed + tabs, global search, search modules - Categories, reactions catalog, comment moderation enum, per-post mute settings

Write side (captured by driving the UI through Chrome):

PUT /publication

β€” single-field saves (name

,hero_text

,language

,welcome_email_content

, …)PUT /publication_settings

β€” boolean toggles (high-res video, AI training opt-out, cross-posting, etc.)- Drafts: create, update, delete, publish, schedule, scheduled_release

  • Notes: create ( POST /comment/feed

), delete, mark-seen - Reader comments: create, delete; post reactions(literal emoji +surface

) Recommendations: add (PUT /recommendations

), remove (DELETE /recommendations/

β€” note trailing slash), search, suggestedAudio/podcast uploadβ€” full S3 presigned multipart sequence (init β†’ S3 PUT β†’ transcode β†’ poll)** Substack Chat**: enable/disable (/publication_threads_settings

), send thread (client-generated UUID), delete thread- Co-author invites, subscriber add/remove, post-tag attach/detach, image upload

  • Generic user setting ( PUT /user-setting

)

Known gates (documented but won't be cracked here):

POST /publication

β€” captcha-gated by design- Custom domain config β€” $50 one-time + DNS setup

  • Magic-link verify β€” at /sign-in?token=...

, not under/api/v1/*

  • Moderator-delete-with-reason β€” needs another user's comment to surface

Each endpoint in ENDPOINTS.md is marked:

  • βœ… Verifiedβ€” personally tested against the live API - 🟑 Reportedβ€” documented by another client / blog post, not independently re-tested - ❓ Inferredβ€” pattern-matched from related endpoints, no successful call observed - ❌ Deadβ€” tested and confirmed 404, documented to save you the time - πŸ”’ Gatedβ€” exists but returns 403 from a plain curl (often works from a real browser; see the "two-host trick" and "browser-vs-curl gap" notes inENDPOINTS.md

)

PRs welcome to upgrade ❓ β†’ 🟑 β†’ βœ….

This reference was built over 14 progressive rounds of capture, documented as Round N

commits. The methodology stack:

Curl probing for read endpoints + empirical-error field discovery (sending intentionally-invalid POSTs to surface required fields via Substack's error messages β€” that's howtrigger_at

was found, and dozens of others)Playwright headless captureβ€” driving Chromium with a session cookie through the admin SPA to log everyapi/v1

request that firesChrome extension live capture(Claude in Chrome) β€” monkey-patchingwindow.fetch

andXMLHttpRequest.send

from alocalStorage

-backed log so request bodies survive React-driven page reloads, then doing add-then-revert cycles on real publication actions to capture the write-side bodies that couldn't be reached passively

Why all three: passive observation gets you read endpoints and a lot of POST bodies "for free." Empirical probing fills the gaps where errors are descriptive (Substack's are). Live driving the UI catches the cases where the body shape is non-obvious (the literal-emoji reaction value, the client-generated UUID for thread sends, the stringified-ProseMirror welcome email content) and where endpoints 403 from curl but 200 from a real browser session (recommendations was the canonical example).

Found a new endpoint? Test with curl, then PR with:

  • Method + path
  • Required host ( substack.com

vs{sub}.substack.com

) - Required headers / query params

  • Sample response (sanitize user data)
  • Classification (βœ… / 🟑 / ❓)
  • Date you verified it

Capture playbook for body shapes you can't get from curl:

  • Open Substack in Chrome β†’ DevTools β†’ Network β†’ filter to Fetch/XHR

  • Perform the action in the UI

  • The matching request appears in Network β†’ right-click β†’ Copy as cURL - Strip the cookie + headers down to the minimum that still works

  • PR the result here

This reference was assembled by Anthony David Adams and the EarthPilot.ai Lab β€” a small applied-AI lab building Mission Support for Spaceship Earth: tools and protocols for sense-making, decision-making, and coordination at planetary scale. Working on Substack-adjacent infrastructure (newsletter platforms, AI editorial pipelines, growth tooling) pushed us to map this surface; sharing it back is the natural thing to do.

If you're working at the edge of AI Γ— meaning Γ— infrastructure β€” or you want to be β€” come hang out at the ** Singularity Playground**. It's our open community for builders, researchers, and writers working on what comes next. Free to join, no pitch deck required.

MIT. See LICENSE.

── more in #developer-tools 4 stories Β· sorted by recency
── more on @substack 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/show-hn-substack-ski…] indexed:0 read:5min 2026-06-24 Β· β€”