Substack to clean Markdown.
Bulk-export Substack posts as Obsidian-friendly Markdown using your authenticated browser session : no passwords, no copy-paste.
Substack does not let you bulk-export your reading list or subscriptions in any useful format. substack2md uses your logged-in browser via the Chrome DevTools Protocol to convert posts to clean Markdown with YAML frontmatter, image-and-embed-as-link rewriting, and [[Publication/YYYY-MM-DD-slug|Original label]] wikilinks for Obsidian.
What it does
Run substack2md against a single post URL or a text file of URLs. It opens a Chrome tab against your local browser, reads the rendered post HTML, and writes Markdown to disk organized by publication. The tool does not read or export session cookies. Your browser authenticates its normal requests to Substack.
Browser-session auth
Uses an isolated Chrome or Brave profile via the Chrome DevTools Protocol. No passwords stored, no Substack credentials handled by the tool.
Obsidian-friendly output
YAML frontmatter, [[Publication/YYYY-MM-DD-slug|Original label]] wikilinks for cross-references, images and embeds rewritten as plain links so your vault stays portable.
Polite and configurable
Sequential by default, with optional parallel workers across publications. Configurable delays, publication folders, transcript cleanup, and optional paywall metadata.
Designed for
| You want to | substack2md does |
|---|---|
| Archive your own newsletter | Converts the post URLs you supply, individually or in a text file. Paywall metadata is opt-in. |
| Read newsletters offline in Obsidian | Saves article text with frontmatter and wikilinks. Linked images and embeds still require network access. |
| Build a personal corpus for search or LLM context | Plain Markdown, one file per post, structured filenames you can grep. |
| Stop accidentally sharing paid content | Optional paywall detection writes is_paid and audience fields to frontmatter. |
Install
Requires Python 3.10+ and a Chrome / Chromium browser. Start Chrome with the remote debugging port, log in to Substack, then run substack2md.
Homebrew on macOS or Linux:
Literal
brew install snapsynapse/tap/substack2md
Install from source (macOS/Linux)
Literal
git clone https://github.com/snapsynapse/substack2md.git cd substack2md python3 -m venv .venv source .venv/bin/activate python -m pip install .
Start the browser from a source checkout
On macOS, run the helper in the cloned repository, then log in to Substack in the browser it opens.
Literal
./launch-browser.sh
For manual commands and other platforms, see the Quick Start.
Convert a single post
Replace: POST_URL -> the full HTTPS URL of your Substack post.
Customize
substack2md POST_URL
Batch from a URL list
Replace: URLS_FILE -> the path to your text file of post URLs. Quote paths containing spaces.
Customize
substack2md --urls-file URLS_FILE
See urls.txt.example in the repo for the expected format.
This page documents version 2.2.0: reliable capture recovery, explicit failure outcomes, and a maintenance-only contribution scope. Check GitHub Releases for published artifacts. See the maintenance policy and release checks; source changes do not imply a published release.
AI-assisted install
substack2md ships a GuideCheck human-verifiable assistant guide (profile 0.3.0) for bounded local installation under explicit human approval. Verify the exact guide with a conformant verifier and match its SHA-256 to the local copy before letting the assistant execute an [action] block. Copy/hash tests alone do not establish a hosted conformance level.
- · Canonical:
https://substack2md.space/.well-known/assistant-guide.txt - · Repo path:
.well-known/assistant-guide.txt - · Manifest: served integrity manifest (repository source:
assistant-guide-manifest.txt)
Output shape
Each post becomes a Markdown file with YAML frontmatter Obsidian and most static-site generators can read directly. This example includes optional paywall metadata from --detect-paywall.