AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻译准备中
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Official Rust SDK for the CabalSpy API — KOL, smart money and whale wallet tracking for Solana, Base, BNB Chain, Ethereum and Robinhood Chain
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
CabalSpy Rust SDK — KOL and smart money wallet tracking API
Official Rust client for the CabalSpy API: a realtime multichain data layer for labeled wallets, covering Solana, Base, BNB Chain, Ethereum and Robinhood Chain.
Track what Key Opinion Leaders, smart money wallets and whales are actually buying — with PnL, holder data, cluster signals and bundle detection.
What you can build with it: copy-trading bots, KOL leaderboards, memecoin alert systems, wallet analytics dashboards, bundle and sniper detection, portfolio and PnL trackers.查看完整 README ↗Official Rust client for the CabalSpy API: a realtime multichain data layer for labeled wallets, covering Solana, Base, BNB Chain, Ethereum and Robinhood Chain.
Track what Key Opinion Leaders, smart money wallets and whales are actually buying — with PnL, holder data, cluster signals, bundle detection and our curated calls.
What you can build with it: copy-trading bots, KOL leaderboards, memecoin alert systems, wallet analytics dashboards, bundle and sniper detection, portfolio and PnL trackers.
[dependencies]
cabalspy = "0.3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
use cabalspy::{CabalSpy, Chain, LeaderboardOpts, Period, SignalMode, SignalOpts, WalletType};
#[tokio::main]
async fn main() -> Result<(), cabalspy::Error> {
let client = CabalSpy::from_env()?; // reads CABALSPY_API_KEY
let wallet = client.wallets().lookup("As7HjL7dzzvbRbaD3WCun47robib2kmAKRXMvjHkSMB5").await?;
println!("{:?}", wallet.data);
let board = client.wallets().leaderboard(Chain::Solana, LeaderboardOpts {
wallet_type: Some(WalletType::Kol),
period: Some(Period::D7),
limit: Some(25),
..Default::default()
}).await?;
let signals = client.signals().list(
Chain::Solana, WalletType::Kol, SignalMode::Cluster,
SignalOpts { min_wallets: Some(5), hours: Some(1), ..Default::default() },
).await?;
for signal in &signals.data.signals {
println!("{:?} {:?}", signal.token.token_name, signal.window.wallet_count);
}
Ok(())
}
The public demo key needs no account. CabalSpy::demo() uses it against the normal API:
let client = cabalspy::CabalSpy::demo()?; // api key "demo"
let kols = client.wallets().list(Chain::Solana, WalletType::Kol, None, None).await?;
println!("{:?} requests left today", kols.demo.and_then(|d| d.remaining_today));
Or with no key at all:
curl "https://demo-api.cabalspy.xyz/v1/wallets?blockchain=solana&type=kol"
demo block (Envelope::demo) with remaining_today and upgrade linksError::DemoLimit { resets_in_seconds, .. }For realtime data, get a free test key (1,000 requests per month) at apidashboard.cabalspy.xyz, or pay per call with x402.
| Chain | Variant | Currency | Wallet types |
|---|---|---|---|
| Solana | Chain::Solana | SOL | Kol, Smart, Whale |
| BNB Chain | Chain::Bnb | BNB | Kol, Smart |
| Base | Chain::Base | ETH | Kol, Smart |
| Ethereum | Chain::Eth | ETH | Kol |
| Robinhood Chain | Chain::Rh | ETH | Kol, Smart |
Impossible combinations are rejected before a request goes out, so a mistake costs no credits:
let result = client.wallets().list(Chain::Eth, WalletType::Whale, None, None).await;
// Err(Error::InvalidRequest { message: "eth has no whale wallets; it supports: kol" })
The deepest coverage of the five. Solana is the only chain with WalletType::Whale, live market cap,
pump.fun bonding curve progress, and the bundle endpoint that detects KOL wallets buying through Jito
bundles alongside side wallets they control.
let bundles = client.bundle().get("MINT_ADDRESS").await?;
for entry in &bundles.data.bundles {
println!("{:?} confidence {:?}", entry.kol_wallet, entry.confidence);
}
Base carries both Kol and Smart wallets. Addresses are EVM format, values are denominated in ETH.
let feed = client.transactions()
.latest(Chain::Base, WalletType::Smart, FeedOpts { limit: Some(50), ..Default::default() })
.await?;
Kol and Smart wallets, denominated in BNB.
let board = client.wallets()
.leaderboard(Chain::Bnb, LeaderboardOpts { period: Some(Period::D7), ..Default::default() })
.await?;
Ethereum mainnet carries Kol wallets only; smart money signals are unavailable there.
Robinhood Chain is Robinhood's Ethereum L2 on the Arbitrum Orbit stack, with ETH as the gas token.
Both Kol and Smart wallets are tracked.
let volume = client.transactions()
.volume(Chain::Rh, WalletType::Kol, FeedOpts { hours: Some(24), ..Default::default() })
.await?;
Market cap availability differs between REST and websocket.
On REST,
market_cap,price,unrealized_pnl_*andremaining_*are populated for Solana only; elsewhere they areNone. Realized PnL, invested amounts, holdings and counters work on every chain. SeeChain::has_rest_marketcap().The gateway populates them on every chain, and unlike REST it does not zero the unrealized values once a position is fully sold.
The curated CabalSpy calls, the same ones posted in Discord, with the wallets behind each call and
how it performed over the 24 hours after it. Chains Solana, Bnb, Eth and Rh (Base has none);
tiers Gamble, Moderate, Good, Big and Whale (the last two on Solana only).
use cabalspy::{CallStatsPeriod, CallTier, CallsOpts, Chain};
let calls = client.calls().list(CallsOpts {
chains: vec![Chain::Solana],
tiers: vec![CallTier::Good, CallTier::Moderate],
..Default::default()
}).await?;
for call in &calls.data {
let peak = call.performance.as_ref().and_then(|p| p.peak_multiple);
println!("{:?} {:?} peak {:?}", call.tier_label, call.token.symbol, peak);
}
let stats = client.calls().stats(Some(CallStatsPeriod::D30), &[], &[]).await?;
println!("{:?} % reached 2x", stats.data.totals.hit_rate.get("2x"));
performance.status is "pending" for about two days, until the call has been measured. Older
calls are on the next page, via pagination.next_cursor. For calls as they happen, subscribe to
{"op":"subscribe","stream":"call"} on the websocket gateway.
match client.wallets().tracker(Chain::Solana, addr, None).await {
Ok(env) => println!("{:?}", env.data.period_stats.realized_pnl),
Err(cabalspy::Error::NotFound { .. }) => println!("not tracked"),
Err(cabalspy::Error::InsufficientCredits { .. }) => println!("top up"),
Err(cabalspy::Error::RateLimited { retry_after, .. }) => println!("{retry_after:?}"),
Err(cabalspy::Error::DemoLimit { resets_in_seconds, .. }) => println!("demo resets in {resets_in_seconds:?}s"),
Err(err) => return Err(err),
}
Every API error carries .code(), .request_id(), .parameter(), .allowed() and .rate_limit().
429 (except DemoLimit), 5xx and network failures are retried with exponential backoff and jitter; a server-sent
Retry-After wins over the SDK's own timing.
Timestamps inside data arrive as "YYYY-MM-DD HH:MM:SS" with no offset and are UTC.
Only meta.timestamp carries a Z. Treating them as local time shifts every value.
use cabalspy::parse_api_timestamp;
assert_eq!(parse_api_timestamp("2026-07-25 21:33:14"), Some(1785015194)); // Unix seconds, UTC
Dependency free on purpose. Feed the result into chrono or time if you need a full date type.
realized_pnlIt is total_sell - total_buy. A wallet that bought and has not sold reports its whole investment
as a loss, with -100 percent. Check still_holding before showing that number to a user.
Every response struct has an extra: HashMap<String, serde_json::Value> catch-all and every field
is optional, so a server-side addition never breaks deserialization and nothing is silently dropped.
For endpoints not yet modelled, or when you want the raw JSON:
let env = client.get::<serde_json::Value>("/wallets/leaderboard",
vec![("blockchain".into(), "bnb".into()), ("period".into(), "30d".into())]).await?;
println!("{} {:?}", env.status, env.rate_limit.remaining);
REST is complete. The websocket gateway is not wrapped yet; until it is, connect to
client.websocket_url() with tokio-tungstenite and send
{"op":"subscribe","stream":"tx","blockchain":"solana","type":"kol"}.
KOL stands for Key Opinion Leader: a trader or crypto personality whose token calls move markets. CabalSpy tracks their onchain wallets with a public identity attached — name, avatar, Twitter and Telegram handle — so you can verify whether they actually bought what they promoted.
A KOL is identified by influence, a smart money wallet by track record. KOL trades carry social signal, smart money trades carry statistical signal. Both are wallet types on the same endpoints, so you can query either or merge them.
On Solana, KOL wallets often buy through Jito bundles together with side wallets they control, which hides the true size of a position. The bundle endpoint groups those wallets, reports a confidence score and exposes the evidence: fee match, block index, adjacency to the KOL transaction.
Tokio. reqwest and the retry backoff both build on it. TLS is rustls, so there is no OpenSSL
dependency and cross compilation stays simple.
docs.cabalspy.xyz · free API key at apidashboard.cabalspy.xyz
MIT