> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dripstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sources

> GET /api/v2/sources — the curated catalog of newsletters, podcasts, and RSS feeds, and the posts they publish.

A **source** is where Drip reads from: a Substack newsletter, a Beehiiv
publication, a podcast feed, a plain RSS feed, a YouTube channel, or an X
timeline. Every post in the Drip feed belongs to a source, and every analyst
call is attributed to one.

Sources are the entry point of the API. Resolve a source, read its recent
posts, unlock the ones you want, then pivot to
[stock picks](https://docs.dripstack.com/v2/stock-picks.md) for the analyst calls behind them.

| Route | Access |
| - | - |
| `GET /api/v2/sources` | Free, no auth |
| `GET /api/v2/sources/search` | Free, no auth |
| `GET /api/v2/sources/{sourceSlug}` | Free, no auth |
| `GET /api/v2/sources/{sourceSlug}/{postSlug}` | Paid (credits or subscription — account mount) |
| `GET /api/v2/x402/sources/{sourceSlug}/{postSlug}` | Paid (x402 / MPP — wallet mount) |
| `GET /api/v2/sources/{sourceSlug}/stock-picks` | Subscription (PRO/EXPERT) or credits (account mount) |
| `GET /api/v2/x402/sources/{sourceSlug}/stock-picks` | Paid (x402 / MPP — wallet mount) |

Payment methods are split by mount: the plain `/api/v2/...` paid routes accept
`Bearer` API keys, session JWTs, and MCP OAuth only; the `/api/v2/x402/...`
twins accept wallet payments (x402 / MPP) only. Wrong-mount credentials get
`403 account_only` / `403 wallet_only`.

## Why "source" and not "publication"

A publication is a Substack. A source is anything Drip reads from — a
podcast has no posts on Substack, and a YouTube channel has no feed at all.
V1 called all of them `publications`, which was wrong for podcasts and
misleading for everything else. V2 uses one word for the whole set.

The v1 routes still work and are unchanged. See
[Legacy publication routes](https://docs.dripstack.com/publications.md) for the V1 shapes.

## `GET /api/v2/sources`

Lists the curated, chat-visible catalog. Free, no auth.

```bash theme={null}
curl "https://dripstack.com/api/v2/sources"
```

```json theme={null}
{
  "sources": [
    {
      "slug": "bytesbeyondborders.substack.com",
      "title": "Bytes and Borders",
      "description": "A weekly deep dive on the bytes that move markets.",
      "siteUrl": "https://bytesbeyondborders.substack.com",
      "platform": "SUBSTACK",
      "lastSyncedAt": "2026-10-12T08:30:00.000Z"
    }
  ]
}
```

| Field | Meaning |
| - | - |
| `slug` | Normalized host — the id you pass to every other source route |
| `title` | Display name |
| `description` | Short description from the feed |
| `siteUrl` | Canonical source URL |
| `platform` | Source type: `SUBSTACK`, `BEEHIIV`, `PODCAST_RSS`, `RSS`, … |
| `lastSyncedAt` | ISO timestamp of the last successful feed sync |

`platform` is how you tell a podcast from a newsletter: `PODCAST_RSS` marks a
podcast feed. There is no dedicated podcast route in V2 — filter the catalog
on `platform` instead.

## `GET /api/v2/sources/search`

Resolves a source by name, author, show title, or URL. Free, no auth.

```bash theme={null}
curl "https://dripstack.com/api/v2/sources/search?q=Unchained"
curl "https://dripstack.com/api/v2/sources/search?q=daily&platform=PODCAST_RSS"
```

```json theme={null}
{
  "query": "Unchained",
  "count": 1,
  "items": [
    {
      "sourceSlug": "unchainedcrypto.beehiiv.com",
      "title": "Unchained Daily Newsletter",
      "author": "Unchained",
      "siteUrl": "https://unchainedcrypto.beehiiv.com"
    }
  ]
}
```

`q` is required and must be at least 2 characters. At most **3** matches come
back, ranked. If several look plausible, ask the user which one they meant
rather than guessing.

`platform` is an optional exact-match filter on the source type — same values
as the catalog's `platform` field: `SUBSTACK`, `BEEHIIV`, `GENERIC_RSS`,
`PODCAST_RSS`, `EMAIL`, `TWITTER`, `BOOK`, `YOUTUBE`. Use `platform=PODCAST_RSS`
to find podcast shows. An unknown value returns `400`.

## `GET /api/v2/sources/{sourceSlug}`

One source plus its recent posts. Free, no auth.

```bash theme={null}
curl "https://dripstack.com/api/v2/sources/bytesbeyondborders.substack.com?limit=20"
```

```json theme={null}
{
  "slug": "bytesbeyondborders.substack.com",
  "title": "Bytes and Borders",
  "description": "A weekly deep dive on the bytes that move markets.",
  "siteUrl": "https://bytesbeyondborders.substack.com",
  "imageUrl": "https://bytesbeyondborders.substack.com/img/cover.png",
  "language": "en",
  "authorName": "Bytes and Borders",
  "authorEmail": null,
  "copyright": null,
  "lastSyncedAt": "2026-10-12T08:30:00.000Z",
  "posts": [
    {
      "slug": "the-chip-export-controls-question",
      "title": "The chip export controls question",
      "subtitle": "What actually changes for semis.",
      "publishedAt": "2026-10-10T12:00:00.000Z",
      "priceCents": 50,
      "isPodcast": false
    }
  ]
}
```

* Works for **any** enabled indexed source, not just the curated catalog.
* `limit` is 1-100. Omit it to get every post with a ready summary.
* `isPodcast` marks podcast episodes — those unlock to a `transcript` rather
  than only a summary.
* `priceCents` is what unlocking that post costs. Show it as `$X.XX` and
  confirm before paying.
* `404` when the source is unknown or disabled.

## `GET /api/v2/sources/{sourceSlug}/{postSlug}` (paid)

The full post: metadata plus the `synthesizedSummary` (and `transcript` for
podcast episodes) after payment.

```bash theme={null}
# 1. See the price and the summary shape before you commit
curl "https://dripstack.com/api/v2/sources/bytesbeyondborders.substack.com/the-chip-export-controls-question"

# 2. Pay and read
curl -H "Authorization: Bearer pk_drip_..." \
  "https://dripstack.com/api/v2/sources/bytesbeyondborders.substack.com/the-chip-export-controls-question"
```

The body uses `sourceId` and `sourceSlug` where V1 used `publicationId` and
`publicationSlug`. Everything else matches.

| Status | Meaning |
| - | - |
| `200` | Unlocked content |
| `402` | Payment required — no credential, or the credential can't pay |
| `403` | Credits exhausted, or the API key has no paid plan |
| `404` | Source or post not found |
| `503` | Summary not ready yet — retry later, no payment is taken |

The first successful pay unlocks that post **forever** for the paying subject.
Later reads are free. Store the `unlockToken` (or the `X-Drip-Unlock` response
header) and send it back for free re-reads. API key, JWT, and MCP OAuth all
share one Drip-user entitlement.

## `GET /api/v2/sources/{sourceSlug}/stock-picks` (paid)

The source's analyst profile: pick totals, direction-adjusted performance, win
rate, per-author split, top picks, and the latest calls.

```bash theme={null}
curl -H "Authorization: Bearer pk_drip_..." \
  "https://dripstack.com/api/v2/sources/bytesbeyondborders.substack.com/stock-picks"
```

* **PRO / EXPERT** subscribers are served **free**.
* Everyone else pays one flat price per call via x402 or MPP.
* The body uses a `source` block where V1 used `publication`.

For per-ticker analyst calls rather than a per-source profile, use
[Stock Picks](https://docs.dripstack.com/v2/stock-picks.md).

## Hosted MCP

The same routes are available as MCP tools:

| Tool | Route |
| - | - |
| `list_sources` | `GET /api/v2/sources` |
| `search_sources` | `GET /api/v2/sources/search` |
| `get_source` | `GET /api/v2/sources/{sourceSlug}` |
| `unlock_post` | `GET /api/v2/sources/{sourceSlug}/{postSlug}` |

`unlock_post` takes `publicationSlug` and `postSlug` — the argument name
predates the source rename and is shared with the in-app chat tool.

## Errors

| Status | Meaning |
| - | - |
| `400` | Invalid query params (`q` too short, `limit` out of range) |
| `401` | Invalid or missing credential on a paid route |
| `402` | Payment required on a paid route |
| `403` | Credits exhausted, or no paid plan for an API key |
| `404` | Source or post not found |
| `503` | Summary not ready yet — retry later, no payment is taken |

Non-402 errors return `{ "error", "code", "hint" }`. `error` is the human
message, `code` is machine-readable, `hint` points at recovery.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.