Skip to content

About

Push Markdown files to Substack as editor drafts via python-substack. Hero upload, section assignment, dry-run. Never publishes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

md2substack — push Markdown drafts to Substack (no official API needed)

Substack has no official write API. md2substack pushes a Markdown file to your Substack publication as an editor draft using python-substack and your own session cookie. It never publishes and never sends email: the Publish button stays human.

Built and used in production to run the AI Jungle Weekly newsletter rail (drafts written by agents, reviewed and published by a human).

What it does

  • Parses a Markdown issue file: YAML frontmatter + # SUBJECT: + PREVIEW: + body
  • Uploads a hero image (hero_image: in frontmatter) and inserts it on top
  • Converts the Markdown body (headings, bold, links, lists, dividers) to Substack's editor format
  • Assigns the draft to a publication section (e.g. a sub-newsletter) post-creation
  • Refuses to push if unresolved {{PLACEHOLDER}} markers remain
  • --dry-run parses and validates without any network call

Setup

  1. pip install python-substack
  2. Export your substack.sid session cookie from a logged-in browser to ~/.config/md2substack/cookies.json:
    {"substack.sid": "<your session cookie value>"}
  3. Create ~/.config/md2substack/config.json:
    {
      "cookies_path": "~/.config/md2substack/cookies.json",
      "publications": {
        "myblog":  {"url": "https://myblog.substack.com", "section": null},
        "mybrand": {"url": "https://myblog.substack.com", "section": "My Section"}
      }
    }

Issue file format

---
brand: myblog
hero_image: ./hero.png
---

# SUBJECT: The stopwatch found what 261 tests missed
PREVIEW: How we killed a six second boot flash.

## First section

Body in **Markdown** with [links](https://example.com), lists and dividers.

Usage

python md2substack.py issue.md --dry-run   # validate only
python md2substack.py issue.md             # create the Substack draft
# -> DRAFT CREATED (not published): https://myblog.substack.com/publish/post/123456

Gotchas learned in production

  • The editor stores the body as ProseMirror JSON; bare URLs are NOT auto-linked, write [text](url).
  • Section assignment via the library is unreliable; this script PUTs draft_section_id after creation, which works.
  • To edit an existing draft in place: GET /api/v1/drafts/<id>, rebuild the body, PUT it back (keep the first image node to preserve your hero).

MIT license. No affiliation with Substack.

About

Push Markdown files to Substack as editor drafts via python-substack. Hero upload, section assignment, dry-run. Never publishes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages