Substack Public Posts API
Undocumented but public Substack endpoint listing a publication's posts as JSON, with titles, dates, reaction counts, word counts and paywall status. Tested example included.
Endpoint tested and returned HTTP 200 on 21 Aug 2026
What is the Substack Public Posts API?
Every Substack publication serves its post list as JSON at `/api/v1/posts` on its own domain, with no API key. Each post returns its title, subtitle, slug, publication date, audience setting, reaction and comment counts, word count and cover image.
Substack has never shipped a documented public API, but the endpoint its own web front-end calls is open on every publication and needs nothing to read. Point it at any Substack domain — the `*.substack.com` subdomain or a custom domain like the one in the example — and the post list comes back as JSON, ordered newest first.
The metadata is richer than the equivalent RSS feed by a wide margin. `audience` tells you whether a post is public or subscriber-only; `wordcount`, `reactions`, `restacks` and `comment_count` give you engagement figures RSS simply does not carry; `truncated_body_text` provides a preview even for paywalled posts. The obvious caveat is that undocumented means unstable — the field set can change without warning, and Substack's terms govern what you do with the content regardless of how easy it is to fetch. Custom-domain publications redirect off `*.substack.com`, so follow redirects or use the canonical domain directly.
Quick facts
- Base URL
https://www.astralcodexten.com/api/v1- Authentication
- No API key. Paywalled post bodies stay truncated without a subscriber session; metadata for them is still returned.
- Rate limit
- No published limit. It backs a live site — keep requests slow and cache results.
- Pricing
- Free to read. Content remains the publication's copyright.
- CORS
- Not enabled — call it from your server
- Official docs
- Read the docs
How to use the Substack Public Posts API
Every request below was executed against the live API on 21 Aug 2026, and the response shown is the real body it returned — not an illustration.
1. List a publication's most recent posts
GET https://www.astralcodexten.com/api/v1/posts?limit=1
curl 'https://www.astralcodexten.com/api/v1/posts?limit=1'const res = await fetch("https://www.astralcodexten.com/api/v1/posts?limit=1");
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const data = await res.json();
console.log(data);import requests
res = requests.get("https://www.astralcodexten.com/api/v1/posts?limit=1", timeout=20)
res.raise_for_status()
print(res.json())[
{
"audience": "everyone",
"audience_before_archived": null,
"canonical_url": "https://www.astralcodexten.com/p/college-ea-meetups-everywhere-call",
"default_comment_sort": null,
"editor_v2": false,
"exempt_from_archive_paywall": false,
"free_unlock_required": false,
"id": 212052999,
"podcast_art_url": null,
"podcast_duration": null,
"podcast_preview_upload_id": null,
"podcast_upload_id": null,
"podcast_url": null,
"post_date": "2026-08-20T19:55:39.806Z",
"updated_at": "2026-08-20T19:58:27.477Z",
"publication_id": 89120,
"search_engine_description": null,
"search_engine_title": null,
"section_id": null,
"should_send_free_preview": false,
"show_guest_bios": true,
"slug": "college-ea-meetups-everywhere-call",
"social_title": "College EA Meetups Everywhere",
"subtitle": "...",
"teaser_post_eligible": true,
"title": "College EA Meetups Everywhere: Call For Organizers",
"type": "newsletter",
"video_upload_id": null,
"write_comment_permissions": "everyone",
"meter_type": "none",
"live_stream_id": null,
"is_published": true,
"restacks": 9,
"reactions": {
"❤": 76
},
"top_exclusions": [],
"pins": [],
"section_pins": [],
"has_shareable_clips": false,
"previous_post_slug": "why-im-staying-out-of-the-substack",
"next_post_slug": null,
"cover_image": "https://substack-post-media.s3.amazonaws.com/public/images/27645a54-8b36-4494-9413-62d22a675cd7_1999x1268.png",
"cover_image_is_square": false,
"cover_imageParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | query | Optional | How many posts to return. Twelve by default; large values are throttled. 1 |
offset | query | Optional | How many posts to skip, for paging back through the archive. 12 |
sort | query | Optional | `new` for reverse-chronological, `top` for most popular. new |
(publication) | base URL | Required | The publication's own domain — either `name.substack.com` or its custom domain. https://www.astralcodexten.com |
Response fields
(root)array- Posts, newest first. There is no envelope object.
title / subtitle / slugstring- Headline, standfirst and URL slug. Build the post URL from `canonical_url`, which is already absolute.
post_date / updated_atstring- ISO 8601 UTC timestamps for first publication and last edit.
audiencestring- `everyone` for public posts, `only_paid` for subscriber-only. This is how you detect the paywall.
typestring- `newsletter`, `podcast` or `thread` — determines which of the podcast and audio fields are populated.
wordcountinteger- Word count of the full post, present even when the body is truncated.
reactions / reaction_count / restacksobject / integer- Engagement figures. `reactions` is a map keyed by reaction type.
comment_count / child_comment_countinteger- Top-level comments and replies.
truncated_body_textstring- Plain-text preview. For paywalled posts this is all you get.
cover_image / coverImagePalettestring / object- Header image URL and its extracted colour palette.
publishedBylinesarray- Authors, with names, handles and profile images.
What you can build with the Substack Public Posts API
- Mirror a newsletter's archive into your own site or search index
- Track publishing cadence and engagement across several publications
- Build a reading list spanning multiple Substacks in one place
- Detect which posts are public before linking to them
Common errors and how to fix them
Redirect to a different host
The publication uses a custom domain.
Fix: Follow redirects, or call the canonical domain directly. `*.substack.com` 301s to the custom domain when one is configured.
Body text is cut off
The post is subscriber-only.
Fix: `audience` will read `only_paid`. Metadata and `truncated_body_text` are all that is available without a subscriber session.
Fields disappear after a Substack deploy
The endpoint is undocumented and covered by no compatibility promise.
Fix: Read defensively, treat every field as optional, and do not build anything load-bearing on a specific key.
Requests start failing
You are hitting infrastructure that serves a live site.
Fix: Slow down, cache, and page with `offset` rather than requesting huge limits.
Substack Public Posts API — frequently asked questions
Is there an official Substack API?
No. This endpoint powers Substack's own front-end and is publicly readable, but it is undocumented and unsupported, so treat its stability as a courtesy rather than a guarantee.
Can I read paywalled posts through it?
No. Posts marked `audience: only_paid` return metadata and a truncated preview only. The full body needs a subscriber session.
Which URL do I use for a publication?
Its own domain. `example.substack.com/api/v1/posts` works, and publications on a custom domain redirect there — the example uses a custom domain directly to skip the hop.
Is it legal to use this data?
Reading public metadata is straightforward, but the content is copyright the publication and Substack's terms of service apply. Republishing post bodies is a very different proposition from linking to them.
Tools that pair with this API
RSS Feed Generator
Build a valid RSS 2.0 XML feed from channel details and item rows with title, link, description and pubDate. Copy or download the ready-to-publish feed.
Word Counter
Count words, characters, sentences, paragraphs and estimated reading time instantly. Free online word counter for essays, blogs and social media.
Strip HTML Tags
Remove all HTML tags and convert markup to clean plain text. Decode HTML entities and keep line breaks for block elements like paragraphs and headings.
Markdown Link Extractor
Extract every link from Markdown into a table of anchor text, URL and title. Splits internal from external and flags duplicates and empty anchors.
Alternatives to Substack Public Posts API
If Substack Public Posts does not fit, these news & open data apis cover similar ground. Every one was called before publishing, and each links to its own reference page with a working curl command and the response it returned.
| API | What it does | Auth | CORS | Last verified |
|---|---|---|---|---|
| Hacker News | Official Hacker News API with no key: top stories, comments, users and live item feeds via Firebase. Extremely stable. Tested curl example and live JSON. | No key | Yes | 19 Aug 2026 |
| disease.sh | Free open disease and health statistics API with no key: global and per-country case data, historical time series and vaccination figures. Tested example. | No key | Yes | 19 Aug 2026 |
| Spaceflight News | Free spaceflight news API with no key: articles, blogs and reports from major space outlets, with search, filtering and pagination. Tested curl example. | No key | Yes | 19 Aug 2026 |
| Hacker News Search (Algolia) | Free Algolia-powered Hacker News search API with no key: full-text search across all stories and comments with filtering by points, date and author. Tested. | No key | Yes | 20 Aug 2026 |
| Tagesschau | Free German news API with no key: the ARD Tagesschau homepage as structured JSON, with headlines, teaser images in every crop and regional tagging. Tested example included. | No key | Yes | 21 Aug 2026 |
| Wikipedia On This Day | Free Wikimedia On This Day API with no key: historical events, births, deaths and holidays for any calendar date, with linked Wikipedia articles. Tested. | No key | Yes | 20 Aug 2026 |
Substack Public Posts is an independent third-party service and is not affiliated with ByteTools or ByteVancer. Details on this page were verified on 21 Aug 2026; always check the official documentation before relying on this API in production, as terms and limits can change.