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 Substack profiles, posts, notes, comments, drafts, and publication / post analytics
- Create, update, and delete notes, comments, and drafts
- Access authenticated
meendpoints and MCP tools with a base64-encoded credential token - Use the same gateway as a REST API or an MCP server for AI tools and agent workflows
- Extend the app with custom routes, MCP tools, auth providers, credential resolvers, and lifespan hooks
- REST API at
/api/v1/* - MCP server at
/mcp
Both share the same gateway_core HTTP clients and config. The thin
substack_gateway shell package assembles the application (REST + MCP
composition) from per-domain packages — notes, posts, profiles, following,
comments, me, drafts, and stats — each published as a capability, while
gateway_oss provides health routes and the extension surface.
Requirements:
- Python 3.10+
- uv
Install dependencies:
uv sync --all-packages --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 profile (every /api/v1 route except /health/live requires the
x-gateway-token header, even for reads over public Substack data — see
Authentication):
curl \
-H "x-gateway-token: <base64-encoded-json>" \
http://127.0.0.1:5001/api/v1/profiles/<slug>Profile lookup:
curl \
-H "x-gateway-token: <base64-encoded-json>" \
http://127.0.0.1:5001/api/v1/profiles/<slug>me:
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 45 endpoints across
health, profiles, posts, notes, comments, drafts, publication/post
analytics, following, and me — see the full
API reference.
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. It
exposes 43 tools across the same domains as the REST API — see the full
MCP reference.
Every tool takes an optional token argument carrying the same
base64-encoded credentials as x-gateway-token; when omitted, the gateway
asks an installed credential resolver (this repository installs none, so an
omitted token raises an error telling the caller to pass one). Only the
three profiles tools need no credentials at all:
get_profileget_profile_postsget_profile_notes
The root src/substack_gateway/ package composes the app (app_factory.py,
api_app.py, mcp_app.py, registry.py). packages/gateway_core/ is the
shared foundation: auth, HTTP clients, config, converters, and models. Each
domain ships as a trio of packages:
gateway_<domain>/: service layergateway_<domain>_rest/: FastAPI router, published as a capabilitygateway_<domain>_mcp/: FastMCP tools, published as a capability
packages/gateway_oss/ provides health routes, the extension protocol, and
versioning.
Application settings are environment-driven via the SUBSTACK_GATEWAY_ prefix.
Common examples include:
SUBSTACK_GATEWAY_LOG_LEVELSUBSTACK_GATEWAY_SUBSTACK_BASE_URLSUBSTACK_GATEWAY_SUBSTACK_TIMEOUT_SECSUBSTACK_GATEWAY_STATS_TIMESERIES_WATERMARK_LAG_DAYS(default2)
OSS performs no caching; an extension may install one.
uv run ruff check .
uv run ruff format --check .
uv run ty check .
uv build --all-packages
uv run pytest
uv run behave packages/gateway_oss/features/Reference documentation lives in docs/:
- Docs home
- Introduction
- Installation guide
- Authentication
- API reference
- MCP documentation
- Development guide
- Contributing guide
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.