# Substack API Scraper: Posts, Authors & Newsletters (`maximedupre/substack-api`) Actor

Read public Substack posts, publications, and comments in structured rows. Browse archives, search posts, discover publications by topic or category, and read public recommendations. Get titles, authors, dates, engagement, public previews, and quality notes without a Substack login or API key.

- **URL**: https://apify.com/maximedupre/substack-api.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** News, Social media, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### 🧭 Public Substack data for research and archives

This Actor is for researchers, newsletter teams, analysts, and developers who need public Substack data. It returns structured post, publication, and comment rows with links, authors, dates, access status, engagement, discovery context, and quality notes. Use it to build a research table, check a publication, or collect a public post thread without manual copying.

- Read a publication archive with **[Substack API Posts](https://apify.com/maximedupre/substack-api/examples/substack-api-posts)**.
- Retrieve public posts from their links with **[Substack Newsletter Scraper](https://apify.com/maximedupre/substack-api/examples/substack-newsletter-scraper)**.
- Read a publication profile with **[Substack Publication Scraper](https://apify.com/maximedupre/substack-api/examples/substack-publication-scraper)**.
- Search posts inside a publication with **[Substack API Search](https://apify.com/maximedupre/substack-api/examples/substack-api-search)**.
- Find publications by a topic with **[Substack Topic Discovery](https://apify.com/maximedupre/substack-api/examples/substack-topic-discovery)**.
- Find ranked publications in a category with **[Substack Category Discovery](https://apify.com/maximedupre/substack-api/examples/substack-category-discovery)**.
- Find public publication data with **[Substack API Publications](https://apify.com/maximedupre/substack-api/examples/substack-api-publications)**.
- Read **[Substack API Documentation](https://apify.com/maximedupre/substack-api/examples/substack-api-documentation)** before planning a public data workflow.
- Check **[Substack API Key](https://apify.com/maximedupre/substack-api/examples/substack-api-key)** guidance when choosing an access method.
- Try a general public-data run with **[Substack API](https://apify.com/maximedupre/substack-api/examples/substack-api)**.

#### 🧾 See the Substack data you can use

Post rows hold public post identity, editorial fields, access status, author details, publication context, engagement, media, audio, and content when requested. Publication rows hold public identity, branding, setup details, and the category, topic, or recommendation value that led to the row. Comment rows hold public comment text, author details, time, engagement, depth, parent information, and post context.

The Actor reads public Substack surfaces. It does not need a Substack login or API key. Restricted posts keep public metadata and previews, while subscriber-only body content is not promised.

#### ▶️ Choose a Substack task

**How a run works**

1. Choose one target in the input form.
2. Add the matching publication, post URL, category, topic, or post query.
3. Add filters when you need a narrower set of posts.
4. Turn on public article content only when you need HTML and readable text.
5. Start the run, then open the dataset or the source-availability report.

Use one shared target and filter setup for each run. A list of same-kind publication targets is supported. Handles, Substack subdomain URLs, and supported custom-domain URLs can be used for publication targets.

#### ⚙️ Input

**Input fields**

| Field | Type | What it does |
| --- | --- | --- |
| `target` | string | Required. Choose `publicationArchive`, `post`, `publicationDetails`, `categoryDiscovery`, `topicDiscovery`, `publicationRecommendations`, `publicationSearch`, or `postComments`. |
| `category` | string | Required for `categoryDiscovery`. Enter the category used by Substack's public discovery view. |
| `ranking` | string | Required for `categoryDiscovery`. Enter the ranking view used with the category. |
| `topic` | string | Required for `topicDiscovery`. Enter free text for the public publication search. |
| `publicationTargets` | array of strings | Add public publication handles, Substack subdomain URLs, or supported custom-domain URLs. Used by archive, details, recommendations, and publication search targets. For recommendations, each publication is the source. |
| `postUrls` | array of objects | Add canonical public post URLs for post retrieval or comment threads. |
| `postUrls[].url` | string | The full canonical public post URL. |
| `postQuery` | string | Optional for `publicationArchive` and required for `publicationSearch`. Use text from a post title or subtitle. Other targets ignore it. |
| `dateFrom` | string | Optional lower date bound for post targets. Use a UTC date in `YYYY-MM-DD` form. |
| `dateTo` | string | Optional upper date bound for post targets. Use a UTC date in `YYYY-MM-DD` form. |
| `audience` | string | Optional post filter. Choose `all`, `free`, or `paid`. Discovery and publication-details targets ignore it. |
| `languages` | array of strings | Optional language filter for discovered publications and post results. Add language names or codes. Other targets ignore it. |
| `postTypes` | array of strings | Optional post type filter. Choose `newsletter`, `podcast`, `thread`, or `video`. Non-post targets ignore it. |
| `authors` | array of strings | Optional post filter. Add author names or public handles. Non-post targets ignore it. |
| `tags` | array of strings | Optional post filter. Add publication tag names. Non-post targets ignore it. |
| `minReactions` | integer | Optional minimum reaction count for post results. Non-post targets ignore it. |
| `minComments` | integer | Optional minimum comment count for post results. Non-post targets ignore it. |
| `minWords` | integer | Optional minimum word count for post results. Non-post targets ignore it. |
| `sortBy` | string | Optional post order. Choose `newest`, `oldest`, `popularity`, `engagement`, or `length`. Targets that do not return posts ignore it. |
| `includeArticleContent` | boolean | When true, fetch complete public article HTML and readable text when available. It applies to archive, post, and publication-search targets. Restricted posts still return public previews only. |
| `maxItems` | integer | Optional positive limit on saved rows. It can stop archive, discovery, search, and comment work early. Leave it empty to return all available results until the source is exhausted. |

**Default input**

This is the complete public input from a successful current-beta default run:

```json
{
  "target": "publicationArchive",
  "publicationTargets": [
    "arxivblog"
  ],
  "includeArticleContent": false
}
```

#### 🧾 Output

**Output links**

| Field | Type | What it does |
| --- | --- | --- |
| `results` | URL | Opens the default dataset with successful post, publication, and comment rows. |
| `sourceAvailability` | URL | Opens the run report for unavailable sources and fetch failures. |

Every dataset row has a `resultType` value. The value is `post`, `publication`, or `comment`. The tables below cover each shape.

**Post rows**

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string | Identifies this as a `post` row. |
| `post` | object | Groups post identity, content, publication, and engagement data. |
| `post.id` | string | Public post ID for joining or checking the source row, when available. |
| `post.url` | string | Canonical public post URL. |
| `post.title` | string | Public post title. |
| `post.description` | string | Public subtitle or description, when available. |
| `post.publishedAt` | string | Post publication time. |
| `post.postType` | string | Source post type, such as newsletter, podcast, thread, or video. |
| `post.accessStatus` | string | Says whether the post is public, paid, or otherwise restricted. |
| `post.authors` | array of objects | Public post byline details. |
| `post.authors[]` | object | One public post author. |
| `post.authors[].name` | string | Public author name. |
| `post.authors[].handle` | string | Public author handle, when available. |
| `post.authors[].id` | string | Public author ID, when available. |
| `post.authors[].url` | string | Public author profile URL, when available. |
| `post.publication` | object | Publication context for the post. |
| `post.publication.id` | string | Public publication ID, when available. |
| `post.publication.name` | string | Public publication name. |
| `post.publication.handle` | string | Public publication handle, when available. |
| `post.publication.url` | string | Public publication URL. |
| `post.publication.domain` | string | Publication domain, when available. |
| `post.publication.description` | string | Public publication description, when available. |
| `post.publication.authors` | array of objects | Available public publication authors. |
| `post.publication.authors[]` | object | One public publication author. |
| `post.publication.authors[].name` | string | Public publication author name. |
| `post.publication.authors[].handle` | string | Public publication author handle, when available. |
| `post.publication.authors[].id` | string | Public publication author ID, when available. |
| `post.publication.authors[].url` | string | Public publication author profile URL, when available. |
| `post.publication.category` | string | Publication category, when available. |
| `post.publication.language` | string | Publication language or language code, when available. |
| `post.publication.branding` | object | Public publication branding, when available. |
| `post.publication.branding.logoUrl` | string | Public publication logo URL, when available. |
| `post.publication.branding.coverImageUrl` | string | Public publication cover image URL, when available. |
| `post.publication.branding.accentColor` | string | Publication accent color, when available. |
| `post.publication.setup` | object | Public publication setup details, when available. |
| `post.publication.setup.subscriptionStatus` | string | Public subscription status, when available. |
| `post.publication.setup.hasFoundingPlan` | boolean | Whether a public founding plan is available, when shown. |
| `post.publication.setup.hasCommunity` | boolean | Whether a public community is available, when shown. |
| `post.publication.setup.createdAt` | string | Publication creation time, when available. |
| `post.engagement` | object | Public post engagement counts and score, when available. |
| `post.engagement.reactionCount` | integer | Number of post reactions, when available. |
| `post.engagement.commentCount` | integer | Number of post comments, when available. |
| `post.engagement.restackCount` | integer | Number of post restacks, when available. |
| `post.engagement.score` | number | Aggregate source engagement measure, when available. |
| `post.reactionBreakdown` | array of objects | Count for each reaction type exposed by the source. |
| `post.reactionBreakdown[]` | object | One reaction type and count. |
| `post.reactionBreakdown[].type` | string | Reaction type. |
| `post.reactionBreakdown[].count` | integer | Count for that reaction type. |
| `post.wordCount` | integer | Post word count, when available. |
| `post.language` | string | Post language or language code, when available. |
| `post.media` | object | Public cover and in-article image references, when available. |
| `post.media.coverImage` | object | Cover image reference, when available. |
| `post.media.coverImage.url` | string | Public cover image URL. |
| `post.media.coverImage.altText` | string | Cover image alt text, when available. |
| `post.media.coverImage.caption` | string | Cover image caption, when available. |
| `post.media.images` | array of objects | In-article image references, when available. |
| `post.media.images[]` | object | One in-article image reference. |
| `post.media.images[].url` | string | Public in-article image URL. |
| `post.media.images[].altText` | string | Image alt text, when available. |
| `post.media.images[].caption` | string | Image caption, when available. |
| `post.audio` | object | Audio details for an audio post, when available. |
| `post.audio.url` | string | Public audio file URL. |
| `post.audio.durationSeconds` | number | Audio duration in seconds, when available. |
| `post.content` | object | Full public article or public preview, when available. |
| `post.content.html` | string | Article HTML, when available. |
| `post.content.text` | string | Readable article text, when available. |
| `post.content.isComplete` | boolean | Says whether the content is the full public body instead of a preview. |
| `quality` | object | Confidence and source-data warnings for the row. |
| `quality.confidence` | string | Confidence level for the row fields. |
| `quality.warnings` | array of strings | Notes about missing or uncertain source data. |
| `quality.warnings[]` | string | One warning about missing or uncertain source data. |

**Current-beta post row**

```json
{
  "resultType": "post",
  "post": {
    "url": "https://arxivblog.substack.com/p/next-generation-ai-attack-vectors",
    "title": "Next generation AI attack vectors will be ideas, say Anthropic researchers",
    "publishedAt": "2026-08-21T17:08:27.058Z",
    "postType": "newsletter",
    "accessStatus": "public",
    "id": "212161880",
    "description": "Computer scientists have bred self-propagating \"mind viruses\" that spread from one AI agent to the next through ordinary conversation — no hacking required",
    "authors": [
      {
        "name": "Physics arXiv Blog",
        "handle": "physicsarxivblog",
        "id": "75337004"
      }
    ],
    "publication": {
      "name": "Emerging Technology from the arXiv",
      "url": "https://arxivblog.substack.com/",
      "id": "5026881",
      "handle": "arxivblog",
      "domain": "arxivblog.substack.com",
      "description": "Technology. Science. Insight.",
      "authors": [
        {
          "name": "Physics arXiv Blog",
          "handle": "physicsarxivblog",
          "id": "75337004"
        }
      ],
      "language": "en",
      "branding": {
        "logoUrl": "https://substackcdn.com/image/fetch/$s_!-WgM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3743002d-f5ed-4304-9f23-701618b0b1d2_1280x1280.png",
        "accentColor": "#FF6719"
      },
      "setup": {
        "subscriptionStatus": "paid",
        "hasFoundingPlan": true,
        "hasCommunity": true,
        "createdAt": "2025-05-15T12:12:29.439Z"
      }
    },
    "engagement": {
      "reactionCount": 30,
      "commentCount": 0,
      "restackCount": 13,
      "score": 43
    },
    "reactionBreakdown": [
      {
        "type": "❤",
        "count": 30
      }
    ],
    "wordCount": 1113,
    "language": "en",
    "media": {
      "coverImage": {
        "url": "https://substackcdn.com/image/fetch/$s_!jAvp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47f0a7f9-f2cd-4927-9bb6-539ad99de42d_550x665.png"
      }
    },
    "audio": {
      "url": "https://substack-video.s3.amazonaws.com/video_upload/post/212161880/tts/5cbb9c7b-74ce-4fe0-9b74-bd84b8e5cfde/en-GB-AdaMultilingualNeural.mp3"
    }
  },
  "quality": {
    "confidence": "high",
    "warnings": []
  }
}
```

**Publication rows**

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string | Identifies this as a `publication` row. |
| `publication` | object | Groups public publication identity, context, branding, and setup data. |
| `publication.id` | string | Public publication ID, when available. |
| `publication.name` | string | Public publication name. |
| `publication.handle` | string | Public publication handle, when available. |
| `publication.url` | string | Public publication URL. |
| `publication.domain` | string | Publication domain, when available. |
| `publication.description` | string | Public publication description, when available. |
| `publication.authors` | array of objects | Available public publication authors. |
| `publication.authors[]` | object | One public publication author. |
| `publication.authors[].name` | string | Public author name. |
| `publication.authors[].handle` | string | Public author handle, when available. |
| `publication.authors[].id` | string | Public author ID, when available. |
| `publication.authors[].url` | string | Public author profile URL, when available. |
| `publication.category` | string | Publication category, when available. |
| `publication.language` | string | Publication language or language code, when available. |
| `publication.branding` | object | Public publication branding, when available. |
| `publication.branding.logoUrl` | string | Public publication logo URL, when available. |
| `publication.branding.coverImageUrl` | string | Public publication cover image URL, when available. |
| `publication.branding.accentColor` | string | Publication accent color, when available. |
| `publication.setup` | object | Public publication setup details, when available. |
| `publication.setup.subscriptionStatus` | string | Public subscription status, when available. |
| `publication.setup.hasFoundingPlan` | boolean | Whether a public founding plan is available, when shown. |
| `publication.setup.hasCommunity` | boolean | Whether a public community is available, when shown. |
| `publication.setup.createdAt` | string | Publication creation time, when available. |
| `discovery` | object | The category, topic, or publication value that produced the row, when available. |
| `discovery.method` | string | Discovery method, such as `category`, `topic`, or `recommendation`. |
| `discovery.value` | string | Submitted category, topic, handle, or URL that produced the row. |
| `discovery.ranking` | string | Category ranking view used for discovery, when applicable. |
| `quality` | object | Confidence and source-data warnings for the row. |
| `quality.confidence` | string | Confidence level for the row fields. |
| `quality.warnings` | array of strings | Notes about missing or uncertain source data. |
| `quality.warnings[]` | string | One warning about missing or uncertain source data. |

**Current-beta publication row**

```json
{
  "resultType": "publication",
  "publication": {
    "name": "Emerging Technology from the arXiv",
    "url": "https://arxivblog.substack.com/",
    "id": "5026881",
    "handle": "arxivblog",
    "domain": "arxivblog.substack.com",
    "description": "Technology. Science. Insight.",
    "authors": [
      {
        "name": "Physics arXiv Blog",
        "handle": "physicsarxivblog",
        "id": "75337004"
      }
    ],
    "language": "en",
    "branding": {
      "logoUrl": "https://substackcdn.com/image/fetch/$s_!-WgM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3743002d-f5ed-4304-9f23-701618b0b1d2_1280x1280.png",
      "accentColor": "#FF6719"
    },
    "setup": {
      "subscriptionStatus": "paid",
      "hasFoundingPlan": true,
      "hasCommunity": true,
      "createdAt": "2025-05-15T12:12:29.439Z"
    }
  },
  "quality": {
    "confidence": "high",
    "warnings": []
  }
}
```

**Comment rows**

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string | Identifies this as a `comment` row. |
| `comment` | object | Groups comment text, author, time, engagement, and thread relationship data. |
| `comment.id` | string | Public comment ID, when available. |
| `comment.body` | string | Public comment body. |
| `comment.createdAt` | string | Time when the comment was posted. |
| `comment.author` | object | Public comment author details, when available. |
| `comment.author.name` | string | Public author name, when available. |
| `comment.author.handle` | string | Public author handle, when available. |
| `comment.author.id` | string | Public author ID, when available. |
| `comment.author.url` | string | Public author profile URL, when available. |
| `comment.engagement` | object | Public comment engagement counts and score, when available. |
| `comment.engagement.reactionCount` | integer | Number of comment reactions, when available. |
| `comment.engagement.replyCount` | integer | Number of direct replies, when available. |
| `comment.engagement.score` | number | Aggregate source comment engagement measure, when available. |
| `comment.depth` | integer | Comment depth in the thread. A root comment is `0`. |
| `comment.parentId` | string | Public parent comment ID, when this is a reply. |
| `comment.post` | object | Public post that contains the comment. |
| `comment.post.id` | string | Public post ID, when available. |
| `comment.post.title` | string | Public post title, when available. |
| `comment.post.url` | string | Canonical public post URL. |
| `comment.post.publication` | object | Publication context for the commented post, when available. |
| `comment.post.publication.id` | string | Public publication ID, when available. |
| `comment.post.publication.name` | string | Public publication name. |
| `comment.post.publication.handle` | string | Public publication handle, when available. |
| `comment.post.publication.url` | string | Public publication URL. |
| `comment.post.publication.domain` | string | Publication domain, when available. |
| `comment.post.publication.description` | string | Public publication description, when available. |
| `comment.post.publication.authors` | array of objects | Available public publication authors. |
| `comment.post.publication.authors[]` | object | One public publication author. |
| `comment.post.publication.authors[].name` | string | Public author name. |
| `comment.post.publication.authors[].handle` | string | Public author handle, when available. |
| `comment.post.publication.authors[].id` | string | Public author ID, when available. |
| `comment.post.publication.authors[].url` | string | Public author profile URL, when available. |
| `comment.post.publication.category` | string | Publication category, when available. |
| `comment.post.publication.language` | string | Publication language or language code, when available. |
| `comment.post.publication.branding` | object | Public publication branding, when available. |
| `comment.post.publication.branding.logoUrl` | string | Public publication logo URL, when available. |
| `comment.post.publication.branding.coverImageUrl` | string | Public publication cover image URL, when available. |
| `comment.post.publication.branding.accentColor` | string | Publication accent color, when available. |
| `comment.post.publication.setup` | object | Public publication setup details, when available. |
| `comment.post.publication.setup.subscriptionStatus` | string | Public subscription status, when available. |
| `comment.post.publication.setup.hasFoundingPlan` | boolean | Whether a public founding plan is available, when shown. |
| `comment.post.publication.setup.hasCommunity` | boolean | Whether a public community is available, when shown. |
| `comment.post.publication.setup.createdAt` | string | Publication creation time, when available. |
| `quality` | object | Confidence and source-data warnings for the row. |
| `quality.confidence` | string | Confidence level for the row fields. |
| `quality.warnings` | array of strings | Notes about missing or uncertain source data. |
| `quality.warnings[]` | string | One warning about missing or uncertain source data. |

**Current-beta comment row**

```json
{
  "resultType": "comment",
  "comment": {
    "body": "Yep! Spec decoding doesn't reduce the work, it just converts memory-bound idle time into tokens i.e. compute you'd already paid for.",
    "createdAt": "2026-08-26T19:35:43.743Z",
    "depth": 0,
    "post": {
      "url": "https://blog.bytebytego.com/p/how-to-make-llms-3x-faster",
      "id": "212180385",
      "title": "How to Make LLMs 3X Faster",
      "publication": {
        "name": "ByteByteGo Newsletter",
        "url": "https://blog.bytebytego.com/",
        "id": "817132",
        "handle": "bytebytego",
        "domain": "blog.bytebytego.com",
        "description": "Explain complex systems with simple terms, from the authors of the best-selling system design book series. Join over 1,000,000 friendly readers.",
        "authors": [
          {
            "name": "Alex Xu",
            "handle": "bytebytego",
            "id": "22329494"
          },
          {
            "name": "Sahn Lam",
            "handle": "sahnlam",
            "id": "55391021"
          },
          {
            "name": "ByteByteGo",
            "handle": "bytebytego399569",
            "id": "106455990"
          }
        ],
        "language": "en",
        "branding": {
          "logoUrl": "https://substackcdn.com/image/fetch/$s_!1eXV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a5609ae-1239-4400-9491-6010a15c4d60_504x504.png",
          "coverImageUrl": "https://substack-post-media.s3.amazonaws.com/public/images/a4e1c1bd-03ee-4126-b3e4-19553b2d2fc6_1279x821.png",
          "accentColor": "#9D6FFF"
        },
        "setup": {
          "subscriptionStatus": "paid",
          "hasFoundingPlan": true,
          "hasCommunity": true,
          "createdAt": "2022-03-27T03:36:45.845Z"
        }
      }
    },
    "id": "323271360",
    "author": {
      "name": "Mitchell Kosowski",
      "handle": "mitchellkosowski",
      "id": "803879"
    },
    "engagement": {
      "reactionCount": 0,
      "replyCount": 0,
      "score": 1
    }
  },
  "quality": {
    "confidence": "high",
    "warnings": []
  }
}
```

#### 💳 Pricing

**How billing works**

Pricing is pay per event. An event is charged when the matching public row is saved. The exact rate depends on the event and the Apify plan tier.

| Event | Charge |
| --- | --- |
| `substack-post-retrieved` | One saved public post. The rates are $0.0018 on FREE, $0.0009 on BRONZE, $0.0007 on SILVER, $0.0005 on GOLD, $0.00035 on PLATINUM, and $0.00025 on DIAMOND. |
| `substack-publication-retrieved` | One saved public publication at $0.0018. |
| `substack-comment-retrieved` | One saved public comment at $0.0018. |

Use filters and `maxItems` to limit the rows a run can save. Leaving `maxItems` empty returns all available results until the source is exhausted. This pricing explanation does not assume a charge for an empty or no-result run.

#### 🔌 Integrations

**Dataset access**

Start the Actor in Apify Console or through the standard Actor API. Open the dataset after the run to review rows or export them in the formats provided by Apify.

**Video guide**

https://www.youtube.com/watch?v=bNACk1\_S\_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### What happens with a paid or restricted post?

The Actor returns public identity, access status, metadata, and a public preview when the source provides one. It does not promise subscriber-only body content.

##### How do I search posts inside a publication?

Choose `publicationSearch`, add a publication target, and enter `postQuery`. The query uses text found in post titles or subtitles. You can also add the post filters that apply to the target.

##### How do I discover publications by topic or category?

Choose `topicDiscovery` and enter a topic, or choose `categoryDiscovery` and enter a category with its ranking view. You can also choose `publicationRecommendations` to read public recommendations from a publication target.

##### Can I retrieve one post by URL?

Yes. Choose `post`, add its canonical public URL to `postUrls`, and set any post filters you need.

##### Can I return all available rows?

Yes. Leave `maxItems` empty. The Actor continues until all available results are returned or the source is exhausted.

##### Can I read public comments?

Yes. Choose `postComments` and add one or more canonical public post URLs. Comment rows can include body, author, time, engagement, depth, and parent comment ID when the source provides them.

##### What if a source is unavailable?

Open the `sourceAvailability` report from the output. It provides a place to review unavailable sources and fetch failures separately from successful rows.

##### Can I use more than one publication target?

Yes. Add a list of same-kind public publication targets for archive, details, recommendations, or publication search work. The same filters apply to the whole run.

##### Do I need a Substack login or API key?

No. The Actor reads public Substack data without a login or API key. Public availability and source changes can still affect what a run returns.

##### How can I reduce the work in a run?

Use `postQuery`, date, language, audience, post type, author, tag, engagement, and word-count filters when they fit your target. Set `maxItems` when you need a smaller result set, and leave article content off unless you need the public body.

### 📝 Changelog

**0.0: Initial release**

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~substack-api/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [Substack Notes Scraper](https://apify.com/maximedupre/substack-notes) collects public Notes for topic and author research alongside newsletter data.
- [Substack Recommendations](https://apify.com/maximedupre/substack-recommendations) maps public recommendation links between publications.
- [Substack API & Scraper — Posts, Authors & Newsletters](https://apify.com/sian.agency/substack-scraper) provides another post, author, and newsletter archive workflow.
- [Substack Scraper - Newsletters, Posts & Authors](https://apify.com/logiover/substack-newsletter-scraper) collects newsletter posts and author data for comparison work.
- [Substack Scraper — Posts, Comments & Newsletters](https://apify.com/pixflor/substack-scraper) covers post, comment, and newsletter research workflows.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `target` (type: `string`):

Choose the public Substack data to read.

## `category` (type: `string`):

Required for `categoryDiscovery`. Enter a category from Substack's public discovery view.

## `ranking` (type: `string`):

Required for `categoryDiscovery`. Enter the ranking view to use with the category.

## `topic` (type: `string`):

Required for `topicDiscovery`. Enter free text for the public publication search.

## `publicationTargets` (type: `array`):

Enter one or more public publication handles, Substack subdomain URLs, or supported custom-domain URLs. Use this list for publication archive, publication details, recommendations, or publication post search. For recommendations, each publication is the source.

## `postUrls` (type: `array`):

Enter one or more canonical public post URLs. For `post`, each URL returns a post. For `postComments`, each URL supplies its public comment thread.

## `postQuery` (type: `string`):

Optional for `publicationArchive` and required for `publicationSearch`. Enter text found in a post title or subtitle. Other targets ignore this field.

## `dateFrom` (type: `string`):

Optional lower bound for post publication dates. It applies to post targets. Use a UTC calendar date in `YYYY-MM-DD` form.

## `dateTo` (type: `string`):

Optional upper bound for post publication dates. It applies to post targets. Use a UTC calendar date in `YYYY-MM-DD` form.

## `audience` (type: `string`):

Optional filter for post results. Choose all audiences, free posts only, or paid posts only. Discovery and publication-details targets ignore this field.

## `languages` (type: `array`):

Optional language filter for discovered publications and post results. Enter language names or codes. Other targets ignore this field.

## `postTypes` (type: `array`):

Optional post type filter. Choose newsletters, podcasts, threads, or videos. Non-post targets ignore this field.

## `authors` (type: `array`):

Optional author filter for post results. Enter author names or public handles. Non-post targets ignore this field.

## `tags` (type: `array`):

Optional publication tag filter for post results. Enter tag names. Non-post targets ignore this field.

## `minReactions` (type: `integer`):

Optional minimum reaction count for post results. Non-post targets ignore this field.

## `minComments` (type: `integer`):

Optional minimum comment count for post results. Non-post targets ignore this field.

## `minWords` (type: `integer`):

Optional minimum word count for post results. Non-post targets ignore this field.

## `sortBy` (type: `string`):

Optional order for returned posts. Choose newest, oldest, most popular, most engaged, or longest. Targets that do not return posts ignore this field.

## `includeArticleContent` (type: `boolean`):

When true, fetch complete public article content as HTML and readable text when available. It applies to publication archives, post retrieval, and publication post search. Restricted posts return public previews only.

## `maxItems` (type: `integer`):

Optional positive limit on saved rows. It can stop archive, discovery, search, and comment work early. Leave it empty to return all available results until the source is exhausted.

## Actor input object example

```json
{
  "target": "publicationArchive",
  "publicationTargets": [
    "arxivblog"
  ],
  "includeArticleContent": false
}
```

# Actor output Schema

## `results` (type: `string`):

Open the default dataset with successful Substack post, publication, and comment rows.

## `sourceAvailability` (type: `string`):

Open the run report for unavailable sources and fetch failures.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "target": "publicationArchive",
    "publicationTargets": [
        "arxivblog"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/substack-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "target": "publicationArchive",
    "publicationTargets": ["arxivblog"],
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/substack-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "target": "publicationArchive",
  "publicationTargets": [
    "arxivblog"
  ]
}' |
apify call maximedupre/substack-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maximedupre/substack-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ESm7vffVF0Q0Tkdkk/builds/xS7WxUlI7Kzkui9St/openapi.json
