Substack Posts API
Pull posts from any Substack publication by its URL, or pass a single post URL (…/p/slug) to fetch just that one article. Returns title, subtitle, publish date, paywall status, word count, engagement (reactions, comments, restacks), author profile, publication details, the full article body as text, HTML and Markdown, and optional comment threads.
Try it
Make your first request
{
"data": {
"items": [
{
"authorBio": "Alex Rivera",
"authorHandle": "alex_rivera",
"authorImage": "https://example.com/image.jpg",
"authorName": "Alex Rivera",
"authorUrl": "https://example.com/page",
"commentCount": 12500,
"comments": [
{
"authorHandle": "alex_rivera",
"authorImage": "https://example.com/image.jpg",
"authorName": "Alex Rivera",
"authorUrl": "https://example.com/page",
"commentId": "a1b2c3d4",
"createdUtc": 12.5,
"editedUtc": 12.5,
"isAuthor": true,
"isPinned": true,
"reactionCount": 12500,
"replies": [
null
],
"restackCount": 12500,
"text": "A short example description of this item."
}
],
"contentStatus": "A short example description of this item.",
"createdUtc": 12.5,
"description": "A short example description of this item.",
"hasVoiceover": true,
"html": "example",
"image": "https://example.com/image.jpg",
"isPaid": true,
"language": "en",
"markdown": "example",
"podcastUrl": "https://example.com/page",
"postId": "a1b2c3d4",
"postType": "general",
"publication": {
"customDomain": "example.com",
"description": "A short example description of this item.",
"id": "a1b2c3d4",
"image": "https://example.com/image.jpg",
"language": "en",
"name": "Example title",
"paymentsEnabled": true,
"subdomain": "example.com",
"subscriberCount": 12500,
"url": "https://example.com/page"
},
"reactionCount": 12500,
"replyCount": 12500,
"restackCount": 12500,
"slug": "alex_rivera",
"subtitle": "Example title",
"text": "A short example description of this item.",
"title": "Example title",
"updatedUtc": 12.5,
"url": "https://example.com/page",
"wordcount": 12500
}
]
},
"found": true
}interface SubstackPostsResponse {
data: {
items: {
authorBio?: string;
authorHandle?: string;
authorImage?: string;
authorName?: string;
authorUrl?: string;
commentCount?: number;
comments?: {
authorHandle?: string;
authorImage?: string;
authorName?: string;
authorUrl?: string;
commentId: string;
createdUtc?: number;
editedUtc?: number;
isAuthor?: boolean;
isPinned?: boolean;
reactionCount?: number;
replies?: {
authorHandle?: string;
authorImage?: string;
authorName?: string;
authorUrl?: string;
commentId: string;
createdUtc?: number;
editedUtc?: number;
isAuthor?: boolean;
isPinned?: boolean;
reactionCount?: number;
restackCount?: number;
text?: string;
}[];
restackCount?: number;
text?: string;
}[];
contentStatus?: string;
createdUtc?: number;
description?: string;
hasVoiceover?: boolean;
html?: string;
image?: string;
isPaid?: boolean;
language?: string;
markdown?: string;
podcastUrl?: string;
postId?: string;
postType?: string;
publication?: {
customDomain?: string;
description?: string;
id?: string;
image?: string;
language?: string;
name?: string;
paymentsEnabled?: boolean;
subdomain?: string;
subscriberCount?: number;
url?: string;
};
reactionCount?: number;
replyCount?: number;
restackCount?: number;
slug?: string;
subtitle?: string;
text?: string;
title: string;
updatedUtc?: number;
url: string;
wordcount?: number;
}[];
} | null;
found: boolean;
}Full parameter and response reference - every field, type, and example for this endpoint.
Reference
Request, response, and price
Last verified 2026-09-16 · uptime and latency measured over 30dcurl -X POST https://api.getanyapi.com/v1/run/substack.posts \
-H "Authorization: Bearer $ANYAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"limit":3,"url":"https://www.astralcodexten.com"}'| Field | Type | Example value |
|---|---|---|
| Request body | ||
| url | string | "https://www.astralcodexten.com"Either a Substack publication URL / custom domain to fetch its recent posts (e.g. https://www.astralcodexten.com), OR a single post URL to fetch just that one article with full content (e.g. https://www.astralcodexten.com/p/your-book-review). |
| contentType | enum | Restrict to a single post type, or 'all' (e.g. newsletter). |
| endDate | string | Only return posts published on or before this date, format YYYY-MM-DD (e.g. 2024-12-31). Applied within the most recent 'limit' posts scanned. |
| includeComments | boolean | Include public comment threads and their direct replies on each post (e.g. true). |
| includeContent | boolean | Include the full article body as text, HTML and Markdown. Set false for metadata only, which is faster (e.g. false). |
| limit | integer | 3Maximum number of recent posts to return when given a publication URL (1-100, default 25); ignored for a single post URL, which always returns that one post. You are billed per post returned, so a lower limit costs less. |
| maxComments | integer | Maximum comments collected per post when 'includeComments' is true (0-500, default 20). Costs nothing extra (e.g. 50). |
| minComments | integer | Only return posts with at least this many comments (e.g. 10). |
| minReactions | integer | Only return posts with at least this many reactions (e.g. 100). |
| minWordCount | integer | Only return posts with at least this many words, which filters out short notes and announcements (e.g. 1000). |
| onlyFree | boolean | Return only free (non-paywalled) posts (e.g. true). |
| startDate | string | Only return posts published on or after this date, format YYYY-MM-DD (e.g. 2024-01-01). Applied within the most recent 'limit' posts scanned, so raise 'limit' to reach older date ranges. |
| Response | ||
| data | object | Result payload, or null when nothing was found. |
| data.items | object[] | Post records: title, subtitle, URL, publish date, paywall status, word count, engagement (reactions, comments, restacks), author profile, publication details, the article body as text, HTML and Markdown, and comment threads when requested. Populated whenever the provider has data for the entity. |
| data.items[].authorBio | string | Author bio as shown on their Substack profile. |
| data.items[].authorHandle | string | Substack handle of the post author. Populated whenever the provider has data for the entity. |
| data.items[].authorImage | string | Profile photo URL of the post author. |
| data.items[].authorName | string | Display name of the post author. Populated whenever the provider has data for the entity. |
| data.items[].authorUrl | string | Substack profile URL of the post author. |
| data.items[].commentCount | integer | Number of top-level comments on the post. |
| data.items[].comments | object[] | Top-level comment threads on the post, each with its direct replies. Empty unless 'includeComments' is true. Replies nested more than one level deep are not returned. |
| data.items[].comments[].authorHandle | string | Substack handle of the comment author. |
| data.items[].comments[].authorImage | string | Profile photo URL of the comment author. |
| data.items[].comments[].authorName | string | Display name of the comment author. |
| data.items[].comments[].authorUrl | string | Substack profile URL of the comment author. |
| data.items[].comments[].commentId | string | Substack comment identifier. |
| data.items[].comments[].createdUtc | number | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
| data.items[].comments[].editedUtc | number | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
| data.items[].comments[].isAuthor | boolean | Whether the comment was written by the post author. |
| data.items[].comments[].isPinned | boolean | Whether the comment is pinned by the publication. |
| data.items[].comments[].reactionCount | integer | Number of reactions on the comment. |
| data.items[].comments[].replies | object[] | Direct replies to this comment. |
| data.items[].comments[].replies[].authorHandle | string | Substack handle of the reply author. |
| data.items[].comments[].replies[].authorImage | string | Profile photo URL of the reply author. |
| data.items[].comments[].replies[].authorName | string | Display name of the reply author. |
| data.items[].comments[].replies[].authorUrl | string | Substack profile URL of the reply author. |
| data.items[].comments[].replies[].commentId | string | Substack comment identifier. |
| data.items[].comments[].replies[].createdUtc | number | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
| data.items[].comments[].replies[].editedUtc | number | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
| data.items[].comments[].replies[].isAuthor | boolean | Whether the reply was written by the post author. |
| data.items[].comments[].replies[].isPinned | boolean | Whether the reply is pinned by the publication. |
| data.items[].comments[].replies[].reactionCount | integer | Number of reactions on the reply. |
| data.items[].comments[].replies[].restackCount | integer | Number of times the reply was restacked. |
| data.items[].comments[].replies[].text | string | Reply body text. |
| data.items[].comments[].restackCount | integer | Number of times the comment was restacked. |
| data.items[].comments[].text | string | Comment body text. |
| data.items[].contentStatus | string | How much of the article body this record carries: 'full' for the whole article, 'preview_only' for the public excerpt of a paywalled post, 'metadata_only' when no body was requested or available, or 'failed' when extraction failed. Read this before trusting 'text', 'html', or 'markdown'. Populated whenever the provider has data for the entity. |
| data.items[].createdUtc | number | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. Populated whenever the provider has data for the entity. |
| data.items[].description | string | Short post description, usually the subtitle or an excerpt. |
| data.items[].hasVoiceover | boolean | Whether the post carries a narrated audio version. |
| data.items[].html | string | Article body as HTML. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'. |
| data.items[].image | string | Cover image URL. |
| data.items[].isPaid | boolean | Whether the post is behind a paywall. |
| data.items[].language | string | Two-letter language code of the post. |
| data.items[].markdown | string | Article body as Markdown. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'. |
| data.items[].podcastUrl | string | Audio URL for a podcast post or a narrated voiceover, when the post has one. |
| data.items[].postId | string | Substack post identifier. Populated whenever the provider has data for the entity. |
| data.items[].postType | string | Post type (newsletter, podcast, or thread). Populated whenever the provider has data for the entity. |
| data.items[].publication | object | The publication the post belongs to. |
| data.items[].publication.customDomain | string | Custom domain the publication is served on, when it has one. |
| data.items[].publication.description | string | Publication tagline or hero text. |
| data.items[].publication.id | string | Substack publication identifier. |
| data.items[].publication.image | string | Publication logo URL. |
| data.items[].publication.language | string | Two-letter language code of the publication. |
| data.items[].publication.name | string | Publication name. |
| data.items[].publication.paymentsEnabled | boolean | Whether the publication sells paid subscriptions. |
| data.items[].publication.subdomain | string | Publication subdomain on substack.com. |
| data.items[].publication.subscriberCount | integer | Subscriber count, when the publication publishes it. |
| data.items[].publication.url | string | Publication home URL. |
| data.items[].reactionCount | integer | Number of reactions (likes) on the post. |
| data.items[].replyCount | integer | Number of replies to comments on the post. |
| data.items[].restackCount | integer | Number of times the post was restacked. |
| data.items[].slug | string | Post slug, the last path segment of the post URL. Populated whenever the provider has data for the entity. |
| data.items[].subtitle | string | Post subtitle or deck. |
| data.items[].text | string | Article body as plain text. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'. |
| data.items[].title | string | Post title. Populated whenever the provider has data for the entity. |
| data.items[].updatedUtc | number | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
| data.items[].url | string | Canonical post URL. Populated whenever the provider has data for the entity. |
| data.items[].wordcount | integer | Approximate word count of the article. |
| found | boolean | Whether any posts were found for the request. |
| Price | ||
| Price per request (ceiling) | USD | $0.0444 |
| Price /1k results | USD | $0.439604 |
FAQ
About the Substack Posts API
The AnyAPI Substack Posts API returns Substack posts data as normalized JSON from one POST call to /v1/run/substack.posts. Pull posts from any Substack publication by its URL, or pass a single post URL (…/p/slug) to fetch just that one article. Returns title, subtitle, publish date, paywall status, word count, engagement (reactions, comments, restacks), author profile, publication details, the full article body as text, HTML and Markdown, and optional comment threads. AnyAPI returns one normalized schema whichever source serves it. It costs $0.44 per 1,000 results, and never more than $0.0444 for a single request, in US dollars with no subscription and no monthly minimum. Over the last 30 days, 100.0% of Substack posts calls through AnyAPI succeeded, with a median response time of 3.4 seconds across 10 measured calls.