MCP Server for AI Agents: Substack Newsletter & Posts API
Pricing
Pay per usage
MCP Server for AI Agents: Substack Newsletter & Posts API
Standby MCP Server (/mcp) and batch Actor for AI agents to search Substack publications, list newsletter archives, search inside a newsletter and extract full posts as Markdown with podcast audio URLs. No proxy, no login.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Mutualistic Labs
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
3 days ago
Last modified
Categories
Share
An always-on Model Context Protocol (MCP) server and batch Actor that searches Substack publications, lists newsletter archives (newest or top posts, with keyword search inside a newsletter) and extracts full posts and podcast episodes as clean Markdown (headings, lists, links and fenced code blocks preserved) including podcast audio URLs. Built for AI agents (Claude Desktop, Cursor, Windsurf, OpenAI Agents, LangChain), market and competitor research, newsletter monitoring and content analysis.
No proxies, no browser, no logins, no cookies: only the public Substack JSON endpoints (/api/v1/publication/search,
/api/v1/archive, /api/v1/posts/{slug}) are called. Paywalled posts return their public preview and is_paywalled: true.
What you get
record_type | Mode | What it is |
|---|---|---|
publication | search_publications | One Substack publication (name, author, tagline, subscriber magnitude, podcast flags) |
post | archive, posts | One post or podcast episode (metadata, counts, tags, audience, Markdown content_text, audio URLs) |
Endpoints
GET /andGET /health: readiness probe, returns{"status":"ok","actor":"substack-newsletter-mcp",...}.POST /mcp: MCP JSON-RPC 2.0 (initialize,ping,tools/list,tools/call).
Standby URL: https://mutualistic-housing--substack-newsletter-mcp.apify.actor/mcp (authenticate with your Apify API token).
Input
| Field | Type | Default |
|---|---|---|
mode | archive, search_publications, posts | archive |
publications | string[] (preset keys, subdomains, *.substack.com URLs or custom https domains, 1 to 10) | ["pragmaticengineer"] |
query | string, max 200 chars (required for search_publications; optional keyword search inside each newsletter in archive) | none |
sort | new, top | new |
include_full_text | boolean | true |
audience_filter | all, everyone (free only), only_paid | all |
post_urls | string[] of post URLs (mode posts, max 50 together with slugs) | none |
slugs | string[] of post slugs bound to the one explicit publication (mode posts) | none |
max_items | integer 1 to 500 | 25 |
max_total_charge_usd | number or null | no cap |
An empty {} input runs: the 25 newest posts of pragmaticengineer with full text. camelCase aliases (includeFullText,
audienceFilter, postUrls, maxItems, maxTotalChargeUsd) are accepted. Invalid input is rejected with
INVALID_INPUT_ZERO_CHARGE before any network call or charge.
Batch input examples
Newest posts of two newsletters, full text:
{"mode": "archive", "publications": ["pragmaticengineer", "lenny"], "max_items": 25}
Search inside a newsletter (titles and bodies), top posts, free only:
{"mode": "archive", "publications": ["semianalysis"], "query": "HBM", "sort": "top", "audience_filter": "everyone", "max_items": 20}
Find publications:
{"mode": "search_publications", "query": "machine learning", "max_items": 30}
Specific posts (URLs, or slugs bound to one publication):
{"mode": "posts", "post_urls": ["https://newsletter.pragmaticengineer.com/p/some-post"]}
{"mode": "posts", "publications": ["lenny"], "slugs": ["some-post-slug"]}
MCP tools
{"mcpServers": {"substack": {"url": "https://mutualistic-housing--substack-newsletter-mcp.apify.actor/mcp","headers": { "Authorization": "Bearer <APIFY_TOKEN>" }}}}
get_newsletter_posts:publications,sort,search,include_full_text(defaultfalsein MCP),audience_filter,max_items(default 10).search_publications:query(required),max_items.get_post_content:post_url, orpublication+slug.list_preset_publications: no arguments; lists the preset publications (free).
Example calls:
curl -X POST "https://mutualistic-housing--substack-newsletter-mcp.apify.actor/mcp" \-H "Authorization: Bearer <APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_newsletter_posts","arguments":{"publications":["pragmaticengineer"],"sort":"top","search":"scaling","max_items":5}}}'
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_publications","arguments":{"query":"semiconductors","max_items":10}}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_post_content","arguments":{"post_url":"https://newsletter.pragmaticengineer.com/p/some-post"}}}
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"list_preset_publications","arguments":{}}}
Output fields
post:
| Field | Meaning |
|---|---|
record_type, uid | post; deterministic SHA-256 identifier |
post_id, publication_id, publication_subdomain, publication_base_url | Ids and publication the post belongs to |
title, subtitle, description, slug, canonical_url, post_date | Post metadata |
audience, post_type, is_paywalled | everyone / only_paid / ...; newsletter / podcast / thread; paywall flag |
wordcount, reaction_count, comment_count, restacks | Counts |
authors, tags, section_name, cover_image | Authors, tags, section, cover image |
excerpt, content_text, content_source | Clean excerpt; full Markdown (up to 100000 characters); body_html or post_endpoint |
Podcast fields: podcast_url (audio URL), podcast_duration (seconds) and audio_items, a list of
{"audio_url", "type", "status", "duration"} entries for every audio attachment of the episode.
publication:
| Field | Meaning |
|---|---|
record_type, uid, publication_id | publication; deterministic SHA-256 identifier; Substack id |
name, subdomain, custom_domain, base_url | Name and addresses |
author_name, author_handle, author_bio, author_bestseller_tier | Main author |
hero_text, language, logo_url | Tagline, language, logo |
free_subscriber_magnitude, ranking_detail | Subscriber order of magnitude (e.g. 100K+) and ranking text |
has_podcast, has_free_podcast, community_enabled | Feature flags |
created_at, first_post_date | Dates |
Example post (podcast episode, shortened):
{"record_type": "post", "post_id": 1234567, "publication_id": 458709, "publication_subdomain": "pragmaticengineer","publication_base_url": "https://pragmaticengineer.substack.com", "title": "Example episode", "slug": "example-episode","canonical_url": "https://newsletter.pragmaticengineer.com/p/example-episode", "post_date": "2026-01-10T09:00:00.000Z","audience": "everyone", "post_type": "podcast", "wordcount": 4200, "reaction_count": 310, "comment_count": 42, "restacks": 12,"authors": ["Gergely Orosz"], "tags": ["AI"], "podcast_url": "https://example.com/audio.mp3", "podcast_duration": 3605.5,"audio_items": [{"audio_url": "https://example.com/audio.mp3", "type": "podcast", "status": "transcoded", "duration": 3605.5}],"content_text": "# Example episode\n\nIntro...", "content_source": "post_endpoint", "is_paywalled": false, "uid": "<sha256>"}
Preset publications
Use the key in publications; any other Substack subdomain, *.substack.com URL or custom newsletter domain works too (https only).
| Key | Publication | Topic |
|---|---|---|
pragmaticengineer | The Pragmatic Engineer | software engineering |
lenny | Lenny's Newsletter | product |
semianalysis | SemiAnalysis | semiconductors, AI |
astralcodexten | Astral Codex Ten | essays, science, rationality |
noahpinion | Noahpinion | economics |
slowboring | Slow Boring | policy |
oneusefulthing | One Useful Thing | AI |
garymarcus | Marcus on AI | AI |
thezvi | Don't Worry About the Vase | AI |
simonw | Simon Willison's Newsletter | AI, development |
importai | Import AI | AI policy |
bytebytego | ByteByteGo Newsletter | system design |
latentspace | Latent Space | AI engineering |
thegeneralist | The Generalist | tech, business |
heathercoxrichardson | Letters from an American | US politics |
Security and limits
- https only: bare names become
https://name.substack.com;http://,file:and other schemes are rejected. - SSRF protections: credentials in URLs, non-443 ports,
..path traversal and fragments are rejected; IP literals,localhost,.localand.internalnames are blocked; DNS results (and the connected peer address) that are loopback, private, link-local, CGNAT, multicast or unspecified are refused. Redirects (for example to a custom domain) must be https and are re-checked per host. - Bounds: at most 500 items per run, 10 publications per call, 50 posts in mode
posts, a 120 s global batch deadline, a 45 s deadline per MCP tool call, a 15 s per-request timeout (connect, TLS, headers and body) and a 2 MiB response size cap. - MCP server: request bodies above 64 KiB return
413,POST /mcprequiresContent-Type: application/json(415otherwise) and at most 8 concurrent tool calls run (503when busy). - Untrusted content:
content_textis untrusted third-party Markdown written by newsletter authors. Link destinations are percent-encoded and only http(s) links are emitted, but plain text is passed through verbatim (it can contain Markdown or HTML-like text such as[a](javascript:x)). Sanitize link schemes and raw HTML before rendering it in a browser or UI. is_paywalledistruefor every non-public audience (only_paid,founding,only_free): without a subscriber cookie the API only serves a preview, socontent_textof such posts is a truncated preview.- Rate limits (
429) and 5xx responses are retried with back-off; per-publication and per-post failures are reported inerrorsand never abort other publications.
Pricing
Pay per event: $0.001 per result-item (result-item: one de-duplicated publication or post, as a dataset item or an MCP tool
result row); the actor-start event is free and list_preset_publications is free. max_items and max_total_charge_usd are hard
caps enforced in code, so a run never charges more than you configure. Invalid input and empty results are never charged.