# Substack Scraper - Posts, Engagement & Category Leaderboards (`cleandatalabs/substack-newsletters`) Actor

Scrape Substack posts with likes, comments, restacks, word count and free/paid flag, plus ranked top newsletters in 32 categories with subscriber tier and paid status. No login. From $1 per 1,000 posts.

- **URL**: https://apify.com/cleandatalabs/substack-newsletters.md
- **Developed by:** [Clean Data Labs](https://apify.com/cleandatalabs) (community)
- **Categories:** Social media, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Substack Scraper: Posts, Likes and Comments, Plus Top Newsletters by Category

**Scrape any [Substack](https://substack.com) newsletter's posts with likes, comments, restacks, word count and free/paid flag, and pull the ranked top newsletters in 32 Substack categories** with subscriber tier, paid-subscriber band, author and launch date. Export to JSON, CSV or Excel, or call it from the API. No login, no cookies, no proxies, from **$1 per 1,000 posts** with no start fee.

*Built and maintained by **[Clean Data Labs](https://apify.com/cleandatalabs)**.*

### Ready-made examples (one click to run)

| Example | What it returns |
|---|---|
| [Top Substack Technology newsletters with subscriber tiers](https://apify.com/cleandatalabs/substack-newsletters/examples/top-substack-technology-newsletters) | Ranked Technology leaderboard with subscriber tiers |
| [Top Substack Finance newsletters for sponsorship outreach](https://apify.com/cleandatalabs/substack-newsletters/examples/top-substack-finance-newsletters) | Finance leaderboard, a ready sponsorship list |
| [Top Substack Crypto newsletters](https://apify.com/cleandatalabs/substack-newsletters/examples/top-substack-crypto-newsletters) | Crypto leaderboard |
| [Substack posts with likes and comments from tech newsletters](https://apify.com/cleandatalabs/substack-newsletters/examples/substack-posts-engagement-tech-newsletters) | Latest posts with engagement from three tech newsletters |
| [Recent free posts from top Substack Business newsletters](https://apify.com/cleandatalabs/substack-newsletters/examples/substack-free-posts-business-leaders) | Top Business newsletters plus their latest free posts |

### Who it's for

- **Marketers and agencies** prospecting newsletters for sponsorships, partnerships and guest posts.
- **Creators and media teams** tracking competitors and benchmarking engagement.
- **Content strategists and researchers** studying which topics earn the most likes and comments.
- **Data teams** building datasets of top newsletters per niche.

#### Popular use cases

- Pull the top newsletters in Technology, Finance, Crypto, Politics and other categories, ranked, with subscriber tiers.
- Export every recent post from a list of newsletters with engagement metrics for analysis.
- Monitor competitors' posting cadence, free vs. paid mix and audience response.
- Build a sponsorship target list with author names, descriptions and paid-subscriber bands.

### Features

- **Two modes in one actor:** posts from newsletters you choose, and ranked category leaderboards.
- **Engagement metrics:** likes, comments, restacks and word count on every post.
- **Leaderboards for 32 Substack categories**, with rank, subscriber tier (e.g. "1.1M+"), paid-subscriber band, bestseller tier, author, language and launch date.
- **Flexible publication input:** full URLs or bare subdomains (e.g. `pragmaticengineer`).
- **Filters:** posted-after date and free/paid audience.
- **Chain both modes:** optionally pull the latest N posts from every newsletter found on a leaderboard.
- **Public data only:** no login, cookies or proxies. Paywalled post content is never accessed.

### Input example

```json
{
  "publications": ["https://blog.bytebytego.com", "pragmaticengineer", "https://www.lennysnewsletter.com/"],
  "maxPostsPerPublication": 15,
  "postedAfter": "2026-09-01",
  "maxResults": 100
}
```

| Input | What it does |
|---|---|
| `publications` | Newsletter URLs or subdomains to fetch posts from |
| `maxPostsPerPublication` | Latest posts per publication (default 50) |
| `postedAfter` | Only posts on or after this date (YYYY-MM-DD) |
| `audience` | `all`, `free` or `paid` posts |
| `categories` | Substack category leaderboards to pull |
| `maxPublicationsPerCategory` | Top newsletters per category (default 50) |
| `postsFromCategoryPublications` | Also fetch the latest N posts from each leaderboard newsletter (0 = none) |
| `maxResults` | Hard cap on rows across the run; you are charged per row (default 500) |

### Output fields

#### Post rows (`record_type: "post"`)

| Field | Description |
|---|---|
| `publication_name` / `publication_url` | Newsletter name and URL |
| `post_id` / `title` / `subtitle` / `url` | Post identity and link |
| `post_date` | Publication time (UTC) |
| `type` / `audience` | Post type (e.g. newsletter, podcast) and `free` or paid audience |
| `authors` / `section` / `tags` | Bylines, section and tags |
| `likes` / `comments` / `restacks` | Engagement metrics |
| `wordcount` / `podcast_duration_sec` | Length of the post or podcast |
| `preview_text` / `cover_image` | Preview text and cover image URL |
| `scraped_at` | When the row was collected (UTC) |

#### Newsletter rows (`record_type: "publication"`)

| Field | Description |
|---|---|
| `category` / `category_rank` | Leaderboard and position |
| `publication_name` / `url` / `subdomain` | Newsletter identity |
| `author_name` / `description` / `language` | Author, tagline and language |
| `subscribers_tier` / `subscribers_min` | Subscriber tier label and its lower bound |
| `ranking_detail` | Paid-subscriber band, e.g. "Tens of thousands of paid subscribers" |
| `bestseller_tier` / `paid_enabled` | Bestseller tier and whether paid subscriptions are on |
| `first_post_date` / `logo_url` | Launch date and logo |
| `scraped_at` | When the row was collected (UTC) |

### Sample output

Real rows from test runs on October 31, 2026 (image URLs omitted for display).

```json
{
  "record_type": "post",
  "publication_url": "https://blog.bytebytego.com",
  "publication_name": "ByteByteGo Newsletter",
  "post_id": 222752055,
  "title": "EP231: AI Terms You Need to Know",
  "subtitle": "What else should be on the list?",
  "url": "https://blog.bytebytego.com/p/ep231-ai-terms-you-need-to-know",
  "post_date": "2026-10-31T15:31:08.160Z",
  "type": "newsletter",
  "audience": "free",
  "authors": ["ByteByteGo"],
  "likes": 185,
  "comments": 3,
  "restacks": 4,
  "wordcount": 1084,
  "section": null,
  "tags": [],
  "podcast_duration_sec": null,
  "preview_text": "The all-in-one intelligent cloud (Sponsored)",
  "scraped_at": "2026-11-01T06:02:24.783437+00:00"
}
```

```json
{
  "record_type": "publication",
  "category": "Top in Technology",
  "category_rank": 2,
  "publication_name": "The Pragmatic Engineer",
  "url": "https://newsletter.pragmaticengineer.com",
  "subdomain": "pragmaticengineer",
  "author_name": "Gergely Orosz",
  "description": "Big Tech and startups, from the inside. Highly relevant for software engineers, AI engineers and engineering leaders, useful for those working in tech.",
  "language": "en",
  "subscribers_tier": "1.1M+",
  "subscribers_min": 1000000,
  "ranking_detail": "Tens of thousands of paid subscribers",
  "bestseller_tier": 10000,
  "paid_enabled": true,
  "first_post_date": "2021-08-30T11:24:02.082Z",
  "scraped_at": "2026-11-01T06:02:42.372648+00:00"
}
```

### Pricing

**Pay per result:** you pay only for rows saved to your dataset, with no start fee.

| Result | Price | Per 1,000 |
|---|---|---|
| Post (with engagement metrics) | $0.001 | $1.00 |
| Newsletter (leaderboard row) | $0.003 | $3.00 |

#### Free credit

Apify's free plan includes **$5 of usage credit every month**, no credit card required. That covers up to about 5,000 posts or about 1,600 leaderboard newsletters a month. Use `maxResults` to cap any single run.

### FAQ and troubleshooting

#### A publication returned no posts.

Check the URL or subdomain, and try a full URL such as `https://www.lennysnewsletter.com`. Also check that `postedAfter` and `audience` are not filtering everything out.

#### Can I get the full text of paid posts?

No. Only publicly listed metadata and preview text are collected; paywalled content is never accessed.

#### Why are subscriber counts shown as tiers?

Substack publishes subscriber sizes as tiers (for example "1.1M+") rather than exact counts. `subscribers_min` gives the numeric lower bound.

#### Can I use this as a Substack API?

Yes. Call it from the Apify API, MCP or integrations and get posts and leaderboards as structured JSON rows, built from the same public data you see on Substack pages.

#### How do I find newsletters to sponsor or pitch?

Pick a category such as Finance or Technology in `categories`. Each row has rank, subscriber tier, paid-subscriber band, author and description. See the [Finance example](https://apify.com/cleandatalabs/substack-newsletters/examples/top-substack-finance-newsletters).

#### How do I get posts from a whole category?

Pick `categories` and set `postsFromCategoryPublications` to the number of latest posts you want from each newsletter.

### More from Clean Data Labs

- [Prediction Markets Odds API](https://apify.com/cleandatalabs/prediction-markets-odds): live Kalshi and Polymarket odds, bid/ask and volume in one schema.
- [SAM.gov Contract Opportunities Scraper](https://apify.com/cleandatalabs/samgov-opportunities): live US federal bids with NAICS, set-asides, deadlines and contracting contacts.

### Support

Questions, bugs or feature requests? Email **<clean-data-labs@mail.grokbot.com>** or open an issue on the actor's Issues tab.

*Clean Data Labs: clean, structured public data, ready to use.*

# Actor input Schema

## `publications` (type: `array`):

Newsletter URLs or subdomains, e.g. https://www.lennysnewsletter.com, pragmaticengineer, https://blog.bytebytego.com. Returns their posts.

## `categories` (type: `array`):

Returns the top newsletters listed in each Substack category, with subscriber tier and paid status.

## `maxPublicationsPerCategory` (type: `integer`):

How many top newsletters to return from each category leaderboard.

## `postsFromCategoryPublications` (type: `integer`):

For each newsletter found in a category, also fetch this many of its latest posts. 0 = newsletters only.

## `maxPostsPerPublication` (type: `integer`):

Latest posts to fetch from each publication in the list above.

## `postedAfter` (type: `string`):

Only posts published on or after this date (YYYY-MM-DD).

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

Filter posts by free or paid (paywalled) audience.

## `maxResults` (type: `integer`):

Hard cap on rows across the whole run (you are charged per row).

## Actor input object example

```json
{
  "publications": [
    "https://blog.bytebytego.com"
  ],
  "maxPublicationsPerCategory": 50,
  "postsFromCategoryPublications": 0,
  "maxPostsPerPublication": 50,
  "audience": "all",
  "maxResults": 500
}
```

# Actor output Schema

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

Post rows (likes, comments, restacks, word count, audience) and/or publication leaderboard rows (rank, subscriber tier, paid status).

# 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 = {
    "publications": [
        "https://blog.bytebytego.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleandatalabs/substack-newsletters").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 = { "publications": ["https://blog.bytebytego.com"] }

# Run the Actor and wait for it to finish
run = client.actor("cleandatalabs/substack-newsletters").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 '{
  "publications": [
    "https://blog.bytebytego.com"
  ]
}' |
apify call cleandatalabs/substack-newsletters --silent --output-dataset

```

## MCP server setup

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

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/J7u6zWVTT50Ly5Ws5/builds/3DPHfG8zbnxKpK34S/openapi.json
