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

[!IMPORTANT] Creating and publishing are separate operations. substack drafts create always creates an unpublished draft. It never schedules, sends, publishes, or deletes content.

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.
  • 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 --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.

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_substack-0.5.0.tar.gz (28.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_substack-0.5.0-py3-none-any.whl (30.1 kB view details)

Uploaded Python 3

File details

Details for the file python_substack-0.5.0.tar.gz.

File metadata

  • Download URL: python_substack-0.5.0.tar.gz
  • Upload date:
  • Size: 28.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for python_substack-0.5.0.tar.gz
Algorithm Hash digest
SHA256 b5b28bfc7932091c542c8c840d5c16c92a754aa5098cbb516b3a708d91bb443b
MD5 20f8cf1cad9eaf4ac57bbf9ce06cc084
BLAKE2b-256 e4aaa9888e13c6973937a68cc0381d755b266952c3f4c35670afd1e719a0cbe3

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_substack-0.5.0.tar.gz:

Publisher: ci_publish.yml on ma2za/python-substack

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_substack-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: python_substack-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 30.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for python_substack-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5565baa09cce92fc8c03ec24b4fae55dda2791b5dc8d9dce7eda76b63aeb51ea
MD5 aef6d6808d056868ad2b8311b7d021b5
BLAKE2b-256 f9df7d3e6b02e460e9c6bf51a1e5ae21c8e105f5f7430bebe6ac1063ed5a491d

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_substack-0.5.0-py3-none-any.whl:

Publisher: ci_publish.yml on ma2za/python-substack

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.7.0

2 files

0.6.0

2 files

This release

0.5.0 This release

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.27

2 files

0.1.26

2 files

0.1.25

2 files

0.1.24

2 files

0.1.23

2 files

0.1.22

2 files

0.1.21

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

0.1.17

2 files

0.1.16

2 files

0.1.15

2 files

0.1.14

2 files

0.1.13

2 files

0.1.12

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 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