Skip to main content

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 for old-to-new mappings.

Quick start

  1. Generate an API key from your API Key page
  2. Verify the key:
curl https://www.catchthegoodones.com/api/v2/me \
  -H "Authorization: Bearer ctgo_your_api_key_here"
  1. Create a campaign in one call:
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
  }'
  1. Pull leads from the campaign:
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

MethodEndpointPurpose
POST/api/v2/streamsCreate a campaign (one-shot: handles + segments + everything)
POST/api/v2/segmentsCreate a reusable segment to add to streams later
GET/api/v2/segmentsList the segments you’ve authored
GET/api/v2/accountsList your tracked accounts (active + sleeping)

Manage campaigns

MethodEndpointPurpose
GET/api/v2/streamsList 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}/runTrigger a sync across the campaign’s pairs
POST/api/v2/streams/{id}/re-testRe-classify cached followers (optional {accountId?, segmentId?} scope)

Monitor campaigns

MethodEndpointPurpose
GET/api/v2/streams/{id}/runsSync history for one campaign
GET/api/v2/streams/{id}/runs/{runId}Single run status + progress log
GET/api/v2/runsFlat sync-run list across all campaigns

Extract leads

MethodEndpointPurpose
GET/api/v2/streams/{id}/leadsPer-campaign leads, with ?lifecycle, ?matchConfidence, ?accountId, ?segmentId, ?since, ?limit, ?offset. Pass ?summary=true for counts only.
GET/api/v2/leadsCross-campaign poll. Supports ?streamId, ?since, ?lifecycle, ?groupBy=stream.
POST/api/v2/leads/{id}/feedbackGood / bad feedback (informs future classification)
POST/api/v2/leads/{id}/enrichOn-demand enrichment of one lead
POST/api/v2/leads/{id}/exportMark a lead exported when you move it into your CRM

Account management

MethodEndpointPurpose
GET/api/v2/accountsList tracked accounts
POST/api/v2/accounts/{id}/sleepSleep an account (decrements Stripe quantity)
POST/api/v2/accounts/{id}/wakeWake a sleeping account
POST/api/v2/accounts/{id}/change-tierUpgrade or downgrade an account’s tier

Account + billing visibility

MethodEndpointPurpose
GET/api/v2/meIdentity + per-account budgets + credit balance + totals rollup

Webhooks

MethodEndpointPurpose
POST/api/v2/webhooks/subscribeSubscribe a webhook for real-time lead delivery. Filter: {streamId?, accountId?, sourceType?, feedbackStatus?, matchConfidence?}
DELETE/api/v2/webhooks/subscribeUnsubscribe

DM templates + notification settings

MethodEndpointPurpose
GET, POST/api/v2/dm-templatesList / create canned DM templates
GET, PUT, DELETE/api/v2/dm-templates/{id}Single-template CRUD
GET, PUT/api/v2/notification-settingsTeam-default summary frequency + channels

Lookups (Chrome extension surface)

One-shot enrichment + segment-from-description for the extension. Distinct from the campaign-centric surface above.
MethodEndpointPurpose
POST/api/v2/lookups/enrichLook up LinkedIn URL + email for an X handle
POST/api/v2/lookups/enrich-emailEmail-only lookup
GET/api/v2/lookups/enrich/historyRecent LinkedIn lookups
GET/api/v2/lookups/enrich-email/historyRecent email lookups
POST/api/v2/lookups/segments/from-descriptionCreate 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:
HeaderDescription
X-RateLimit-LimitMaximum requests per window (60)
X-RateLimit-RemainingRequests remaining in current window
Retry-AfterSeconds 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 for the full list. Standard HTTP status codes apply:
StatusDescription
200Success
201Created
400Invalid request (bad parameters or body)
401Missing, invalid, or revoked API key
403Forbidden (subscription required, budget exhausted, etc.)
404Resource not found
409Conflict (duplicate, concurrent operation)
410Endpoint removed (see migration guide)
422Unprocessable (e.g. private account)
429Rate limit exceeded or credits exhausted
502Upstream error (Stripe, etc.)
503Service temporarily unavailable