# Substack Newsletter Finder: Best Newsletters on Any Topic (`forgelabs/substack-newsletter-intelligence`) Actor

Type a topic, get the most credible Substack newsletters on it, ranked 0-100 by subscribers, engagement, comment depth, consistency and topic fit, with why each ranks, its best on-topic post, and whether it takes sponsors. Also export posts, rank and monitor known newsletters.

- **URL**: https://apify.com/forgelabs/substack-newsletter-intelligence.md
- **Developed by:** [Forge Labs](https://apify.com/forgelabs) (community)
- **Categories:** News, Social media, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 Newsletter Finder: Find the Best Newsletters on Any Topic

**Type a topic. Get the most credible Substack newsletters on it, ranked, with the reason each one ranks and the best post to read first.** You don't need to know any newsletter names.

Just press **Start**. The default topic, "AI startup idea validation", returns a ranked top 10 in under a minute.

### Real example: "AI startup idea validation" (cloud run Oct 15, 2026, 51 s)

| # | Newsletter | Score /100 | Why it ranks | Start with |
|---|---|---|---|---|
| 1 | Lenny's Newsletter | 79.9 | 1.2M subscribers, #1 Business leaderboard, median 474 engagements/post | [How to validate your B2B startup idea](https://www.lennysnewsletter.com/p/how-to-validate-your-b2b-startup) |
| 2 | The VC Corner | 76.3 | 146K subscribers, posts 5x/week, 3 on-topic posts, **takes sponsors** ([page](https://www.thevccorner.com/p/sponsorships)) | [50 AI Agent Startup Ideas](https://www.thevccorner.com/p/ai-agent-startup-ideas-2025) |
| 3 | Andrew Chen | 66.2 | 257K subscribers, Bestseller, 20 comments/post | [The Anti-Pitch: When haters hate your startup idea](https://andrewchen.substack.com/p/the-anti-pitch-one-liner-rebuttals) |
| 4 | Innovation & Strategy Lab | 63.2 | small, but its post is exactly on-topic | [The Startup idea validation framework (free worksheet)](https://innovationstrategylab.substack.com/p/free-worksheet-the-startup-idea-validation) |
| 5 | Product Growth (Aakash Gupta) | 63.2 | 241K subscribers, Bestseller | [How to Build an AI Startup in 2026](https://www.news.aakashg.com/p/how-to-build-an-ai-startup) |

Scores change as newsletters publish. Re-run for fresh results.

### How the credibility score works (transparent, 0 to 100)

| Signal | Weight | What it measures |
|---|---|---|
| Topic fit | 30 | How strongly and how often Substack search matches this newsletter's posts to your topic |
| Audience | 20 | Public subscriber count or followers (log scale), plus Substack Bestseller status |
| Engagement per post | 15 | Median likes + comments + restacks over the last 30 posts |
| Comment depth | 15 | Average comments per post. Real discussion is hard to fake. |
| Consistency | 10 | Posts per week |
| Recency | 10 | Days since the last post |

Every row includes `scoreBreakdown`, `whyItRanks` and its weakest signal, so you can disagree with the ranking and re-sort.

### Sponsor-ready? (for sponsors, agencies and newsletter-ad buyers)

Each row also has `takesSponsors` (yes / likely / not detected), `advertisePage`, `sponsorMentionsLast30Posts`, `sponsorEvidence` and `publicContactEmail`. "Yes" means we found an advertise or sponsor page, or sponsor mentions in at least 2 of the last 30 posts. Turn on **Only newsletters that take sponsors** to get a ranked, sponsor-ready shortlist in which credibility, engagement and comment depth come first, not just audience size. "Not detected" means we found no public signal. Some newsletters sell sponsorships privately.

### Ideas for topics

"B2B SaaS pricing", "climate tech", "AI agents", "personal finance for doctors", "indie hacking", "product-led growth". Add `extraSearchQueries` such as "customer discovery" or "zero to one" to widen the search. The tool automatically searches your exact phrase and gives rare words (e.g. "investing" in "climate tech investing") more weight than common ones, so a newsletter that only matches the broad word ranks lower.

### Already know the newsletters? Other modes

**Which Substack newsletters actually get read?** Give this Actor a list of newsletters. It ranks them by real engagement, posting frequency, followers and public subscriber count. It can also watch them and return only new posts, or export every post with likes, comments and restacks as clean CSV.

It uses public data only, with no login, cookies, browser or proxy. It handles custom domains, finishes in seconds, and charges per result with no start fee.

### What you can do

| Mode | You get | Typical use |
|---|---|---|
| `rankNewsletters` | One profile row per newsletter, sorted, with a `rank` field | Sponsor shortlists, competitor benchmarks, lead lists |
| `newSinceLastRun` | Only posts published since the previous run | Daily monitoring, Slack or webhook alerts, AI-agent feeds |
| `posts` | One row per post with engagement and optional full text | Content research, datasets, RAG corpora |

### Who it's for

- **Sponsors and ad buyers:** compare newsletters on engagement per post, not just audience size, before you buy a placement.
- **Newsletter operators:** benchmark competitors' cadence, paywall share and top-performing posts.
- **Marketers, PR and investors:** monitor a watchlist of newsletters and get only what's new.
- **Researchers and AI builders:** get flat, typed rows ready for CSV, Sheets, pandas or an LLM.

### Real example output

A `rankNewsletters` run on Oct 15, 2026, sampling 30 posts per newsletter:

| # | Newsletter | Public subscribers | Followers | Posts/week | Avg engagement | Top post engagement |
|---|---|---|---|---|---|---|
| 1 | Astral Codex Ten | hidden | 296,737 | 4.32 | **655.7** | 3,004 |
| 2 | Noahpinion | 460,000 | 686,203 | 3.5 | **553.1** | 922 |
| 3 | Lenny's Newsletter | 1,200,000 | 1,384,977 | 0.83 | **527.2** | 1,246 |
| 4 | Slow Boring | 206,000 | 362,238 | 14.5 | **396.8** | 1,025 |
| 5 | The Generalist | 169,000 | 213,187 | 0.9 | **99.9** | 231 |

Lenny's Newsletter has the most subscribers, but Astral Codex Ten gets more engagement per post. This is the kind of insight a plain scraper won't give you.

Input:

```json
{
  "mode": "rankNewsletters",
  "publications": ["lenny", "astralcodexten", "noahpinion", "thegeneralist", "www.slowboring.com"],
  "maxPostsPerPublication": 30,
  "rankBy": "avgEngagement"
}
```

A post row from a `posts` run, shortened (real data):

```json
{
  "publication": "noahpinion.substack.com",
  "title": "AI vs. the railroads",
  "subtitle": "Are we in 1872?",
  "url": "https://www.noahpinion.blog/p/ai-vs-the-railroads",
  "postDate": "2026-10-15T08:47:39.523Z",
  "type": "newsletter",
  "audience": "only_paid",
  "isPaywalled": true,
  "authors": "Noah Smith",
  "wordcount": 3060,
  "reactionCount": 232,
  "commentCount": 69,
  "restacks": 17,
  "engagement": 318
}
```

### How it compares

This table is based on each Actor's public Store listing as of Oct 15, 2026. Check the current listings, since they change.

| | **This Actor** | automation-lab/substack-scraper | easyapi/substack-posts-scraper | fatihtahta/substack-scraper |
|---|---|---|---|---|
| Rank a list of newsletters | ✅ built in | ❌ | ❌ | ❌ |
| Only new posts since last run | ✅ built in (stateful) | date filters only | ❌ | ❌ |
| Computed stats (posts/week, avg and median engagement, top post) | ✅ | ❌ | ❌ | ❌ |
| Engagement (likes, comments, restacks) | ✅ | ✅ | ✅ | ✅ |
| Public subscriber count, followers, leaderboard rank | ✅ | subscriber count | ❌ | subscriber count |
| Flat CSV-ready rows (no nested JSON) | ✅ | nested comments | not stated | not stated |
| Comment threads | ❌ | ✅ | ❌ | not stated |
| Keyword discovery | ❌ | ✅ | ✅ | ✅ (search) |
| Start fee | **none** | $0.005 per run | $0.09 per run | none |
| Price per 1,000 posts (free plan) | $1.50 | $1.15 | $4.99 | $2.50 |

If you need comment threads or keyword search, the alternatives above are a good fit. If you need to decide which newsletters matter, or keep watch on them, use this Actor.

### Pricing (pay per result, no start fee)

Discover by topic: **$0.02 per ranked newsletter** (`newsletter-ranked` event), so a top 10 costs $0.20. Each row is a scored and explained recommendation with its best posts and sponsor signal, not a raw record.

| Event | Price |
|---|---|
| Post (metadata and engagement) | $0.0015 ($1.50 per 1,000) |
| Post with full text | $0.003 ($3.00 per 1,000) |
| Newsletter profile (rank mode or `includeProfiles`) | $0.005 |

Worked examples:

- **Rank 20 newsletters** (30 posts sampled each): 20 profiles × $0.005 = **$0.10**. Posts sampled for stats aren't charged.
- **Export 1,000 posts:** **$1.50**, or $3.00 with full text.
- **Monitor 10 newsletters daily** (about 1 new post each per day): about 300 posts a month = **about $0.45/month**. Runs with nothing new cost $0.

### Input

| Field | Description |
|---|---|
| `mode` | `posts` (default), `rankNewsletters` or `newSinceLastRun` |
| `publications` | Subdomains (`astralcodexten`), `*.substack.com` URLs, or custom domains (`www.slowboring.com`) |
| `maxPostsPerPublication` | Posts per newsletter. Default 50, or 30 in rank mode. Max 5,000 |
| `publishedAfter` | Optional ISO date, such as `2026-01-01` |
| `includeBodyText` | Full plain text of free posts. Paywalled posts are never bypassed |
| `includePaidPosts` | Include metadata for paywalled posts (default on) |
| `includeProfiles` | In posts and monitor modes, also save profiles to the `PROFILES` key-value record |
| `rankBy` | `avgEngagement`, `medianEngagement`, `topPostEngagement`, `postsPerWeek`, `followerCount`, `subscriberCountPublic` |
| `monitorStateName` | Name of the store that keeps monitor checkpoints. Use one name per watchlist |

### Output fields

**Post rows:** `publication, postId, title, subtitle, url, postDate, type, audience, isPaywalled, authors, tags, section, wordcount, reactionCount, commentCount, restacks, engagement, podcastDurationSec, coverImage, previewText, bodyText, isNew, scrapedAt`

**Profile rows:** `rank, rankedBy, name, publication, url, tagline, customDomain, language, createdAt, paidSubscriptionsEnabled, authorName, authorHandle, authorBio, subscriberCountPublic, subscriberCountLabel, paidSubscribersRange, followerCount, bestsellerTier, leaderboardCategory, leaderboardRank, twitter, socialLinks, publicContactEmail, postsAnalyzed, firstPostAnalyzed, lastPostDate, daysSinceLastPost, postsPerWeek, paywalledShare, avgLikes, avgComments, avgEngagement, medianEngagement, topPostTitle, topPostUrl, topPostEngagement, error`

Lists are joined with `; ` so every cell is plain text. The dataset has three ready-made views: Overview, Posts and Newsletter profiles.

### Use with AI agents (MCP and API)

**MCP** (Claude, Cursor, VS Code and other MCP clients):

```json
{ "mcpServers": { "apify": { "url": "https://mcp.apify.com/?tools=forgelabs/substack-newsletter-intelligence" } } }
```

Example prompts:

- "Rank these 10 AI newsletters by average engagement and tell me which 3 to sponsor."
- "Check my Substack watchlist and summarize anything published since yesterday."

**API (one call, results returned directly):**

```bash
curl -X POST "https://api.apify.com/v2/acts/forgelabs~substack-newsletter-intelligence/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"rankNewsletters","publications":["lenny","noahpinion"]}'
```

Add `&format=csv` to get CSV.

**Monitoring recipe:** create a Task with `"mode": "newSinceLastRun"`, schedule it daily, and attach a Slack, email or webhook integration that fires when a run succeeds.

### FAQ

**Is this allowed?** It reads only what Substack serves publicly without login: archive listings, public posts and public profiles. It never touches paywalled content or private subscriber lists. You're responsible for how you use the data, including copyright when reusing text.

**Why is `subscriberCountPublic` empty for some newsletters?** Some authors hide the count. In that case, `paidSubscribersRange` (Substack's own public range) and `followerCount` are still filled in.

**What does "engagement" mean?** Likes, comments and restacks added together for each post.

**What happens with a newsletter that left Substack?** You get a row with an `error` field, and the rest of the run continues.

**How does monitoring remember what it has seen?** The last post date for each newsletter is saved in a named key-value store on your account (`monitorStateName`). The first run returns recent posts and sets the checkpoint.

**Custom domains?** Yes, for example `www.slowboring.com` or `www.lennysnewsletter.com`.

### Support

Found a bug or need a field? Open an issue on the Issues tab.

# Actor input Schema

## `mode` (type: `string`):

Discover by topic = type a topic, get the most credible Substack newsletters on it, ranked with a transparent score and their best posts. Other modes work on newsletters you already know.

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

What do you want to read about? e.g. "AI startup idea validation", "B2B SaaS pricing", "climate tech investing".

## `maxNewsletters` (type: `integer`):

Top N ranked newsletters (Discover mode).

## `extraSearchQueries` (type: `array`):

More phrasings of your topic to widen the search, e.g. "zero to one", "customer discovery".

## `searchDepth` (type: `integer`):

More pages = more candidate newsletters, slower run.

## `language` (type: `string`):

e.g. "en". Leave empty for any.

## `onlySponsorReady` (type: `boolean`):

Keep only newsletters with an advertise/sponsor page or sponsor mentions in recent posts. Useful for sponsorship buyers and agencies.

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

Only for Posts / Rank / Monitor modes. Substack subdomains (e.g. 'astralcodexten'), \*.substack.com URLs, or custom domains (e.g. 'www.lennysnewsletter.com').

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

Max posts per publication.

## `publishedAfter` (type: `string`):

Optional date filter.

## `includeBodyText` (type: `boolean`):

Adds plain text of publicly readable posts (paywalled posts return only the public preview).

## `includePaidPosts` (type: `boolean`):

Include paywalled posts (metadata only).

## `rankBy` (type: `string`):

Metric used to sort newsletters in rankNewsletters mode.

## `includeProfiles` (type: `boolean`):

In posts/monitor modes, also save one profile per newsletter (subscriber count if public, posting frequency, engagement stats, author and public social links) to the PROFILES record of the default key-value store.

## `monitorStateName` (type: `string`):

Named key-value store that remembers the last seen post per newsletter. Use different names for independent monitors.

## Actor input object example

```json
{
  "mode": "discoverByTopic",
  "topic": "AI startup idea validation",
  "maxNewsletters": 10,
  "searchDepth": 4,
  "onlySponsorReady": false,
  "publications": [
    "www.lennysnewsletter.com",
    "astralcodexten"
  ],
  "maxPostsPerPublication": 50,
  "includeBodyText": false,
  "includePaidPosts": true,
  "rankBy": "avgEngagement",
  "includeProfiles": false,
  "monitorStateName": "substack-monitor-state"
}
```

# Actor output Schema

## `ranked` (type: `string`):

Discover-by-topic results: newsletters ranked by credibility score with reasons.

## `summary` (type: `string`):

Readable top-N list with why each ranks (Discover mode).

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

All rows in the default dataset (posts or newsletter profiles, depending on mode).

## `profiles` (type: `string`):

Profile/ranking view of the dataset (rankNewsletters mode).

## `profilesRecord` (type: `string`):

JSON array of publication profiles saved in posts/monitor modes when includeProfiles is on.

# 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 = {
    "topic": "AI startup idea validation"
};

// Run the Actor and wait for it to finish
const run = await client.actor("forgelabs/substack-newsletter-intelligence").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 = { "topic": "AI startup idea validation" }

# Run the Actor and wait for it to finish
run = client.actor("forgelabs/substack-newsletter-intelligence").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 '{
  "topic": "AI startup idea validation"
}' |
apify call forgelabs/substack-newsletter-intelligence --silent --output-dataset

```

## MCP server setup

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

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/Vc1yRvJT5HlIucIBY/builds/vnaAaOhag7djMf9ew/openapi.json
