---
title: "List possible campaigns / suspected clusters"
method: GET
path: "/api/v1/campaigns"
---

# List possible campaigns / suspected clusters

`GET /api/v1/campaigns`

## Request

- Base URL: `https://phishunt.io`
- URL: `https://phishunt.io/api/v1/campaigns`
- Auth: none declared

Lists live correlation clusters - groups of indicators that share
infrastructure or content signals (same TLS certificate, IP, hosting,
page content, etc.), computed by the same daily correlation job as
`/api/v1/domains/{uuid}/related`. This is a **shared-infrastructure
grouping of public detections, not an attribution claim** - clusters
are labeled "possible campaign" or "suspected cluster" only, never an
actor or group.

Low-confidence ("weak") clusters are never surfaced through this
endpoint or the `/campaigns/` pages.

`total` (distinct from `count`) is the number of clusters matching
the filters BEFORE `limit`/`offset` - page through all of them by
incrementing `offset` until `offset + count >= total`.

## Query parameters

- `limit` integer
- `offset` integer
- `brand` string
- `status` 'active' | 'all'
- `min_size` integer

## Response `200`

List of possible campaigns / suspected clusters.

- CampaignsResponse
  - `count` integer: Number of results returned (after `limit`/`offset`/filters).
  - `total` integer: Total number of clusters matching `brand`/`status`/`min_size`, before `limit`/`offset` are applied - i.e. how many results paging through with `offset` would eventually return. `count` is always <= `total`.
  - `offset` integer
  - `limit` integer
  - `algorithm_version` string, nullable: Correlation algorithm identifier; scores/clusters are not comparable across versions.
  - `generated_at` string, date-time, nullable: When the correlation sidecar was last rebuilt (daily job).
  - `data_status` 'ok' | 'stale' | 'missing': Freshness of the correlation sidecar backing this response. 'ok' is the normal case; 'stale' means the last reload fell back to a cached snapshot, or the snapshot is older than the daily rebuild's expected cadence; 'missing' means no snapshot has ever loaded. Lets a client distinguish "genuinely zero live campaigns" from "the nightly rebuild didn't run" - an empty `results` array alone cannot.
  - `results` CampaignSummary[]
    - `id` integer: Deprecated - versioned per daily rebuild (the same campaign can have many different ids over its lifetime), kept only for backward compatibility. Use `key` for permalinks or anything that needs to survive a rebuild.
    - `key` string: Stable campaign identifier; use this for permalinks.
    - `size` integer: Number of distinct registrable domains (Public Suffix List, private section included) in this cluster. Sibling subdomains of one domain count once; each platform tenant counts once.
    - `host_count` integer: Number of hostnames (members.length).
    - `active_count` integer: Number of hostnames that are currently active detections.
    - `brands` string[]: Sorted, distinct targeted-brand slugs across the cluster's members.
    - `confidence` 'possible campaign' | 'suspected cluster': The two words this project ever uses for a cluster's confidence - never a numeric score, never attribution language. Derived from the underlying `campaign_confidence` float (see Campaign.confidence_score): >= 0.70 is "possible campaign", >= 0.40 is "suspected cluster"; below 0.40 ("weak") is never surfaced by this API at all.
    - `first_seen` string, date-time, nullable: Earliest `first_seen` among live members.
    - `last_activity` string, date-time, nullable: Latest `first_seen` among live members.
    - `top_evidence` object, nullable: The single evidence signal touching the most members, or null when the cluster has no internal edges yet.
      - `type` string: Stable evidence-signal identifier, e.g. `cert_fingerprint_exact`.
      - `coverage` string: Members touched by this signal, e.g. "7/9 members".
    - `url` string: Link to the /campaigns/{key}/ page for this cluster.

## Other responses

- `400`: Bad request - invalid `limit`, `offset`, or `min_size` (must be integers).
- `429`: Rate limited (more than 20 requests in 10 seconds from one IP). Retry after 10 seconds.

---

[API](https://skmtc.dev/phishunt/apis/phishunt-api.md) · [All operations](https://skmtc.dev/phishunt/apis/phishunt-api/llms.txt) · [OpenAPI document](https://skmtc.dev/phishunt/apis/phishunt-api/revisions/0e07c255f282?raw)
