A fully functional Python-based Substack API client that enables programmatic creation, management, and publishing of Substack posts with rich formatting support. Available as both command-line tools and HTTP REST API.
- Draft Creation: Create drafts with rich formatting using simple markup syntax
- Draft Publishing: Interactive publishing with email/audience options
- Rich Content: All Substack content types supported (headings, lists, quotes, buttons, etc.)
- User-Friendly Markup: Write formatted content using simple syntax
- Environment Management: Easy credential setup and management
- π HTTP API: REST API server for integration with any programming language
- π Web Interface: Interactive API documentation and testing
- Scheduling: Cannot create new schedules via API (can only update existing ones)
- Media Upload: Images/audio/video require separate upload process
# Install dependencies
pip install -r requirements.txt
# Configure credentials
python change_env.py
# Start API server
python api_server.pyServer URL: http://localhost:8000
Interactive Docs: http://localhost:8000/docs
# Install requirements
pip install requests python-dotenv
# Configure credentials
python change_env.pyYou'll need these credentials from your Substack account:
- Publication URL: Your Substack homepage (e.g.,
https://yourname.substack.com) - User ID: Your Substack username
- Authentication Cookies: Extract from browser (see guide below)
- Login to your Substack account in browser
- Open Developer Tools (F12)
- Go to Application/Storage β Cookies β https://substack.com
- Find and copy these cookie values:
sidsubstack.sidsubstack.lli
API Method (Recommended):
# Create draft via HTTP API
curl -X POST "http://localhost:8000/drafts/create-markup" \
-H "Content-Type: application/json" \
-d '{
"title": "My First Post",
"markup_content": "Title:: Hello World | Text:: This is **bold** content | Subscribe:: Join Newsletter"
}'Command Line Method:
python draft_create.pyChoose from these options:
- Simple text draft - Basic text content
- Markup draft - Rich formatted content (reads from
sampleinput/2.txt) - Test draft - Comprehensive example with all content types
- Rich formatting - Basic formatting example
Create rich Substack content using simple markup:
Title:: Your Main Heading | Text:: Your content with **bold** and *italic* | Subscribe:: Join Newsletter
Title::- Main heading (H1)Subtitle::- Secondary heading (H2)H1:: - H6::- Custom heading levelsText::- Paragraph with inline formattingQuote::- Block quotePullQuote::- Emphasized quoteList::- Bullet list (β’ separated)NumberList::- Numbered list (1. 2. 3.)Code::- Code block (language | code)Rule::- Horizontal dividerButton::- Custom button (text -> url)Subscribe::- Subscribe buttonShare::- Share buttonComment::- Comment buttonSubscribeWidget::- Subscribe with description (button >> description)LaTeX::- Mathematical equationsBreak::- Empty paragraph/line break
Within Text:: blocks:
**bold**- Bold text*italic*- Italic text~~strikethrough~~- Strikethrough text`code`- Inline code[link text](url)- Links
Title:: Market Analysis Report | Text:: Our analysis shows **strong growth** in tech stocks. Key findings: | List:: β’ Revenue up 25% β’ User growth at 40% β’ New product launches | Quote:: This represents the strongest quarter in company history | Subscribe:: Get Weekly Reports
API Method:
# List drafts
curl "http://localhost:8000/drafts"
# Publish specific draft
curl -X POST "http://localhost:8000/drafts/123456/publish" \
-H "Content-Type: application/json" \
-d '{"send_email": true, "audience": "everyone"}'Command Line Method:
python draft_publish.py- Lists all unpublished drafts with previews
- Choose draft and publishing options
- Publishes immediately with confirmation
substack-api/
βββ api_server.py # π HTTP REST API server
βββ requirements.txt # π API dependencies
βββ API_GUIDE.md # π Complete API documentation
βββ draft_create.py # Create drafts with markup support
βββ draft_publish.py # Publish existing drafts
βββ change_env.py # Manage environment credentials
βββ getposts.py # Analyze all posts and drafts
βββ sampleinput/2.txt # Sample markup content
βββ docs/ # Documentation and guides
βββ .env # Your credentials (create this)
βββ README.md # This file
Perfect for:
- Web applications
- Mobile app backends
- CI/CD pipelines
- Third-party integrations
- Remote automation
Perfect for:
- Local automation
- Quick testing
- One-off posts
- Development workflow
Authentication Errors
- Refresh your browser cookies - they expire regularly
- Run
python change_env.pyto update credentials
Draft Creation Fails
- Ensure you have at least one unpublished draft (create manually first)
- Check that your
.envfile has all required values
Content Not Displaying
- Fixed: Was caused by empty text elements in markup parser
- Current version filters out problematic empty elements
Analyze existing posts:
python getposts.pyView markup examples:
python docs/markup_examples.pyimport requests
# Create draft via API
response = requests.post("http://localhost:8000/drafts/create-markup", json={
"title": "My Post",
"markup_content": "Title:: Hello World | Text:: This is **bold** content",
"subtitle": "Optional subtitle"
})
draft = response.json()
print(f"Created draft {draft['draft_id']}")
# Publish draft
requests.post(f"http://localhost:8000/drafts/{draft['draft_id']}/publish", json={
"send_email": True,
"audience": "everyone"
})from draft_create import create_markup_draft
# Create draft from markup
draft = create_markup_draft(
title="My Post",
markup_content="Title:: Hello World | Text:: This is **bold** content",
subtitle="Optional subtitle"
)Required in .env file:
PUBLICATION_URL=https://yourname.substack.com
USER_ID=your_username
SID=cookie_value
SUBSTACK_SID=cookie_value
SUBSTACK_LLI=cookie_value
The API client works by:
- Using session-based authentication with browser cookies
- Copying structure from existing unpublished drafts
- Converting markup syntax to Substack's JSON content format
- Making authenticated requests to Substack's internal API endpoints
Key technical notes:
- Empty text elements break Substack's parser (now filtered out)
- Byline IDs must match user IDs for draft creation
- Content uses hierarchical JSON structure with
type,attrs,content,marks
For issues or questions:
- Check the troubleshooting section above
- Review
docs/folder for detailed technical guides - Ensure your authentication cookies are current
Note: This tool uses Substack's internal API endpoints. While functional, it may break if Substack changes their API structure.