# CabalSpy (/docs/nodes/data/cabalspy)

Labeled-wallet intel — KOL, smart-money, and whale trades, PnL, cluster signals, and Solana bundle detection.



The CabalSpy node queries the [CabalSpy API](https://docs.cabalspy.xyz/get) for intel on roughly 2,000 manually labeled wallets across Solana, BNB Chain, Base, and Ethereum. Every wallet carries a category (`kol`, `smart` for smart money, or `whale`), and everything the node returns is scoped to that labeled set: trade tapes, PnL trackers, leaderboards, per-token holder tables with unrealized PnL, cluster signals ("N tracked wallets bought the same token inside a window"), and Solana-only bundle detection (a KOL plus side wallets buying in the same block or Jito bundle, with on-chain proof). CabalSpy's value over generic market data is exactly this labeling: "5 KOLs entered this token in the last hour" is a first-class query rather than something you derive.

Category availability varies by chain: Solana has all three categories, BNB and Base have KOL and smart money, Ethereum is KOL-only. Historical data goes back to February 2025.

Prerequisites [#prerequisites]

A CabalSpy API key is required. Register free at [apidashboard.cabalspy.xyz/register](https://apidashboard.cabalspy.xyz/register) (no credit card), copy the key from the dashboard, and add it as a CabalSpy credential in [Connections](/docs/credentials/adding). The free plan includes 10,000 credits per month; every request costs 10 credits, so that is 1,000 requests at up to 5 requests per second. Credits reset on the 1st of each month.

Operations [#operations]

Most operations take a **Blockchain** (`solana`, `bnb`, `base`, `eth`; defaults to Solana) and many take a **Wallet Type** (`kol`, `smart`, `whale`). Where the type is optional, omitting it means "all categories" (the leaderboard defaults to KOL).

Signals [#signals]

**`getSignals`**: live cluster detections — N tracked wallets moving on the same token inside a lookback window. `mode` picks the detection kind: `cluster` (wallets buying the same token), `entry` (wallets entering), or `exit` (wallets exiting). Tune with **Min Wallets** (default 3), **Min Buy** (per wallet, in the chain's native currency), and **Hours** (default 1). This is the flagship trigger operation: put it behind a cron trigger and route `{cabalspyResponse.data.signals}` into a condition or notification node.

**`getSignalsHistory`**: historical cluster signals for backtesting. Each row records the token, wallet count, the day it triggered, and `would_have_fired` — whether the signal would have passed the given thresholds. Rows here are flat (no nested `token` object), unlike `getSignals`.

**`getBundles`**: Solana-only bundle detection for one mint. Returns bundles where a KOL and one or more "side wallets" bought in the same block or Jito bundle, with a confidence score, proof type (`same_block`, Jito confirmation), the slot, and per-wallet positions. No bundles is a normal 200 with an empty `bundles` array, so it is safe to poll.

Token [#token]

**`getTokenHolders`**: tracked wallets holding a mint, sorted by amount held, with per-holder unrealized PnL, remaining value, entry market cap, and bag % (share of their peak bag still held). The live-position fields (`unrealized_pnl_*`, `entry_market_cap*`) are Solana-only and null on other chains.

**`getTokenTransactions`**: trades by tracked wallets on one mint, newest first, each with the trader's profile and their holdings after the trade.

**`getTokenStats`**: aggregate flow for a mint — total buy/sell volume, net flow, buying pressure (0 to 100), first/latest entry — plus a per-trader breakdown under `data.traders`.

Wallet [#wallet]

Wallet operations require a **tracked** wallet address; an untracked one returns a 404 (`not_found`).

**`getWalletTracker`**: full profile for one wallet over a period — realized PnL, win-rate distribution, active tokens with remaining bags, a realized-PnL chart, and recent trades.

**`getWalletHistory`**: lifetime stats (total PnL, win rate, best/worst trade) plus a per-token overview and a paginated trade log.

**`getWalletHoldings`**: live on-chain balances for a tracked wallet (no cost basis). Note the double nesting: tokens are under `data.active_holdings.tokens`, amounts are numeric **strings**, and the native coin uses the literal `token_address: "native"`. When CabalSpy is still warming the balance read, the API returns a `loading: true` placeholder; the node retries a few times automatically and, if it is still loading, returns the placeholder with `loading` still true so a workflow can branch on it.

**`getPnlCalendar`**: day-by-day realized PnL calendar under `data.months`, with per-month totals and win/loss streaks. Day keys use `DD-MM-YY` format.

**`getWalletConnections`**: wallets that traded the same tokens as this one over the last 30 days, sorted by shared token count, with an `overlap_score` (0 to 1).

**`lookupWallet`**: is this address tracked? Searches every chain and category (no blockchain field). An untracked address is a normal 200 with `found: false` and no profile fields — check `{cabalspyResponse.data.found}` before reading anything else.

Discovery [#discovery]

**`getLeaderboard`**: tracked wallets ranked by trading volume over a period, with per-wallet period stats and win-rate distribution. Type defaults to KOL upstream.

**`listWallets`**: the raw directory of tracked wallets for a chain and category, with names, avatars, and social links.

**`getAnalytics`**: cohort aggregates, selected by `mode`: `most_traded` tokens, `top_performers`, `win_rate` distribution, or `volume_trend`. List modes nest rows under `data.data` (double nesting); `win_rate` returns flat fields instead of an array.

Trade tape [#trade-tape]

**`getLatestTransactions`**: the global live tape of tracked-wallet trades, newest first. Optionally filter to one mint.

**`getTransactionsTimerange`**: same tape, restricted to the last N minutes (max 60; larger values are capped upstream).

**`getTransactionCount`*&#x2A; / &#x2A;*`getTransactionVolume`**: how many trades / how much volume tracked wallets did in a window. Minutes and hours are **summed** (total max 24h); leave both empty for the full available window. `volume_usd` can be null when no USD value could be derived.

Configuration [#configuration]

| Field      | Type   | Required                     | Description                                                                                                               |
| ---------- | ------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| blockchain | string | No (default `solana`)        | `solana` \| `bnb` \| `base` \| `eth`. Not used by `lookupWallet` (all chains) or `getBundles` (Solana-only)               |
| type       | string | Per op                       | `kol` \| `smart` \| `whale`. Required on list/tape/signal/analytics ops; optional filter on token ops and the leaderboard |
| address    | string | Wallet ops                   | Tracked wallet address                                                                                                    |
| mint       | string | Token/bundle ops             | Token mint; optional filter on the trade-tape ops                                                                         |
| mode       | string | `getSignals`, `getAnalytics` | Signals: `cluster` \| `entry` \| `exit`. Analytics: `most_traded` \| `top_performers` \| `win_rate` \| `volume_trend`     |
| period     | string | No                           | `6h` \| `1d` \| `7d` \| `30d` (leaderboard/tracker default `1d`, analytics `7d`)                                          |
| minWallets | number | No                           | Signals: minimum wallets in a cluster (default 3)                                                                         |
| minValue   | number | No                           | `getSignals`: minimum per-wallet buy in native currency                                                                   |
| hours      | number | No                           | `getSignals` lookback (default 1); also the hours part of count/volume windows                                            |
| minutes    | number | No                           | Timerange window (max 60) or the minutes part of count/volume windows                                                     |
| days       | number | No                           | `getSignalsHistory` lookback                                                                                              |
| limit      | number | No                           | 1 to 500. Defaults to 50 on list operations (the API otherwise returns the entire list)                                   |

Output [#output]

Every operation returns the standard envelope, with the raw CabalSpy payload untouched under `.data`:

```json
{
  "success": true,
  "operation": "getSignals",
  "blockchain": "solana",
  "data": { "signals": [ /* raw CabalSpy rows */ ] },
  "pagination": { "limit": 50, "total": 1, "has_more": false, "next_cursor": null },
  "meta": { "request_id": "req_...", "cached": false, "version": "1.0.0" }
}
```

CabalSpy places pagination inconsistently — inside `data.pagination` on the wallet list operations, next to `data` elsewhere; the node hoists the sibling form to the top of the envelope as shown. Reference fields downstream with the response name (default `cabalspyResponse`), for example `{cabalspyResponse.data.signals[0].token.mint}` after `getSignals` or `{cabalspyResponse.data.leaderboard[0].period_stats.realized_pnl_usd}` after `getLeaderboard`.

Two upstream quirks worth knowing when templating: the same concept can change field names between operations (`tx_signature` vs `signature`, `token_name` vs `token_symbol`), and fields suffixed `_sol` (like `unrealized_pnl_sol`) carry the chain's native currency even on BNB/Base/ETH.
