Substack API

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.

POST/v1/run/substack.posts
Uptime
100.00%
30d · 10 calls
Requests
10
30d
Response
3.4s
median · 30d

Try it

Make your first request

Open in
Get a free key
Sample response
Free runs return only the first 3 results. Fund a key to get the full response.
{
  "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
}
Response interface
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 30d
POST /v1/run/substack.posts
curl -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"}'
FieldTypeExample value
Request body
urlstring"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).
contentTypeenumRestrict to a single post type, or 'all' (e.g. newsletter).
endDatestringOnly 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.
includeCommentsbooleanInclude public comment threads and their direct replies on each post (e.g. true).
includeContentbooleanInclude the full article body as text, HTML and Markdown. Set false for metadata only, which is faster (e.g. false).
limitinteger3Maximum 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.
maxCommentsintegerMaximum comments collected per post when 'includeComments' is true (0-500, default 20). Costs nothing extra (e.g. 50).
minCommentsintegerOnly return posts with at least this many comments (e.g. 10).
minReactionsintegerOnly return posts with at least this many reactions (e.g. 100).
minWordCountintegerOnly return posts with at least this many words, which filters out short notes and announcements (e.g. 1000).
onlyFreebooleanReturn only free (non-paywalled) posts (e.g. true).
startDatestringOnly 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
dataobjectResult payload, or null when nothing was found.
data.itemsobject[]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[].authorBiostringAuthor bio as shown on their Substack profile.
data.items[].authorHandlestringSubstack handle of the post author. Populated whenever the provider has data for the entity.
data.items[].authorImagestringProfile photo URL of the post author.
data.items[].authorNamestringDisplay name of the post author. Populated whenever the provider has data for the entity.
data.items[].authorUrlstringSubstack profile URL of the post author.
data.items[].commentCountintegerNumber of top-level comments on the post.
data.items[].commentsobject[]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[].authorHandlestringSubstack handle of the comment author.
data.items[].comments[].authorImagestringProfile photo URL of the comment author.
data.items[].comments[].authorNamestringDisplay name of the comment author.
data.items[].comments[].authorUrlstringSubstack profile URL of the comment author.
data.items[].comments[].commentIdstringSubstack comment identifier.
data.items[].comments[].createdUtcnumberUTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
data.items[].comments[].editedUtcnumberUTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
data.items[].comments[].isAuthorbooleanWhether the comment was written by the post author.
data.items[].comments[].isPinnedbooleanWhether the comment is pinned by the publication.
data.items[].comments[].reactionCountintegerNumber of reactions on the comment.
data.items[].comments[].repliesobject[]Direct replies to this comment.
data.items[].comments[].replies[].authorHandlestringSubstack handle of the reply author.
data.items[].comments[].replies[].authorImagestringProfile photo URL of the reply author.
data.items[].comments[].replies[].authorNamestringDisplay name of the reply author.
data.items[].comments[].replies[].authorUrlstringSubstack profile URL of the reply author.
data.items[].comments[].replies[].commentIdstringSubstack comment identifier.
data.items[].comments[].replies[].createdUtcnumberUTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
data.items[].comments[].replies[].editedUtcnumberUTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
data.items[].comments[].replies[].isAuthorbooleanWhether the reply was written by the post author.
data.items[].comments[].replies[].isPinnedbooleanWhether the reply is pinned by the publication.
data.items[].comments[].replies[].reactionCountintegerNumber of reactions on the reply.
data.items[].comments[].replies[].restackCountintegerNumber of times the reply was restacked.
data.items[].comments[].replies[].textstringReply body text.
data.items[].comments[].restackCountintegerNumber of times the comment was restacked.
data.items[].comments[].textstringComment body text.
data.items[].contentStatusstringHow 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[].createdUtcnumberUTC 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[].descriptionstringShort post description, usually the subtitle or an excerpt.
data.items[].hasVoiceoverbooleanWhether the post carries a narrated audio version.
data.items[].htmlstringArticle body as HTML. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'.
data.items[].imagestringCover image URL.
data.items[].isPaidbooleanWhether the post is behind a paywall.
data.items[].languagestringTwo-letter language code of the post.
data.items[].markdownstringArticle body as Markdown. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'.
data.items[].podcastUrlstringAudio URL for a podcast post or a narrated voiceover, when the post has one.
data.items[].postIdstringSubstack post identifier. Populated whenever the provider has data for the entity.
data.items[].postTypestringPost type (newsletter, podcast, or thread). Populated whenever the provider has data for the entity.
data.items[].publicationobjectThe publication the post belongs to.
data.items[].publication.customDomainstringCustom domain the publication is served on, when it has one.
data.items[].publication.descriptionstringPublication tagline or hero text.
data.items[].publication.idstringSubstack publication identifier.
data.items[].publication.imagestringPublication logo URL.
data.items[].publication.languagestringTwo-letter language code of the publication.
data.items[].publication.namestringPublication name.
data.items[].publication.paymentsEnabledbooleanWhether the publication sells paid subscriptions.
data.items[].publication.subdomainstringPublication subdomain on substack.com.
data.items[].publication.subscriberCountintegerSubscriber count, when the publication publishes it.
data.items[].publication.urlstringPublication home URL.
data.items[].reactionCountintegerNumber of reactions (likes) on the post.
data.items[].replyCountintegerNumber of replies to comments on the post.
data.items[].restackCountintegerNumber of times the post was restacked.
data.items[].slugstringPost slug, the last path segment of the post URL. Populated whenever the provider has data for the entity.
data.items[].subtitlestringPost subtitle or deck.
data.items[].textstringArticle body as plain text. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'.
data.items[].titlestringPost title. Populated whenever the provider has data for the entity.
data.items[].updatedUtcnumberUTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
data.items[].urlstringCanonical post URL. Populated whenever the provider has data for the entity.
data.items[].wordcountintegerApproximate word count of the article.
foundbooleanWhether any posts were found for the request.
Price
Price per request (ceiling)USD$0.0444
Price /1k resultsUSD$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.

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. You fund one USD wallet, each call draws it down, and a failed request costs $0.

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. The response is normalized JSON with the same envelope every AnyAPI endpoint returns, so parsing a second endpoint is a change of URL and nothing else.

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. These are AnyAPI's own measurements of traffic through the gateway, recomputed continuously, not a published service-level target.