Adds Substack Toolkit to the Communication & Writing section: a Claude Skill that reads and writes Substack publications from Markdown or a Python builder API. Full coverage of the practical Substack workflow: drafts, posts, archive, reader feed, profiles, Notes (read + publish), likes, comments, subscribers.
Works around nine undocumented private-API gotchas that make naive integrations silently fail.
Write — long-form drafts:
/api/v1/image)draft_bylines: null workaround built inWrite — Substack Notes:
publish_note(text) — plain Note from a stringpublish_note(doc) — rich Note from the same Doc builderpublish_note(text, attachment_url=url) — two-step attachment-then-publish flow that produces a link cardbodyJson.attrs.schemaVersion injection (silent failure without it)Engagement (library-only, no CLI on purpose):
like_post(post_id), like_note(note_id), comment_on_post(post_id, body)Read:
feed() + iter_feed(max_items) — reader feed for the authenticated user, with cursor pagination, 429 retry, inter-page sleeparchive(sort, limit, offset) + iter_archive(page_size) — published archive of any publicationget_post(slug_or_id) — full post including body_html, unwraps the {post, publication, subscription, ...} envelopeuser_public_profile(handle) — id, name, bio, subscriberCount for any user by @handlenotes_for_user(user_id, cursor) + iter_notes_for_user(user_id, max_items) — paginated Notes for any user with reaction_count / children_count / restackspublication() — name, hero, bylines, custom domainsubscribers_csv(out_path) — owner-only CSV export with documented Playwright fallback for accounts gated behind dashboard JSuser_self() — documented as flaky (some accounts always 403; use the user_id property instead)Helpers:
safe_text(s) — defensive ASCII transliteration for the small number of accounts whose editor still chokes on em-dashes / smart quotes / emojiCLI subcommands (all reads, write side-effects intentionally library-only):
auth, post, list, delete, whoami, publication, archive, get, subscribers, feed, whois, notes.
Substack's editor is built on a schema-strict ProseMirror validator and its writing API is undocumented. Nine traps each consume an afternoon the first time you hit them:
rawHtml node — API accepts it with 200; editor crashes opening the draft with "Something has gone wrong."/api/v1/image multipart upload — rejected with 400 "Invalid value". Needs JSON {"image": "data:image/png;base64,..."}.draft_bylines: null on draft PUT — GET returns null bylines, PUT requires non-null./api/v1/user/self 403 — some accounts always get 403 here.get_post envelope — response wraps the post in {post, publication, ...}; easy to miss body_html./api/v1/subscribers/export works for most owners; some need Playwright.notes_for_user filter bug — the obvious ?types[]=comment filter silently returns zero items.bodyJson.attrs.schemaVersion is missing.This skill packages all nine workarounds into a single file.
SKILL.md — full When-to-Use / What (Write+Read+Publish-Notes+Engage+Helpers) / How (Basic + Advanced + CLI + Library) / Example / Tips / Common Use Cases (Write+Read+Notes+Engage) / Hall of Shame / Roadmap sections per the CONTRIBUTING templatescripts/substack_draft.py — single-file Python library + CLI (~1300 lines, deps: requests, markdown-it-py)resources/example.md — feature-demonstration Markdown for end-to-end testingAll read methods verified end-to-end against mphinance.substack.com:
publication() returned correct name and heroarchive(limit=5) returned 5 most recent postsget_post(198832063) returned 29,808-byte body_htmliter_archive(page_size=3) walked pages correctlywhois @mphinance returned profile (id 108093971, 1K+ subs)feed --limit 3 returned 3 notes from followed publicationsnotes --user @stonemountainresearch returned 3 notes with correct body excerpts and reaction countsuser_self() 403'd as documented; client.user_id fallback resolved correctlyWrite path was previously verified by drafting and updating multiple real posts.
Engagement methods (like_post, like_note, comment_on_post, publish_note) were not live-tested to avoid side effects on the author's public profile; endpoint shapes and request bodies were lifted verbatim from a working TypeScript SDK in the upstream codebase.
Triggers on read or write prompts: "post this markdown to Substack", "draft a Substack article", "publish a Substack note", "list my Substack posts", "read my Substack feed", "fetch the body of post 12345678", "look up @someone on Substack", "show me dickcapital's notes", "like that post", "comment on this post saying X", "export Substack subscribers".
Explicit non-triggers: restack (roadmap), general marketing strategy advice (this is an API tool), bulk auto-engagement loops (the engagement methods are explicit-call only on purpose).
Add [Skill Name] skill[Skill Name](./skill-name/) - description. *By [@user](url)*name and descriptiongithub.com/mphinance/substack-toolkit — tagged v0.3.0. For users who want to pin a version, file issues, or follow updates outside the awesome-list.
Happy to make changes — naming, scope, placement, formatting — based on review.