API Reference

Access startup signal data programmatically. Search companies, retrieve detailed profiles, and query recent signals across all sources.

API access is available on all paid plans (Pro, Edge, and Scale), from the first payment: free trials don't include it. Manage your API key in Account settings.

Authentication

All API requests require a Bearer token in the Authorization header. Create your API key in Account settings.

curl https://cluster.vc/api/v1/companies \
  -H "Authorization: Bearer YOUR_API_KEY"

GET /api/v1/companies

Search and browse companies. Without a query, returns companies sorted by source diversity then recency.

Query parameters

ParameterDefaultDescription
q—Search query. Omit to browse. At most 100 characters. % and _ match literally; *, commas, square brackets, control characters and parentheses (except with match=exact) are ignored.
page1Page number (positive integer).
page_size50Results per page (max 100).
matchcontainsMatch mode: contains, starts_with, ends_with, exact.
scopeallSearch scope: all, name, domain.
signal_type—Companies with a signal of any of these types (the signalType values), comma-separated: launch, filing, batch, funding, domain_sale, domain_reg.
country—Country codes, comma-separated. Up to 20 items, 2-50 characters each.
region—Regions, comma-separated. Up to 20 items, 1-50 characters each.
industry—Companies in any of these industries, comma-separated. Up to 20 items, 1-50 characters each.
tld—Top-level domain, with or without the leading dot: io, .io, .co.uk. Matches the end of the domain.
recencywidest on your planLatest signal within: 24h, 7d, 30d, 90d, all. A window wider than your plan's history is narrowed to the widest one it allows; the applied window is returned in meta.recency.

A q over its length, a list item that is outside its length, blank, or has quotes, backslashes, braces or control characters, or a malformed tld returns 400 INVALID_PARAMS rather than being dropped. Unknown parameters are ignored.

Example request

curl "https://cluster.vc/api/v1/companies?q=acme&page_size=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "id": 123,
      "slug": "acme-corp",
      "displayName": "Acme Corp",
      "domain": "acme.com",
      "website": "https://acme.com",
      "country": "US",
      "city": "San Francisco",
      "description": "Developer tools for startups",
      "industry": ["SaaS", "B2B"],
      "regions": ["North America"],
      "fundingTotalUsd": 1500000,
      "firstSeenAt": "2024-01-15T00:00:00Z",
      "lastSignalAt": "2024-03-10T12:00:00Z",
      "sources": ["hn", "producthunt"],
      "signalsCount": 5,
      "freshness": "hot"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 10,
    "hasMore": true,
    "recency": "30d"
  }
}

GET /api/v1/companies/[id]

Retrieve a single company with its full signal history. Signal detail depends on your plan tier (see Signal Gating).

Path parameters

ParameterDescription
idNumeric company ID.

Merged or renamed companies

When a company is merged into another or renamed, its old ID returns 301 with error code MOVED, a Location header pointing at the current ID, and the current ID in error.successorId. Most HTTP clients follow the redirect; if you store company IDs, replace the old one with successorId.

Example request

curl https://cluster.vc/api/v1/companies/123 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": 123,
    "slug": "acme-corp",
    "displayName": "Acme Corp",
    "domain": "acme.com",
    "website": "https://acme.com",
    "country": "US",
    "city": "San Francisco",
    "description": "Developer tools for startups",
    "industry": ["SaaS", "B2B"],
    "regions": ["North America"],
    "fundingTotalUsd": 1500000,
    "firstSeenAt": "2024-01-15T00:00:00Z",
    "lastSignalAt": "2024-03-10T12:00:00Z",
    "sources": ["hn", "sec.gov"],
    "signalsCount": 5,
    "freshness": "hot",
    "tags": ["show-hn"],
    "signals": [
      {
        "id": 1,
        "source": "hn",
        "signalType": "launch",
        "title": "Show HN: Acme Corp",
        "sourceUrl": "https://news.ycombinator.com/item?id=12345",
        "fundingAmountUsd": null,
        "fundingStage": null,
        "freshness": "hot",
        "eventDate": "2024-03-10T11:40:00Z",
        "detectedAt": "2024-03-10T12:00:00Z",
        "meta": {
          "points": 145,
          "num_comments": 23
        },
        "gating": null
      },
      {
        "id": 2,
        "source": "sec.gov",
        "signalType": "filing",
        "title": "Form D: Acme Corp",
        "sourceUrl": null,
        "fundingAmountUsd": 1500000,
        "fundingStage": null,
        "freshness": "warm",
        "eventDate": "2024-02-20T00:00:00Z",
        "detectedAt": "2024-02-21T08:00:00Z",
        "meta": {
          "total_offering_amount": 1500000
        },
        "gating": {
          "reason": "plan_detail",
          "fields": ["meta", "fundingStage", "sourceUrl"],
          "availableAt": null
        }
      }
    ]
  }
}

