A stateless Python gateway for Substack that exposes a REST API and an MCP server on top of the same service layer.
Designed to make Substack data and actions accessible from scripts, applications, and AI tooling — without duplicating integration logic across different interfaces.
- Read public Substack profiles, posts, notes, and comments
- Access authenticated
meendpoints with a base64-encoded credential token - Create and delete notes through the REST API
- Use the same gateway as an MCP server for AI tools and agent workflows
- Extend the app with custom routes, MCP tools, auth providers, and lifespan hooks
- REST API at
/api/v1/* - MCP server at
/mcp
Both share the same service layer and HTTP clients. The thin
substack_gateway shell package assembles the application (REST + MCP
composition), while gateway_oss provides the OSS module surface as a facade.
Requirements:
- Python 3.10+
- uv
Install dependencies:
uv sync --devRun the application locally:
uv run python -m substack_gateway.mainCheck the root metadata endpoint:
curl http://127.0.0.1:5001/Check the liveness probe:
curl http://127.0.0.1:5001/api/v1/health/liveFetch a public profile:
curl http://127.0.0.1:5001/api/v1/profiles/<slug>Public profile lookup:
curl http://127.0.0.1:5001/api/v1/profiles/<slug>Authenticated request:
curl \
-H "x-gateway-token: <base64-encoded-json>" \
http://127.0.0.1:5001/api/v1/meThe REST API is mounted under /api/v1 and includes endpoints for health,
profiles, posts, notes, comments, and authenticated me operations.
Gateway access and Substack access are separate concerns.
For this OSS repository, Substack credentials are passed as a base64-encoded
JSON object. REST requests send that value as the x-gateway-token header.
Credential shape:
{
"publication_url": "https://example.substack.com",
"substack_sid": "s%3A...",
"connect_sid": "s%3A..."
}Encode it with:
echo '{"publication_url":"https://example.substack.com","substack_sid":"s%3A...","connect_sid":"s%3A..."}' | base64Treat substack_sid and connect_sid as bearer credentials. Do not commit
real values to the repository.
The MCP server is mounted at /mcp and served over streamable HTTP.
Public OSS MCP tools include:
get_noteget_postget_post_commentsget_profileget_profile_postsget_profile_notes
Core application code lives in src/gateway_oss/.
api/v1/: FastAPI route handlersmcp/: FastMCP tool surface and transport integrationservices/: shared business logicclient/: Substack HTTP client wrappersmodels/: schemas and pagination modelsconverters/: Markdown conversionextensions/: runtime extension hooks
Application settings are environment-driven via the SUBSTACK_GATEWAY_ prefix.
Common examples include:
SUBSTACK_GATEWAY_LOG_LEVELSUBSTACK_GATEWAY_SUBSTACK_BASE_URLSUBSTACK_GATEWAY_SUBSTACK_TIMEOUT_SEC
uv run ruff check .
uv run ruff format --check .
uv run ty check .
uv build
uv run pytest tests/
uv run behave features/The repository includes MkDocs and Read the Docs configuration:
- Docs home
- Introduction
- Installation guide
- Authentication
- API reference
- MCP documentation
- Development guide
- Contributing guide
Read the Docs can build the site directly from .readthedocs.yaml and mkdocs.yml.
Built by Jakub Slys — Backend Engineer building distributed systems for telecoms, running a self-hosted Kubernetes homelab, and building AI automation pipelines with n8n, MCP, and Claude.
This gateway is the backend I use to automate my own Substack at iam.slys.dev, where I write about system design, machine learning fundamentals, and the AI tools I actually build and run in production.
If you want to understand how this gateway works under the hood — the architecture decisions, what I got wrong the first time, and how it fits into a full content automation stack — that's what the newsletter is for.