Skip to main content

Python Substack

Write Substack posts in Markdown and safely create, inspect, schedule, and publish them through Python, a command-line interface, or MCP.

PyPI Python Tests Release License Downloads

Documentation · Getting started · PyPI

From Markdown to a Substack draft

Install the package:

pip install python-substack

Check the selected account and publication:

substack status

Create a safe unpublished draft, then publish only when it is ready:

substack drafts create post.md
substack drafts publish 12345 --no-send

Publishing and deletion require confirmation. Noninteractive and JSON workflows must pass --yes explicitly.

Markdown source:

Markdown before conversion

Substack result:

Substack after conversion

What it supports

  • Create rich Substack drafts from Markdown.
  • Upload local images referenced by Markdown.
  • Set audience, comment permissions, SEO metadata, slug, sections, and tags.
  • List and inspect publications and drafts.
  • Export drafts to loss-aware Markdown backups without server writes.
  • Safely update drafts from Markdown, preserving unsupported editor widgets by default.
  • Schedule, unschedule, publish, and delete drafts with explicit safeguards.
  • Use stable JSON envelopes in scripts and automation.
  • Authenticate with browser cookies or email and password.
  • Use the same publishing workflow from Python or an optional MCP server.

Setup

Copy .env.example to .env and configure one authentication method:

EMAIL=
PASSWORD=
PUBLICATION_URL=
COOKIES_PATH=
COOKIES_STRING=

Cookie authentication is usually more reliable when Substack requires captcha or magic-link sign-in. See Authentication for cookie export instructions and account-selection details.

Verify the installation without authenticating:

substack --version
substack --help

CLI

Create a draft with metadata:

substack --json drafts create post.md \
  --title "My Post" \
  --subtitle "Optional subtitle" \
  --tag python \
  --tag substack \
  --slug my-post \
  --search-engine-title "SEO title" \
  --search-engine-description "SEO description"

Inspect publications and drafts:

substack publications list
substack drafts list --limit 10
substack drafts get 12345
substack drafts export 12345 --output backup.md
substack drafts update 12345 revised-post.md --dry-run --yes
substack --publication-url https://example.substack.com drafts list

Manage scheduling:

substack drafts schedule 12345 --at 2026-08-01T09:00:00+03:00
substack drafts unschedule 12345

Publish or delete intentionally:

substack drafts publish 12345 --no-send
substack drafts delete 12345 --yes

Global options such as --json, --cookies, and --publication-url must appear before the command:

substack --json drafts list
substack --cookies cookies.json --json status

The original standalone commands remain supported. See Legacy CLI commands.

Python

import os

from dotenv import load_dotenv
from substack import Api

load_dotenv()

api = Api(
    email=os.getenv("EMAIL"),
    password=os.getenv("PASSWORD"),
    publication_url=os.getenv("PUBLICATION_URL"),
)

result = api.create_draft_from_markdown(
    title="Shipping with Python",
    subtitle="A short note from a script",
    markdown="""
# Hello

This draft was created from **Markdown**.

![Alt text](https://example.com/image.png "Image caption")
""",
    tags=["python", "automation"],
    slug="shipping-with-python",
)

print(result["draft"]["id"])

create_draft_from_markdown creates a draft by default. It publishes only when publish=True is passed.

Back up an existing draft without modifying it:

backup = api.export_draft_to_markdown(12345)
print(backup["markdown"])
print(backup["unsupported_nodes"])

Review a Markdown update before writing it, then update deliberately:

result = api.update_draft_from_markdown(12345, "# Revised draft", dry_run=True)
result = api.update_draft_from_markdown(12345, "# Revised draft")

Unsupported editor widgets exported as python-substack-node:v1 markers are preserved by default. An update refuses to remove or alter them unless allow_unsupported_change=True is explicitly passed.

For direct ProseMirror node construction, see the low-level Python API. YAML workflows are documented in YAML drafts.

Markdown

Supported Markdown includes headings, paragraphs, bold, italic, inline code, strikethrough, superscript, subscript, links, images, linked images, image captions, code blocks, blockquotes, ordered and unordered lists, horizontal rules, footnotes, LaTeX math, pull quotes, and callouts.

from substack.post import Post

post = Post("Title", "Subtitle", user_id=1)
post.from_markdown(
    """
# Heading

Paragraph with **bold**, *italic*, `code`, and [links](https://example.com).
"""
)

Pass api= to upload local images while rendering:

post.from_markdown(markdown_content, api=api)

See the complete Markdown reference.

MCP

Install and run the optional MCP server:

pip install "python-substack[mcp]"
substack-mcp

The MCP tools use the same environment variables and SDK behavior as the CLI. See MCP server for the tool list and safety notes.

Project documentation

Compatibility

The project preserves existing Python APIs, console commands, CLI behavior, environment variables, JSON keys, and MCP tool signatures through the 1.x series. Additive capabilities may be introduced. See the compatibility policy.

Disclaimer

This project is not affiliated with Substack. It uses undocumented Substack interfaces that may change without notice.

Release files for python-substack 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for python-substack 0.9.0
File Size Uploaded
python_substack-0.9.0.tar.gz 35.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for python-substack 0.9.0
File Interpreter ABI Platform
python_substack-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 72.0 kB

Release files / python_substack-0.9.0.tar.gz

Download URL python_substack-0.9.0.tar.gz
Size 35.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c828d8adda42ca3abe8f563812f60971a30efee72ac6e7d2c6cda57276536bd5
BLAKE2b-256 checksum
How to use checksums
2a500c4d32a7585460c54b6792ca3022bbf877cdb16fc7bee23cc65800b89a56
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / python_substack-0.9.0-py3-none-any.whl

Download URL python_substack-0.9.0-py3-none-any.whl
Size 36.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9a79ac3b7606b57381df61a5120f7d15f2191de7089f33017cabf20a8f5ff775
BLAKE2b-256 checksum
How to use checksums
52a6642ca20094ac741e01d2be2d28a91fbf0638eb56fc56fbe5a28d8b6d7b9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.27

2 release files

0.1.26

2 release files

0.1.23

2 release files

0.1.21

2 release files

0.1.20

2 release files

0.1.19

2 release files

0.1.17

2 release files

0.1.16

2 release files

0.1.15

2 release files

0.1.13

2 release files

0.1.12

2 release files

0.1.11

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page