GET /api/v1/signals

Query recent signals across all sources. Returns signals in reverse chronological order. Signals still within your plan's freshness delay are left out until it has passed (a company's detail lists them, with gating.reason: freshness_delay), so every page holds signals you can use.

Query parameters

ParameterDefaultDescription
page1Page number (positive integer).
page_size50Results per page (max 100).
source—Filter by source: hn, producthunt, sec.gov, yc, crypto_directory, tech.eu, domain_sale, domain_reg, or a group: funding (both funding sources), domain (both domain sources).
freshness—Filter by freshness: hot, warm, cold.

Example request

curl "https://cluster.vc/api/v1/signals?source=hn&freshness=hot" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "id": 1,
      "source": "hn",
      "signalType": "launch",
      "title": "Show HN: Acme Corp",
      "sourceUrl": "https://news.ycombinator.com/item?id=12345",
      "fundingAmountUsd": null,
      "fundingStage": null,
      "freshness": "warm",
      "eventDate": "2024-03-10T11:40:00Z",
      "detectedAt": "2024-03-10T12:00:00Z",
      "meta": {
        "points": 145,
        "num_comments": 23
      },
      "gating": null,
      "company": {
        "id": 123,
        "slug": "acme-corp",
        "displayName": "Acme Corp",
        "domain": "acme.com",
        "website": "https://acme.com",
        "country": "US",
        "city": "San Francisco"
      }
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 50,
    "hasMore": true
  }
}

Response Objects

Every field below is always present; a value that is missing or withheld is null. Within v1, fields are only ever added, never removed or renamed, so ignore fields you don't recognise.

Company (search result)

