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

# Introduction

> Stream-centred API for managing lead-streaming campaigns, extracting leads, and integrating Catch The Good Ones with your stack.

# Catch The Good Ones API

The Catch The Good Ones API is **stream-centred**. A **stream** is a campaign: a named operational unit that owns N `(tracked_account, segment)` pairs and a shared schedule + enrichment + delivery config. Three primary verbs frame the surface:

* **Create streams** with `POST /api/v2/streams` (one-shot: handles + segments + everything)
* **Update streams** with `PATCH /api/v2/streams/{id}` (any setting, including full-replace pairs)
* **Extract leads** with `GET /api/v2/streams/{id}/leads` (per-stream) or `GET /api/v2/leads` (cross-stream polling)

Use it to pipe your leads into Zapier, Make, n8n, HeyReach, Apollo, Clay, or your own applications.

## Base URL

```
https://www.catchthegoodones.com/api/v2
```

API v1 paths return **410 Gone**. See the [Migration guide](/api-reference/migration-v1-to-v2) for old-to-new mappings.

## Quick start

1. Generate an API key from your [API Key page](https://www.catchthegoodones.com/settings/api)
2. Verify the key:

```bash theme={null}
curl https://www.catchthegoodones.com/api/v2/me \
  -H "Authorization: Bearer ctgo_your_api_key_here"
```

3. Create a campaign in one call:

```bash theme={null}
curl -X POST https://www.catchthegoodones.com/api/v2/streams \
  -H "Authorization: Bearer ctgo_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "AI founders watching Yongfook",
    "pairs": [
      {
        "accountHandle": "yongfook",
        "accountTier": "starter",
        "segment": {
          "description": "AI founders building developer tools"
        }
      }
    ],
    "autoEnrichLinkedin": true,
    "autoEnrichEmail": true
  }'
```

4. Pull leads from the campaign:

```bash theme={null}
curl https://www.catchthegoodones.com/api/v2/streams/123/leads?lifecycle=new \
  -H "Authorization: Bearer ctgo_your_api_key_here"
```

## Endpoints by user goal

### Set up a campaign

| Method | Endpoint           | Purpose                                                       |
| ------ | ------------------ | ------------------------------------------------------------- |
| `POST` | `/api/v2/streams`  | Create a campaign (one-shot: handles + segments + everything) |
| `POST` | `/api/v2/segments` | Create a reusable segment to add to streams later             |
| `GET`  | `/api/v2/segments` | List the segments you've authored                             |
| `GET`  | `/api/v2/accounts` | List your tracked accounts (active + sleeping)                |

### Manage campaigns

| Method   | Endpoint                       | Purpose                                                                  |
| -------- | ------------------------------ | ------------------------------------------------------------------------ |
| `GET`    | `/api/v2/streams`              | List your active campaigns                                               |
| `GET`    | `/api/v2/streams/{id}`         | Inspect a campaign                                                       |
| `PATCH`  | `/api/v2/streams/{id}`         | Update any setting, including full-replace pairs                         |
| `DELETE` | `/api/v2/streams/{id}`         | Archive a campaign                                                       |
| `POST`   | `/api/v2/streams/{id}/run`     | Trigger a sync across the campaign's pairs                               |
| `POST`   | `/api/v2/streams/{id}/re-test` | Re-classify cached followers (optional `{accountId?, segmentId?}` scope) |

### Monitor campaigns

| Method | Endpoint                            | Purpose                                 |
| ------ | ----------------------------------- | --------------------------------------- |
| `GET`  | `/api/v2/streams/{id}/runs`         | Sync history for one campaign           |
| `GET`  | `/api/v2/streams/{id}/runs/{runId}` | Single run status + progress log        |
| `GET`  | `/api/v2/runs`                      | Flat sync-run list across all campaigns |

### Extract leads

| Method | Endpoint                      | Purpose                                                                                                                                                     |
| ------ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/api/v2/streams/{id}/leads`  | Per-campaign leads, with `?lifecycle`, `?matchConfidence`, `?accountId`, `?segmentId`, `?since`, `?limit`, `?offset`. Pass `?summary=true` for counts only. |
| `GET`  | `/api/v2/leads`               | Cross-campaign poll. Supports `?streamId`, `?since`, `?lifecycle`, `?groupBy=stream`.                                                                       |
| `POST` | `/api/v2/leads/{id}/feedback` | Good / bad feedback (informs future classification)                                                                                                         |
| `POST` | `/api/v2/leads/{id}/enrich`   | On-demand enrichment of one lead                                                                                                                            |
| `POST` | `/api/v2/leads/{id}/export`   | Mark a lead exported when you move it into your CRM                                                                                                         |

### Account management

| Method | Endpoint                            | Purpose                                       |
| ------ | ----------------------------------- | --------------------------------------------- |
| `GET`  | `/api/v2/accounts`                  | List tracked accounts                         |
| `POST` | `/api/v2/accounts/{id}/sleep`       | Sleep an account (decrements Stripe quantity) |
| `POST` | `/api/v2/accounts/{id}/wake`        | Wake a sleeping account                       |
| `POST` | `/api/v2/accounts/{id}/change-tier` | Upgrade or downgrade an account's tier        |

### Account + billing visibility

| Method | Endpoint     | Purpose                                                         |
| ------ | ------------ | --------------------------------------------------------------- |
| `GET`  | `/api/v2/me` | Identity + per-account budgets + credit balance + totals rollup |

### Webhooks

| Method   | Endpoint                     | Purpose                                                                                                                            |
| -------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `POST`   | `/api/v2/webhooks/subscribe` | Subscribe a webhook for real-time lead delivery. Filter: `{streamId?, accountId?, sourceType?, feedbackStatus?, matchConfidence?}` |
| `DELETE` | `/api/v2/webhooks/subscribe` | Unsubscribe                                                                                                                        |

### DM templates + notification settings

| Method             | Endpoint                        | Purpose                                   |
| ------------------ | ------------------------------- | ----------------------------------------- |
| `GET, POST`        | `/api/v2/dm-templates`          | List / create canned DM templates         |
| `GET, PUT, DELETE` | `/api/v2/dm-templates/{id}`     | Single-template CRUD                      |
| `GET, PUT`         | `/api/v2/notification-settings` | Team-default summary frequency + channels |

### Lookups (Chrome extension surface)

One-shot enrichment + segment-from-description for the extension. Distinct from the campaign-centric surface above.

| Method   | Endpoint                                    | Purpose                                              |
| -------- | ------------------------------------------- | ---------------------------------------------------- |
| `POST`   | `/api/v2/lookups/enrich`                    | Look up LinkedIn URL + email for an X handle         |
| `POST`   | `/api/v2/lookups/enrich-email`              | Email-only lookup                                    |
| `GET`    | `/api/v2/lookups/enrich/history`            | Recent LinkedIn lookups                              |
| `GET`    | `/api/v2/lookups/enrich-email/history`      | Recent email lookups                                 |
| `POST`   | `/api/v2/lookups/segments/from-description` | Create a segment from a natural-language description |
| `DELETE` | `/api/v2/lookups/api-keys/{id}`             | Self-revoke (extension sign-out)                     |

## Async operations

Some operations run asynchronously and return a run ID for polling:

* `POST /api/v2/streams/{id}/run` returns `{queued: [{accountId, syncRunId}], skipped: [{accountId, reason}]}`. Poll each with `GET /api/v2/streams/{id}/runs/{runId}`.
* `POST /api/v2/streams/{id}/re-test` returns the same shape. Poll the same way.

## Billing effects

Several operations affect your Stripe subscription:

* `POST /api/v2/streams` with new handles - upserts tracked accounts + increments Stripe quantity (response carries `billingDelta` per account)
* `PATCH /api/v2/streams/{id}` with new handles in `pairs` - same
* `POST /api/v2/accounts/{id}/wake` - increments Stripe quantity
* `POST /api/v2/accounts/{id}/sleep` - decrements Stripe quantity
* `POST /api/v2/accounts/{id}/change-tier` - upgrades apply immediately with proration; downgrades schedule for end-of-period

## Rate limits

60 requests per minute per Bearer key. Response headers:

| Header                  | Description                                   |
| ----------------------- | --------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests per window (60)              |
| `X-RateLimit-Remaining` | Requests remaining in current window          |
| `Retry-After`           | Seconds until the window resets (only on 429) |

In-app session-cookie calls aren't rate-limited at this layer.

## Errors

Paywall and validation errors return a structured `{ error: '<code>', message: '<copy>' }` body. Branch on the code, not the message text. See the [Migration guide](/api-reference/migration-v1-to-v2#paywall-error-codes) for the full list.

Standard HTTP status codes apply:

| Status | Description                                               |
| ------ | --------------------------------------------------------- |
| `200`  | Success                                                   |
| `201`  | Created                                                   |
| `400`  | Invalid request (bad parameters or body)                  |
| `401`  | Missing, invalid, or revoked API key                      |
| `403`  | Forbidden (subscription required, budget exhausted, etc.) |
| `404`  | Resource not found                                        |
| `409`  | Conflict (duplicate, concurrent operation)                |
| `410`  | Endpoint removed (see migration guide)                    |
| `422`  | Unprocessable (e.g. private account)                      |
| `429`  | Rate limit exceeded or credits exhausted                  |
| `502`  | Upstream error (Stripe, etc.)                             |
| `503`  | Service temporarily unavailable                           |