FieldTypeDescription
idintegerCompany ID. Use it as the key (slugs aren't unique).
slugstringURL slug of the company page.
displayNamestringCompany name.
domainstring | nullPrimary domain.
websitestring | nullWebsite URL.
countrystring | nullCountry code.
citystring | nullCity.
descriptionstring | nullShort description.
industrystring[] | nullIndustries.
regionsstring[] | nullRegions.
fundingTotalUsdnumber | nullTotal disclosed funding in USD.
firstSeenAtstring | nullWhen the company was first seen (ISO 8601).
lastSignalAtstring | nullTime of the latest signal (ISO 8601).
sourcesstring[]Sources with at least one signal for the company.
signalsCountintegerNumber of signals.
freshnessstringhot, warm, cold.

Company (detail): every search result field, plus

FieldTypeDescription
tagsstring[] | nullTags such as show-hn or form-d.
signalsSignal[]The company's most recent signals, newest first (up to 100; signalsCount has the total).

Signal

FieldTypeDescription
idintegerSignal ID.
sourcestringOne of: hn, producthunt, sec.gov, yc, crypto_directory, tech.eu, domain_sale, domain_reg.
signalTypestringOne of: launch, filing, batch, funding, domain_sale, domain_reg.
titlestringSignal title.
sourceUrlstring | nullLink to the source. Null for domain signals.
fundingAmountUsdnumber | nullFunding amount in USD.
fundingStagestring | nullFunding round, e.g. seed.
freshnessstringhot, warm, cold.
eventDatestring | nullWhen the event happened (launch, filing, round, sale), ISO 8601.
detectedAtstring | nullWhen the signal was detected, ISO 8601.
metaobject | nullSource-specific details; keys per source below. Null when none or withheld.
gatingobject | nullNull, or why detail is withheld (see below).

Signal in /api/v1/signals: every Signal field, plus

FieldTypeDescription
companyobjectThe signal's company: id, slug, displayName, domain, website, country, city (as in a search result).

gating

FieldTypeDescription
reasonstringhistory_window (older than your plan's history window or undated (permanent)), freshness_delay (too recent for your plan, until availableAt), plan_detail (a detail level your plan doesn't include).
fieldsstring[]Fields withheld: null, or for meta, trimmed to fewer keys; meta.<key> for a single meta key (e.g. meta.venue).
availableAtstring | nullfreshness_delay only: when full detail becomes available (ISO 8601).

meta keys by source

meta holds only these keys, and only those with a value. A domain sale's venue is a known marketplace name or "Undisclosed", and a sale price under $2,000 comes as sale_price_under: 2000 instead of sale_price. investors is a list of names. For dates, prefer the signal's eventDate.

Sourcemeta keys
hnpoints, num_comments, hn_url, domain, description
producthuntdomain, description
sec.govfiling_date, entity_type, jurisdiction, industry_group, total_offering_amount, total_amount_sold, minimum_investment, securities_types, exemption_type, date_of_first_sale, num_investors, revenue_range, has_non_accredited_investors
ycbatch, status, industries, domain, description
crypto_directorydomain, description
tech.eudate_published, funding, stage, investors, website
domain_saledomain_sold, sale_price, venue, date, sale_price_under
domain_regdomain_name, registration_date, is_fresh

Rate Limits

API rate limits are enforced per account (shared by all your API keys) on a daily sliding window. Limits vary by plan. A query (one set of filters, paged with page and page_size) reaches up to the plan's results per query; a page past them returns PAGE_LIMIT_EXCEEDED.

Each plan also has a daily data budget per account, shared with the web app and CSV export. Rows per day counts every list row delivered: /api/v1/companies and /api/v1/signals rows, web search results and CSV rows. Company details count each /api/v1/companies/{id} response and each company page on the web. A page is served in full while rows are left, so the last page of the day may pass the budget by up to one page. Once a budget is spent, requests return 429 ROW_BUDGET_EXCEEDED or DETAIL_BUDGET_EXCEEDED with Retry-After: rows and daily details reset at 00:00 UTC, the weekly details on Monday at 00:00 UTC.

PlanDaily RequestsCompanies per QuerySignals per QueryRows per DayCompany Details per Day / Week
Pro20505050030 / 100
Edge2005005002,000100 / 400
Scale1,0002,0002,0004,000300 / 1,000

Rate limit headers

Responses to an authenticated request include these headers, errors included. Repeated 429s after the quota runs out, and a 503 when the quota couldn't be checked, may come without them. List responses also carry the RowBudget-* set, company detail responses the DetailBudget-* set (the company details budget that runs out first):

HeaderDescription
RateLimit-LimitMaximum requests allowed in the current window.
RateLimit-RemainingRemaining requests in the current window.
RateLimit-ResetSeconds until the rate limit window resets.
RowBudget-LimitYour plan's rows per day.
RowBudget-RemainingRows left today, after this response.
RowBudget-ResetSeconds until the row budget resets (00:00 UTC).
DetailBudget-LimitYour plan's company details per day, or per week once the week has fewer left.
DetailBudget-RemainingCompany details left after this response, today or this week, whichever is fewer.
DetailBudget-ResetSeconds until that budget resets (00:00 UTC; the week on Monday).

Signal Gating

Signal detail varies by plan. A withheld field is null, and the signal's gating object says why and which fields (see Response Objects). In a company's detail, signals newer than your plan's freshness delay come with freshness_delay and the time detail becomes available, and signals older than your plan's history window (or without a date) come with history_window; /api/v1/signals leaves both out. Detail levels per signal type:

Detail levels by plan

Signal TypeProEdgeScale
Launchesfullfullfull
SEC Filingsamount_onlyfullfull
Fundingamount_onlyfullfull
YC Batchesamount_onlyfullfull
Domaintype_onlyfullfull
Domain Displayfullfullfull

Freshness delay and history window

PlanFreshness DelayHistory Window
Pro2-day delay30 days
Edge6-hour delay90 days
Scale1-hour delayFull access

Errors

All errors return a JSON body with an error object containing a machine-readable code and a human-readable message.

HTTP StatusCodeDescription
401INVALID_API_KEYMissing or invalid API key.
403NO_ACTIVE_PLANAPI key valid but no active paid plan (or still on a free trial).
400INVALID_PARAMSInvalid query parameters.
404NOT_FOUNDRequested resource does not exist.
301MOVEDCompany merged or renamed: successorId holds its current ID (see Company Detail).
403PAGE_LIMIT_EXCEEDEDThe page starts past your plan's results per query.
429RATE_LIMITEDDaily request quota exceeded.
429ROW_BUDGET_EXCEEDEDToday's rows are spent (shared with the web app and CSV export). Retry after Retry-After seconds.
429DETAIL_BUDGET_EXCEEDEDToday's or this week's company details are spent (shared with the web app). Retry after Retry-After seconds.
503SERVICE_UNAVAILABLETemporary outage. Retry later (after Retry-After seconds when present).

Example error response

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Daily API request limit exceeded. Resets in 14400 seconds."
  }
}

Ready to integrate?

Every paid plan includes API access. API access starts with your first payment, when the free trial ends.

Get notified