Solana & Robinhood Chain intelligence · API reference
REST, webhooks, and WebSocket streams over the same KOL, deployer, alpha-wallet, and DEX firehose data. Authenticated with a single msk_ bearer key or — for agents — paid per call via x402. Free tier ships the full response shape on 40+ endpoints; 189 operations in total.
Weighing us against another provider? Compare us vs Birdeye, Helius, Moralis →
Base URL madeonsol.com/api/v1·Auth header Authorization: Bearer msk_…·JSON in, JSON out
What ships in v1
The surface area, the venues, the SDKs, the push latency.
189 documented operations · 30 WS channels
Pump · Raydium · Jupiter · …
7 Solana + 7 Robinhood Chain
Pro/Ultra · latency on /benchmarks
Try it live
Try the live API. No signup.
Public demo key. 20 calls/hour per IP, 3 read-only endpoints, live feeds 5-min delayed (like the free tier — paid keys are real-time). Want 40+ endpoints and 200 calls/day? Free key, one click →
Demo API key
msk_demo_try_the_solana_api_2026Paste into your terminal
curl -H "Authorization: Bearer msk_demo_try_the_solana_api_2026" "https://madeonsol.com/api/v1/kol/feed?limit=3"
Quick start
Free key, one curl call, optional SDK.
Authorization header, REST, JSON in / JSON out.
curl -H "Authorization: Bearer msk_…" \ "https://madeonsol.com/api/v1/kol/feed?limit=5"
TypeScript or Rust — same endpoints, same msk_ auth.
npm install madeonsol # or cargo add madeonsol
Starter kits
Open-source (MIT) example projects — free-key runnable, one dependency or fewer. Fork them into your own thing.
kol-copytrade-bot-starterOpens a virtual position when a tracked KOL buys, closes when they sell. Free tier polls /kol/feed (5-min delayed); PRO streams kol:trades over WebSocket (<3s).
rug-check-telegram-botPaste a mint → transparent 0–100 risk score with named factors (PRO) or early-buyer quality (free). Zero dependencies.
deployer-alert-discord-botDiscord embeds when a tracked pump.fun / bonk deployer launches or bonds a token. Free-key-safe polling, one webhook URL.
MIT-licensed. Grab a free key and each runs in five minutes — the free tier is genuinely usable (live feeds run 5 min behind), PRO unlocks real-time.
Authentication
API key for humans and apps. x402 for autonomous agents paying per call.
Get a free key at /developer — instant, no payment to start. Free tier: 200 calls/day, 40+ endpoints.
curl -H "Authorization: Bearer msk_your_api_key" \ "https://madeonsol.com/api/v1/kol/feed?limit=5"
Windows PowerShell: use curl.exe (bare curl is an Invoke-WebRequest alias).
No key. The agent's Solana wallet IS the identity. Pay per call for KOL, deployer, and wallet data — plus the trade-decision calls an agent makes at the moment of a buy: token risk score, buyer quality, live token snapshot, and the machine-readable Signal Scorecard. See /x402 for the full protocol.
# Receive 402 → sign USDC → retry with proof → data curl https://madeonsol.com/api/x402/kol/feed?limit=5
$0.005–$0.02 per call · settles in ~1s · failed payments don't charge.
Transports
Same data — four delivery shapes.
Dashboards, batch jobs, anything human-paced. The default.
Autonomous AI agents with a Solana wallet but no account.
Long-running bots, alerts, copy-trade execution.
Low-latency decisions, all-DEX coverage, agent loops.
Tier comparison
Every tier ships the full response shape. Paid gates are per-user infra (webhooks, WS, rules) and ULTRA-only data depth.
| Feature | BasicFree | Pro€43/mo | Ultra€131/mo | Business€400/mo |
|---|---|---|---|---|
| Daily requests | 200 | 10,000 | 100,000 | 500,000 |
| Burst rate limit | 60/min | 300/min | 600/min | 3,000/min |
| Wallet addresses | Full | Full | Full | Full |
| TX signatures | Included | Included | Included | Included |
| KOL feed · leaderboard · profile · PnL · timing | Full | Full | Full | Full |
| KOL coordination detection | — | Full | Full | Full |
| KOL hot / trending / pairs | 20/page (paginate) | 20/page (paginate) | 50/page + wallets | 50/page + wallets |
| KOL compare | 4 wallets | 4 wallets | 5 wallets | 5 wallets |
| Scout leaderboard · coordination history | — | Full | Full | Full |
| Deployer leaderboard · profile · tokens · trajectory | Full | Full | Full | Full |
| Deployer alerts | 1-50/page, cursor + offset | 1-50/page, cursor + offset | 1-50/page, cursor + offset | 1-50/page, cursor + offset |
| Deployer multiplier stats | Full | Full | Full | Full |
| Alpha leaderboard | 100/page | 100/page | 500 window + bot signals | 500 window + bot signals |
| Alpha profile · linked wallets | — | — | Full | Full |
| Token cap table | — | Top 10 | Top 20 | Top 20 |
| Token holders census | — | Top 10 | Top 50 | Top 100 |
| Token buyer quality | Full | Full | Full | Full |
| Token risk/safety score + per-factor breakdown | — | ✓ | ✓ | ✓ |
| Wallet tracker watchlist | — | 50 wallets | 100 wallets | 500 wallets |
| Wallet tracker history | — | Full | Full | Full |
| Copy-trade rules | — | 3 × 5 wallets | 20 × 50 wallets | 100 × 250 wallets |
| KOL coordination push alerts | — | 5 rules | 20 rules | 100 rules |
| First-touch subscriptions | — | — | 10 | 50 |
| Price alerts (MC dip/recovery) | — | 5 alerts | 25 alerts | 125 alerts |
| Webhooks | — | 3 | 10 | 30 |
| WebSocket streaming | — | 2 conn | 3 conn | 10 conn |
| token:prices mints per connection | — | 25 | 100 | 250 |
| Wallet tracker real-time (WS) | — | — | Included | Included |
| DEX firehose (all-DEX WS) | — | — | 2 conn | 5 conn |
Endpoints
All 189 endpoints, grouped by capability. Each card has parameters, defaults, and a real response example.
Catalog
Click a row to jump to the full reference card below.
Latency
Where the seconds come from — and where they don't.
Constant-K Nexus · two regions
Two-region dual-stream gRPC fans out every confirmed swap (plus shreds for new deploys). We pick the first-to-arrive per slot. Detection latency is measured live on /benchmarks.
Decode · price · MC · tier · denorm
Decode the DEX program, attach KOL/deployer/alpha tags, stamp market cap, write to Postgres + pg_notify. Measured in production 2026-09-19: KOL trade decode to NOTIFY 3 ms p50, new deploy 10 ms p50.
WebSocket · webhook · REST cache
Subscribers get the event via the ws-streaming service. Webhook deliveries kick off in parallel with HMAC signing. This hop is not instrumented yet, so we publish no number for it.
delay, as_of and an X-Data-Delay header). Leaderboards and token search stay real-time on every tier; wallet PnL needs Pro.KOL Tracker
Real-time wallet activity from 1,100+ tracked Solana KOLs across 14 DEX programs spanning 8 venues.
The tracked-wallet roster every /kol/* route is keyed on — 1,100+ Solana KOLs with name, X profile, strategy tag, and follower counts. Roster only — performance numbers live in /kol/leaderboard and /kol/{wallet}. Search by name (`q`) or filter to one `strategy`. Available on every tier, including free — it's the key space, not data. The Robinhood Chain roster is /rhc/kol/leaderboard.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 200 | Max results (1-500) |
| offset | integer | 0 | Pagination offset (0-5000) |
| active | string | true | "true", "false", or "all" |
| q | string | — | Search by name (substring, case-insensitive, 1-40 chars) |
| strategy | string | — | Filter by exact strategy_tag (e.g. scalper) |
{
"wallets": [
{
"wallet_address": "CyaE1VxvBrahnPWkqm5VsdCvyS2QmNht2UFrKJHga54o",
"name": "Cented",
"twitter_url": "https://x.com/Cented7",
"avatar_url": null,
"strategy_tag": "scalper",
"twitter_followers": 41145,
"follow_count": 0,
"is_active": true,
"tracked_since": "2026-03-19T12:44:55.646647+00:00"
}
],
"count": 1,
"total": 1142,
"limit": 200,
"offset": 0,
"has_more": true,
"filters": {
"active": "true",
"q": null,
"strategy": null
}
}Real-time trade feed from 1,100+ tracked Solana KOL wallets. Every buy and sell detected within seconds across Pump.fun, Raydium, Jupiter, Orca, and more.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of trades (1-100) |
| before | ISO datetime | — | Cursor — return trades strictly older than this timestamp. Pass `next_before` from previous response. |
| since | ISO datetime | — | Poll cursor — only trades strictly newer than this timestamp. Pass `next_since` from the previous response; if count === limit page down with `before` until you pass your previous since. |
| include | string | — | "token" embeds the /token/{mint} snapshot on each row as `token` (at most 20 distinct mints per page; the rest carry token:null and are listed in `include_truncated`) |
| action | string | — | Filter: "buy" or "sell" |
| kol | string | — | Filter by KOL wallet address |
| min_sol | number | — | Minimum SOL size per trade |
| min_mc_usd | number | — | Lower bound on market_cap_usd_at_trade (trades with unknown MC drop out when set) |
| max_mc_usd | number | — | Upper bound on market_cap_usd_at_trade (must be ≥ min_mc_usd) |
| token_age_max_min | integer | — | Max token age in minutes at time of trade |
| exclude_sells | boolean | false | Drop sell-side trades |
| min_kol_winrate | number | — | Minimum 7d winrate of the KOL (0-100) |
| strategy | string | — | "scalper", "day_trader", "swing_trader", "hodler", or "mixed" |
{
"trades": [
{
"wallet_address": "CyaE1Vxv...ga54o",
"kol_name": "Cented",
"kol_twitter": "https://x.com/Cented7",
"kol_strategy_tag": "scalper",
"kol_auto_strategy_tag": "scalper",
"kol_winrate_7d": 61.5,
"kol_winrate_30d": 57.2,
"kol_early_entry_pct_30d": 42,
"kol_is_heating_up": true,
"kol_percentile_pnl_7d": 88,
"kol_percentile_winrate_7d": 74,
"token_mint": "tca3tbFf...pump",
"token_name": "Hands Off Coin",
"token_symbol": "Hands Off",
"token_image_url": "https://.../hands-off.png",
"launchpad": "pumpfun",
"token_age_minutes": 14,
"action": "buy",
"sol_amount": 5.27,
"token_amount": 19389235.04,
"market_cap_usd_at_trade": 28430,
"price_usd_at_trade": 0.0000284,
"tx_signature": "4fu8vvsF...v1sb",
"deployer": {
"wallet": "7dEx...4pQ8",
"tier": "good",
"bonding_rate": 0.41
},
"deployer_tier": "good",
"traded_at": "2026-03-25T04:18:43+00:00"
}
],
"count": 1,
"data_age_seconds": 3,
"next_before": "2026-03-25T04:18:43+00:00",
"next_since": "2026-03-25T04:18:43+00:00",
"since": null,
"stream": {
"channel": "kol:trades",
"url": "wss://madeonsol.com/ws/v1/stream",
"token_endpoint": "POST /api/v1/stream/token",
"subscribe": {
"type": "subscribe",
"channels": [
"kol:trades"
]
}
}
}Ranked leaderboard of KOL wallets by realized PnL in SOL — plus alternative sort axes (winrate, ROI, profit factor, early-entry share, consistency). Ranking windows: today, 7d, 30d, 90d, 180d.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 7d | Time period: "today", "7d", "30d", "90d", or "180d" |
| limit | integer | 50 | Number of KOLs (1-100) |
| offset | integer | 0 | Pagination offset (0-10000) |
| sort | string | pnl | "pnl", "winrate", "volume", "avg_roi", "profit_factor", "early_entry_pct", or "consistency" |
| strategy | string | — | Filter by strategy tag |
| min_winrate | number | — | Minimum winrate cutoff (0-100) |
{
"leaderboard": [
{
"name": "Cented",
"wallet": "CyaE1Vxv...ga54o",
"pnl": 1672.8,
"buy_count": 2960,
"sell_count": 1525,
"volume": 8748.72,
"median_hold_minutes_30d": 47,
"percentile_early_entry_30d": 82
},
{
"name": "Marcell",
"wallet": "FixmSpsB...7X",
"pnl": 630.97,
"buy_count": 154,
"sell_count": 51,
"volume": 3457.54,
"median_hold_minutes_30d": 120,
"percentile_early_entry_30d": 61
}
],
"period": "7d"
}Detailed profile for a specific KOL wallet. Includes PnL stats, trade history, buy/sell counts, total volume, full mv_kol_scores peer-rank block, and optional per-token PnL breakdown via ?include=pnl_by_token,recent_winners,recent_losers.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Solana wallet address of the KOL |
| include | string | — | Comma-separated: "pnl_by_token", "recent_winners", "recent_losers" |
{
"kol": {
"name": "Cented",
"wallet": "CyaE1Vxv...ga54o",
"twitter_url": "https://x.com/Cented7"
},
"stats": {
"pnl": 25.71,
"buy_count": 78,
"sell_count": 22,
"volume": 167.89,
"win_rate": 62.5
},
"recent_trades": [
"..."
],
"pnl_by_token": [
{
"token_mint": "tca3tbFf...pump",
"token_symbol": "HANDS",
"pnl": 12.4,
"result": "win"
}
]
}Detect tokens being traded by multiple KOLs within a time window. Surfaces coordination signals — when 3+ KOLs converge on the same token, it's often significant. PRO/ULTRA only (BASIC keys receive 403).
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 24h | Time window: "1h", "6h", "24h", or "7d" |
| min_kols | integer | 3 | Minimum KOLs trading the same token (2-50) |
| limit | integer | 20 | Max results (1-50) |
| min_avg_winrate | number | — | Require cluster avg winrate_7d >= N (0-100) |
| unique_strategies | boolean | — | true — require the cluster to span 2+ distinct KOL strategies |
{
"coordination": [
{
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"token_name": "Pepe Reborn",
"kol_count": 5,
"total_buys": 12,
"total_sells": 3,
"net_sol_flow": 47.82,
"signal": "accumulating",
"kols": [
{
"name": "Cented",
"wallet": "CyaE...ga54o"
},
{
"name": "Marcell",
"wallet": "Fixm...7X"
}
]
}
],
"period": "24h",
"min_kols": 3
}Get all KOL trading activity for a specific token. Returns an aggregate summary (total bought/sold, net flow, accumulating/distributing signal) plus a per-KOL breakdown.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Solana token mint address |
{
"token_mint": "9aK2rFy1...pump",
"summary": {
"kol_count": 4,
"total_bought_sol": 82.5,
"total_sold_sol": 34.7,
"net_flow_sol": 47.8,
"signal": "accumulating"
},
"kols": [
{
"name": "Cented",
"wallet": "CyaE1Vxv...ga54o",
"buy_count": 3,
"sell_count": 1,
"total_bought": 25,
"total_sold": 10.2,
"net_sol": -14.8,
"position": "net_buyer",
"first_trade": "2026-03-24T10:00:00Z",
"last_trade": "2026-03-25T04:18:00Z"
}
]
}Discover which KOLs frequently co-trade the same tokens. Returns pairs ranked by shared token count with agreement rate (both bought within 2 hours). ULTRA adds the list of shared token mints per pair.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 7d | Time period: "7d" or "30d" |
| min_shared | integer | 3 | Minimum shared tokens to qualify (1-20) |
| limit | integer | 20 | Number of pairs (1-50) |
{
"pairs": [
{
"kol_a": {
"name": "Murad",
"wallet": "7QZG...LPZT"
},
"kol_b": {
"name": "traderpow",
"wallet": "2Dh5...w6"
},
"shared_token_count": 7,
"agreement_rate": 85.71,
"shared_tokens": [
"EPjF...Dt1v",
"7vfC...rXs2"
]
}
],
"period": "7d",
"min_shared": 3
}Entry/exit behavioral analytics for a specific KOL. Hold duration, exit speed distribution (% closed within 1h/6h/24h), average trade sizes, and hour-of-day activity pattern.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string | — | KOL wallet address (base58, path param) |
| period | string | 30d | Time period: "7d" or "30d" |
{
"kol": {
"name": "Murad",
"wallet": "7QZG...LPZT"
},
"timing": {
"tokens_traded": 42,
"positions_closed": 28,
"avg_hold_minutes": 312.5,
"median_hold_minutes": 180,
"pct_closed_1h": 21.4,
"pct_closed_6h": 57.1,
"pct_closed_24h": 85.7,
"avg_buy_size_sol": 2.45,
"avg_sell_size_sol": 3.12,
"most_active_hours": [
14,
15,
20
]
},
"period": "30d"
}Tokens with accelerating KOL buy interest — early signal before full coordination triggers. Compares recent KOL buy rate (last 25% of period) to baseline. Acceleration > 1.0 means buying is speeding up. ULTRA adds full KOL wallet addresses per token.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 6h | Time period: "1h" or "6h" |
| min_kols | integer | 1 | Minimum KOL buyers (1-20) |
| limit | integer | 20 | Number of tokens (1-50) |
| min_avg_winrate | number | — | Require avg winrate_7d of buyers >= N (0-100) |
| unique_strategies | integer | — | Require >= N distinct strategies among buyers |
{
"hot_tokens": [
{
"token_mint": "EPjF...Dt1v",
"token_symbol": "PEPE",
"token_name": "Pepe on Sol",
"kols_total": 5,
"kols_recent": 3,
"acceleration": 2.4,
"total_buy_sol": 45.2,
"total_sell_sol": 12.1,
"net_flow": 33.1,
"first_kol_buy_age_minutes": 42,
"kols": [
{
"name": "Murad"
},
{
"name": "traderpow"
}
]
}
],
"period": "6h",
"min_kols": 1
}Tokens ranked by raw KOL buy volume — a pure capital-flow signal showing where KOL money is going right now. Supports 7 time windows from 5 minutes to 12 hours. ULTRA adds full KOL wallet addresses per token.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 1h | Time window: "5m", "15m", "30m", "1h", "2h", "4h", or "12h" |
| min_kols | integer | 1 | Minimum KOL buyers (1-20) |
| limit | integer | 20 | Number of tokens (1-50) |
{
"trending": [
{
"token_mint": "EPjF...Dt1v",
"token_symbol": "Wcoin",
"token_name": "chasing that w",
"buy_volume_sol": 133.76,
"sell_volume_sol": 111.79,
"net_flow_sol": 21.98,
"buy_count": 24,
"sell_count": 18,
"kol_count": 10,
"latest_buy_age_minutes": 2,
"kols": [
{
"name": "OGAntD"
},
{
"name": "Silver"
},
{
"name": "West"
}
]
}
],
"period": "1h",
"min_kols": 1
}Deep per-wallet PnL breakdown. Returns summary stats (realized PnL, win rate, profit factor, max drawdown, avg ROI, hold times), daily PnL equity curve, and per-token closed positions. ULTRA adds open positions (tokens bought but not yet sold).
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | KOL wallet address (base58) |
| period | string | 30d | Time period: "7d", "30d", "90d", or "180d" |
{
"kol": {
"name": "OGAntD",
"wallet": "215n...gQjP",
"strategy_tag": "day_trader"
},
"summary": {
"realized_pnl_sol": -27.39,
"win_count": 92,
"loss_count": 52,
"win_rate": 63.89,
"profit_factor": 0.913,
"max_drawdown_sol": 129.41,
"avg_roi_pct": 4.65,
"avg_hold_minutes": 1149.9
},
"pnl_curve": [
{
"date": "2026-03-19",
"day_pnl": 4.17,
"cumulative_pnl": 4.17,
"trades": 9
},
{
"date": "2026-03-20",
"day_pnl": 24.95,
"cumulative_pnl": 29.12,
"trades": 15
}
],
"closed_positions": [
{
"token_symbol": "BONK",
"pnl_sol": 43.47,
"roi_pct": 180.5,
"hold_minutes": 320,
"result": "win"
}
],
"period": "30d"
}Ranked order of KOL first-buyers for a specific token — who entered first and how quickly others followed. Each entry includes seconds_after_first relative to the first KOL entry and percentile ranks (percentile_pnl_7d, percentile_winrate_7d).
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Solana token mint address |
| limit | integer | 50 | Max ranked entries (1-100) |
{
"token_mint": "9aK2rFy1...pump",
"entries": [
{
"rank": 1,
"wallet_address": "CyaE1Vxv...ga54o",
"kol_name": "Cented",
"sol_amount": 8.42,
"token_amount": 24218991.1,
"traded_at": "2026-03-25T04:18:43+00:00",
"seconds_after_first": 0,
"tx_signature": "4fu8vvsF...v1sb",
"strategy_tag": "sniper",
"winrate_7d": 68.2,
"early_entry_pct_30d": 72.1,
"percentile_pnl_7d": 94.3
}
],
"count": 1
}Side-by-side comparison of 2-5 KOL wallets — strategy tag, 7d/30d winrates, ROI, profit factor, early-entry %, PnL percentiles, cold/heating-up flags, and 30d overlap tokens (bought by 2+ of the wallets). Max 4 wallets; ULTRA allows 5.
| Name | Type | Default | Description |
|---|---|---|---|
| wallets | string | required | Comma-separated list of 2-5 wallet addresses |
{
"profiles": [
{
"wallet_address": "CyaE1Vxv...ga54o",
"name": "Cented",
"strategy_tag": "swinger",
"winrate_7d": 61.4,
"winrate_30d": 58.7,
"roi_30d": 18.4,
"profit_factor_30d": 1.82,
"early_entry_pct_30d": 44.1,
"pnl_30d": 412.5,
"percentile_pnl_30d": 96.7,
"is_heating_up": true
}
],
"overlap": [
{
"token_mint": "EPjF...Dt1v",
"token_symbol": "PEPE",
"wallets": [
"CyaE1Vxv...ga54o",
"FixmSpsB...7X"
],
"first_buy_at": "2026-04-14T08:22:00Z",
"last_buy_at": "2026-04-14T09:12:00Z"
}
],
"count": 2
}Unified live KOL alert stream — consensus clusters (3+ KOLs buying same token), fresh-token KOL buys (token <30 min old, KOL winrate ≥50), and heating-up wallets (percentile jump). Sorted by detected_at DESC then severity.
| Name | Type | Default | Description |
|---|---|---|---|
| window | string | 6h | Lookback window: "1h", "6h", or "24h" |
| types | string | — | Comma-separated: "consensus_cluster", "fresh_token_kol_buy", "heating_up" |
| limit | integer | 30 | Max alerts (1-100) |
{
"alerts": [
{
"type": "consensus_cluster",
"severity": "high",
"detected_at": "2026-04-18T08:42:11Z",
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"details": {
"kol_count": 5,
"buy_span_seconds": 142,
"net_sol_flow": 47.8
}
},
{
"type": "fresh_token_kol_buy",
"severity": "medium",
"detected_at": "2026-04-18T08:39:44Z",
"token_mint": "4HxQKdTT...pump",
"wallet_address": "CyaE1Vxv...ga54o",
"kol_name": "Cented",
"details": {
"token_age_minutes": 12,
"kol_winrate_7d": 64.1
}
}
],
"count": 2,
"window": "15m"
}Every "first KOL buy on a token mint" event — the moment a tracked KOL is the first of the cohort to touch a token. Filterable by scout tier (S/A/B/C from mv_kol_scout_score), KOL winrate, token age, mint suffix. 38d backtest: top scouts attract ≥3 follow-on KOLs within 4h ~50% of the time vs ~14% baseline. Median lead time before second KOL is 12s — for trading, use the kol:first_touches WS channel instead of polling.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of events (1-100, BASIC capped at 20) |
| since | ISO datetime | — | Polling cursor — events strictly newer than this |
| before | ISO datetime | — | Pagination cursor — events strictly older than this |
| kol | string | — | Single KOL wallet (32-44 base58 chars) |
| min_kol_winrate_7d | number | — | 0-100 |
| min_scout_tier | string | — | "S", "A", "B", or "C". Requires n_first_touches_30d ≥ 30. |
| min_n_touches | integer | 30 | Lower the minimum sample size for scout scoring |
| strategy | string | — | "scalper", "day_trader", "swing_trader", "hodler", or "mixed" |
| token_age_max_min | integer | — | Only events on tokens younger than N minutes |
| min_first_buy_sol | number | — | Minimum size of the first KOL buy in SOL |
| mint_suffix | string | — | Suffix-filter the token mint (e.g. "pump") |
| preset | string | — | "scout" = min_scout_tier=B + min_n_touches=30 + token_age_max_min=60. "fresh_launch" = token_age_max_min=15. |
| include | string | — | "followers_4h" attaches retrospective follower count for events ≥4h old |
{
"events": [
{
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"token_name": "Pepe Reborn",
"token_image_url": "https://...",
"first_buy_at": "2026-04-26T07:18:42Z",
"sol_amount": 1.234,
"token_amount": 4567890.12,
"tx_signature": "5mmHpg35...",
"token_age_minutes": 7,
"first_kol": {
"name": "Jijo",
"twitter_url": "https://x.com/jijo",
"winrate_7d": 67.5,
"strategy": "scalper",
"scout_tier": "S",
"scout_score": 51.1,
"n_first_touches_30d": 331
}
}
],
"count": 1,
"next_before": "2026-04-26T07:18:42Z",
"data_age_seconds": 4
}Ranked leaderboard of KOLs by scout score — how often a KOL's first touch on a token attracts a swarm of follow-on KOLs. S-tier scouts attract 3+ follow-on KOLs within 4h ~50% of the time. Filterable by scout tier, sortable by score, first-touch count, or swarm rate. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of results (1-100) |
| scout_tier | string | — | Filter by tier: "S", "A", "B", or "C" |
| sort | string | swarm_3plus_pct | "scout_score", "n_first_touches_30d", "swarm_3plus_pct", or "swarm_5plus_pct" |
{
"scouts": [
{
"wallet": "9xKv...3bPq",
"name": "Jijo",
"avatar_url": null,
"twitter_url": "https://x.com/jijo",
"scout_tier": "S",
"first_touches_30d": 331,
"avg_followers_4h": 3.4,
"swarm_3plus_pct": 52.1,
"swarm_5plus_pct": 27.8,
"computed_at": "2026-08-18T06:00:00Z"
}
],
"count": 1
}Past coordination clusters with outcomes — did the token pump after KOLs converged? Returns clusters with entry MC, peak MC after detection, and outcome classification (hit, miss, pending). Useful for backtesting coordination signals and measuring signal quality over time.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of clusters (1-100) |
| since | ISO datetime | — | Only clusters detected after this timestamp |
| min_score | integer | — | Minimum coordination_score (0-100) |
{
"clusters": [
{
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"kol_count": 5,
"coordination_score": 82,
"detected_at": "2026-05-20T08:42:11Z",
"entry_mc_usd": 28430,
"peak_mc_usd_after": 423000,
"mc_multiplier": 14.9,
"outcome": "hit",
"time_to_peak_minutes": 47
}
],
"count": 1
}ULTRA only. Create a webhook subscription that delivers kol:first_touch events matching your filters as HMAC-SHA256-signed POSTs. Up to 10 active subscriptions per ULTRA user. Returns webhook_secret ONCE — store it for signature verification.
| Name | Type | Default | Description |
|---|---|---|---|
| name | string | — | Optional label |
| filters | object | {} | { kol?, mint_suffix?, min_first_buy_sol?, min_scout_tier?, min_n_touches? } |
| delivery_mode | string | websocket | "websocket", "webhook", or "both" |
| webhook_url | string | — | HTTPS URL — required when delivery_mode includes "webhook" |
{
"subscription": {
"id": "a3f17b22-...",
"name": "S-tier scouts on pump tokens",
"filters": {
"min_scout_tier": "S",
"mint_suffix": "pump"
},
"delivery_mode": "webhook",
"webhook_url": "https://my.bot/hooks/scout",
"is_active": true,
"created_at": "2026-04-26T08:00:00Z"
},
"webhook_secret": "<32-byte hex — shown once>",
"note": "Save the webhook_secret — it will not be shown again. ..."
}ULTRA only. Every first-touch subscription you own — filters, delivery mode, MC band, is_active. The webhook secret is never returned here (only once, at creation). ULTRA: 10 subscriptions, BUSINESS: 50.
{
"subscriptions": [
{
"id": "a3f17b22-...",
"name": "S-tier scouts on pump tokens",
"filters": {
"min_scout_tier": "S",
"mint_suffix": "pump"
},
"delivery_mode": "webhook",
"webhook_url": "https://my.bot/hooks/scout",
"min_mc_usd": null,
"max_mc_usd": null,
"is_active": true,
"created_at": "2026-04-26T08:00:00Z",
"updated_at": "2026-04-26T08:00:00Z"
}
]
}ULTRA only. Fetch one subscription by UUID. Scoped to your account — a subscription you don't own returns 404.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Subscription ID |
{
"subscription": {
"id": "a3f17b22-...",
"name": "S-tier scouts on pump tokens",
"filters": {
"min_scout_tier": "S"
},
"delivery_mode": "webhook",
"is_active": true
}
}ULTRA only. Partial update — send only the fields you want changed (name, filters, delivery_mode, webhook_url, is_active, min_mc_usd/max_mc_usd; null clears an MC bound). No new webhook secret is issued; to rotate one, delete and recreate.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Subscription ID |
| name | string | null | — | Label (≤64 chars) |
| filters | object | — | { kol?, mint_suffix?, min_first_buy_sol?, min_scout_tier?, min_n_touches? } |
| delivery_mode | string | — | "websocket", "webhook", or "both" |
| webhook_url | string | null | — | HTTPS URL |
| is_active | boolean | — | Pause / resume without deleting |
| min_mc_usd | number | null | — | MC floor in USD (null clears) |
| max_mc_usd | number | null | — | MC ceiling in USD (null clears) |
{
"subscription": {
"id": "a3f17b22-...",
"is_active": false,
"updated_at": "2026-05-01T10:00:00Z"
}
}ULTRA only. Permanently delete a subscription. 404 if it isn't yours.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Subscription ID |
{
"deleted": true,
"id": "a3f17b22-..."
}List your KOL-coordination push-alert rules — active and inactive. A rule fires when at least `min_kols` distinct tracked KOLs converge on the same token inside a rolling window. PRO/ULTRA only (PRO: 5 rules, ULTRA: 20 rules).
{
"rules": [
{
"id": "c1a2e0f4-...-e9",
"name": "4+ KOLs, 15m",
"min_kols": 4,
"window_minutes": 15,
"min_score": 0,
"include_majors": false,
"cooldown_min": 30,
"score_jump_break": 20,
"delivery_mode": "websocket",
"webhook_url": null,
"min_mc_usd": null,
"max_mc_usd": null,
"is_active": true,
"created_at": "2026-06-01T09:00:00Z",
"updated_at": "2026-06-01T09:00:00Z"
}
]
}Create a KOL-coordination push-alert rule. Fires when ≥ `min_kols` distinct tracked KOLs buy the same token within `window_minutes`. Returns `webhook_secret` once if webhook delivery is configured. PRO/ULTRA only (PRO: 5 rules, ULTRA: 20 rules).
| Name | Type | Default | Description |
|---|---|---|---|
| name | string | — | Optional label (max 64 chars) |
| min_kols | integer | 5 | Distinct KOLs required to converge (2-50) |
| window_minutes | integer | 15 | Rolling convergence window (1-60) |
| min_score | integer | 0 | Minimum coordination score to fire (0-100) |
| include_majors | boolean | false | Include major/blue-chip tokens (excluded by default) |
| cooldown_min | integer | 30 | Per-token cooldown between fires (1-1440 min) |
| score_jump_break | integer | 20 | Re-fire during cooldown if score jumps by this much (0-100) |
| delivery_mode | string | websocket | "websocket", "webhook", or "both" |
| webhook_url | string | — | HTTPS URL — required when delivery_mode includes webhook |
| min_mc_usd | number | — | Only fire if the triggering trade's entry MC ≥ this (USD) |
| max_mc_usd | number | — | Only fire if the triggering trade's entry MC ≤ this (USD) |
{
"rule": {
"id": "c1a2e0f4-...-e9",
"name": "4+ KOLs, 15m",
"min_kols": 4,
"window_minutes": 15,
"delivery_mode": "webhook",
"webhook_url": "https://my.bot/hooks/coord",
"is_active": true,
"created_at": "2026-06-01T09:00:00Z"
},
"webhook_secret": "<32-byte hex — shown once>",
"note": "Save the webhook_secret — it will not be shown again."
}Fetch a single coordination alert rule by ID. Returns 404 if not found or not owned by the caller. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| id | string (UUID) | required | Rule ID — path parameter |
{
"rule": {
"id": "c1a2e0f4-...-e9",
"name": "4+ KOLs, 15m",
"min_kols": 4,
"window_minutes": 15,
"min_score": 0,
"delivery_mode": "websocket",
"is_active": true,
"created_at": "2026-06-01T09:00:00Z",
"updated_at": "2026-06-01T09:00:00Z"
}
}Update a coordination alert rule — accepts any field from Create (all optional) plus `is_active`. Send only the fields you want to change. Pass `min_mc_usd`/`max_mc_usd` as null to clear a bound. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| id | string (UUID) | required | Rule ID — path parameter |
| is_active | boolean | — | Enable or disable the rule |
| min_kols | integer | — | Distinct KOLs required (2-50) |
| min_score | integer | — | Minimum coordination score (0-100) |
| delivery_mode | string | — | "websocket", "webhook", or "both" |
| webhook_url | string | — | HTTPS URL (or null to clear) |
| min_mc_usd | number | — | MC floor in USD (null clears) |
| max_mc_usd | number | — | MC ceiling in USD (null clears) |
{
"rule": {
"id": "c1a2e0f4-...-e9",
"min_kols": 5,
"min_score": 40,
"is_active": false,
"updated_at": "2026-06-02T10:00:00Z"
}
}Permanently delete a coordination alert rule. Returns 404 if not found or not owned by the caller. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| id | string (UUID) | required | Rule ID — path parameter |
{
"deleted": true
}Deployer Hunter
1.2M+ Pump.fun deployers indexed, 85K+ profiled and tier-graded — real-time launch alerts, bonding history, multiplier stats.
Global deployer hunter statistics: tracked deployer count, signals today, bond rate, and tier distribution (elite/good/rising). Available to all tiers.
{
"tracked_count": 6717,
"signals_today": 42,
"bonds_detected": 1893,
"bond_rate": 28.2,
"tiers": {
"elite": 20,
"good": 62,
"rising": 119
}
}Ranked list of deployers by bonding rate, recent performance, or total bonds. Full pagination and complete wallet addresses on all tiers.
| Name | Type | Default | Description |
|---|---|---|---|
| tier | string | — | Filter: "elite", "good", "moderate", "rising", or "cold" |
| sort | string | bonding_rate | Sort: "bonding_rate", "recent_bond_rate", "total_bonded", "last_deploy_at" |
| limit | integer | 20 | Max results (1-50) |
| offset | integer | 0 | Pagination offset |
{
"deployers": [
{
"wallet_address": "7xKv...9mPQ",
"tier": "elite",
"total_tokens_deployed": 18,
"total_bonded": 12,
"bonding_rate": 66.7,
"recent_bond_rate": 80,
"recent_outcomes": "BBBDBBBDBB"
}
],
"total": 20,
"limit": 20,
"offset": 0
}Full deployer profile with stats, tier classification, outcome history, and Pump.fun token data.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Solana wallet address of the deployer |
{
"deployer": {
"wallet_address": "7xKv...9mPQ",
"tier": "elite",
"total_tokens_deployed": 18,
"total_bonded": 12,
"bonding_rate": 66.7,
"recent_outcomes": "BBBDBBBDBB"
},
"pump_tokens": [
{
"mint": "abc1...pump",
"name": "Token A",
"symbol": "TOKA",
"bondingCurvePercentage": 100
}
]
}Paginated token history for a deployer with peak market cap and bond timing.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Solana wallet address |
| limit | integer | 20 | Max results (1-50) |
| offset | integer | 0 | Pagination offset |
{
"tokens": [
{
"token_mint": "abc1...pump",
"token_name": "Token A",
"token_symbol": "TOKA",
"deployed_at": "2026-03-20T12:00:00Z",
"bonded_at": "2026-03-20T12:45:00Z",
"time_to_bond_minutes": 45,
"peak_market_cap": 285000
}
],
"total": 12,
"limit": 20,
"offset": 0
}Real-time alerts when tracked deployers launch new tokens. Enriched with deployer reputation (tier, best_token_peak_mc), the deployer's SOL balance at alert time (`deployer_sol_balance`, null for alerts before 2026-06-21), and KOL buy cross-reference. Cursor-paginated via `before` (preferred over `offset` at scale) — limit 1-50 per call, cursor and offset pagination on every tier.
| Name | Type | Default | Description |
|---|---|---|---|
| since | ISO datetime | — | Only alerts after this timestamp |
| before | ISO datetime | — | Cursor — only alerts strictly older than this timestamp. Pass `next_before` from previous response. |
| limit | integer | 20 | Max results (1-50) |
| offset | integer | 0 | Legacy offset pagination (prefer `before` for polling) |
| tier | string | — | Filter by deployer tier: elite, good, moderate, rising, or cold. |
| alert_type | string | — | Filter by alert_type (e.g. new_deploy, bonded) |
| priority | string | — | "high", "medium", or "low" |
| min_kol_buys | integer | — | Only alerts where at least N KOLs bought (1-100) |
| token_mint | string | — | Scope to one token's alert history — combine with alert_type=bonded to check if/when a specific token bonded |
{
"alerts": [
{
"id": 918342,
"token_mint": "def2...pump",
"token_name": "New Token",
"token_symbol": "NEW",
"alert_type": "new_deploy",
"title": "Elite deployer launched NEW",
"message": "7xKv...9mPQ (elite, 71% bond rate) deployed a new token on pump.fun",
"priority": "high",
"created_at": "2026-03-28T14:30:00Z",
"market_cap_at_alert": 6120,
"deployer_sol_balance": 42.5301,
"launchpad": "pumpfun",
"deployers": {
"wallet_address": "7xKv...9mPQ",
"tier": "elite",
"total_tokens_deployed": 17,
"total_bonded": 12,
"instant_bonds": 3,
"bonding_rate": 0.71,
"recent_outcomes": "BBFBBBBFBB",
"recent_bond_rate": 0.8,
"best_token_peak_mc": 2850000,
"runner_rate": 0.45,
"runner_tokens": 5,
"labeled_tokens": 11,
"avg_time_to_bond_minutes": 38.5
},
"kol_buys": {
"count": 2,
"total_sol": 7.4,
"kols": [
"Cented",
"Gake"
]
}
}
],
"limit": 20,
"offset": 0,
"next_before": "2026-03-28T14:30:00Z",
"data_age_seconds": 42
}Alert performance metrics: bond rate, multiplier distribution (% hitting 2x, 5x, 10x, 50x), and per-tier breakdown.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | all | Time period: "7d", "30d", or "all" |
{
"period": "all",
"total_alerts": 1247,
"total_bonded": 352,
"bond_rate": 28.2,
"multiplier_distribution": {
"2x": 65.3,
"5x": 41.2,
"10x": 22.7,
"50x": 4.8
},
"by_tier": {
"elite": {
"alerts": 89,
"bonded": 26,
"bond_rate": 29.2
},
"good": {
"alerts": 412,
"bonded": 98,
"bond_rate": 23.8
}
}
}Top tokens by market cap multiplier from deployer alerts. See which alerted tokens performed best. Up to 20 results.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 7d | Time period: "7d", "30d", or "all" |
| limit | integer | 5 | Max results (1-20) |
{
"tokens": [
{
"token_mint": "ghi3...pump",
"token_name": "Moon Token",
"mc_at_alert": 5000,
"peak_market_cap": 750000,
"mc_multiplier": 150,
"deployer_tier": "elite"
}
],
"period": "7d",
"limit": 5
}Latest tokens that graduated the Pump.fun bonding curve with deployer info. Use `since` for incremental polling — response returns `next_since` cursor.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 20 | Max results (1-50) |
| since | ISO datetime | — | Only bonds strictly newer than this timestamp. Pass `next_since` from the previous response to avoid re-fetching the same rows. |
| tier | string | — | Filter by deployer tier: elite, good, moderate, rising, or cold |
| peak_mc_min | integer | — | Only bonds that reached at least this peak market cap (USD) |
{
"tokens": [
{
"token_mint": "jkl4...pump",
"token_name": "Bonded Token",
"token_symbol": "BOND",
"bonded_at": "2026-03-28T13:15:00Z",
"time_to_bond_minutes": 23,
"peak_market_cap": 420000,
"deployers": {
"wallet_address": "7xKv...9mPQ",
"tier": "elite",
"bonding_rate": 66.7
}
}
],
"limit": 20
}Deployer skill curve showing performance evolution over time. Current and longest streaks, rolling 10-token bond rate windows, improvement trend, deployment cadence, and recovery speed.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string | — | Deployer wallet address (base58, path param) |
{
"deployer": {
"wallet_address": "7xKv...9mPQ",
"total_tokens_deployed": 45,
"bonding_rate": 0.267,
"recent_bond_rate": 0.4,
"tier": "good"
},
"trajectory": {
"current_streak": {
"type": "bond",
"count": 3
},
"longest_bond_streak": 5,
"longest_fail_streak": 8,
"trend": "improving",
"avg_days_between_deploys": 2.3,
"avg_recovery_tokens": 3.1,
"total_tokens_analyzed": 45
}
}A deployer's daily reputation time-series — one snapshot per day of how their stats looked AT THAT POINT, so you can backtest "was this deployer elite at the time it launched token X?" without look-ahead bias. Each snapshot carries the tier, tracking status, cumulative totals, and bond rates the deployer had on that date, letting you reconstruct their reputation as it actually was rather than as it looks today after later launches. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Deployer wallet address (base58) |
| limit | integer | 90 | Number of daily snapshots to return, most recent first (1-365) |
{
"is_deployer": true,
"wallet": "7xKv...9mPQ",
"snapshots": [
{
"date": "2026-07-05",
"tier": "elite",
"is_tracked": true,
"total_deployed": 18,
"total_bonded": 12,
"bonding_rate": 0.667,
"recent_bond_rate": 0.7,
"avg_peak_mc": 312000,
"best_token_peak_mc": 750000
},
{
"date": "2026-07-04",
"tier": "good",
"is_tracked": true,
"total_deployed": 17,
"total_bonded": 11,
"bonding_rate": 0.647,
"recent_bond_rate": 0.6,
"avg_peak_mc": 298000,
"best_token_peak_mc": 750000
}
]
}A deployer's reputation exactly as it stood on one date — no look-ahead. Returns the latest write-on-change snapshot at or before `date` (default: today, UTC). `carried: true` means the snapshot was recorded before `date` and hadn't changed by then; no snapshot at or before `date` returns `as_of: false, snapshot: null` — we knew nothing about this deployer yet, nothing is synthesized. A wallet that has never deployed returns `is_deployer: false`. Companion to /deployer-hunter/{wallet}/history (the whole series) and /wallet/{address}/flags?as_of= (every flag source). PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | path | required | Deployer wallet address (base58) |
| date | YYYY-MM-DD | today (UTC) | The date to answer for |
{
"is_deployer": true,
"wallet": "UVXV1BHPQFBfDS1tSVb48kXFMkeZ7ojfFkfsVFEAgyE",
"requested_date": "2026-08-01",
"as_of": true,
"snapshot": {
"snapshot_date": "2026-04-29",
"carried": true,
"tier": "good",
"is_tracked": true,
"total_deployed": 29,
"total_bonded": 9,
"bonding_rate": 0.3103,
"recent_bond_rate": 0.8,
"avg_peak_mc": 21243,
"best_token_peak_mc": 77635
},
"first_snapshot_date": "2026-04-07",
"note": "Latest write-on-change snapshot at or before requested_date. carried=true means the state was recorded earlier and had not changed by requested_date."
}Pump.fun creator-fee rewards, answered two ways that are never merged: `collected` is what actually reached the wallet (direct vault claims, social-handle claims, and shareholder payouts on ANY token — including ones it didn't deploy); `attributed` is every fee payout on tokens this wallet DEPLOYED, whoever received it, split to_self / to_others. A deployer whose attributed total dwarfs its collected total is redirecting its fees — to a launch tool, a KOL, or a social handle — and `redirected_pct` is that gap made explicit. Works for any wallet, not just deployers: a non-deployer that receives fee shares still gets its `collected` side. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | path | required | Solana wallet address |
{
"wallet": "gVDXhoGbePACvSqN7CZBtQXFW9eyJwsgudPEwSydAkx",
"is_deployer": true,
"tokens_in_scope": 96,
"collected": {
"sol": 19.288,
"usdc": 0,
"usd": 2023.49,
"direct_claims": {
"sol": 19.288,
"usd": 2023.49,
"count": 8
},
"social_claims": {
"sol": 0,
"usd": 0,
"count": 0
},
"share_payouts": {
"sol": 0,
"usd": 0,
"count": 0,
"tokens": 0
}
},
"attributed": {
"sol": 2448.215,
"usdc": 0,
"usd": 256842.27,
"count": 113,
"to_self": {
"sol": 0,
"usd": 0
},
"to_others": {
"sol": 2448.215,
"usd": 256842.27
},
"redirected_pct": 100,
"tokens_with_payouts": 19,
"recipients": 21
},
"top_tokens": [
{
"mint": "EVgwa5CHBVwk6sCTa4QJrVXYBKngZhYqh2a2ZGDApump",
"quote": "SOL",
"total": 1604.9,
"total_usd": 168370.57,
"payouts": 30,
"recipients": 1
}
]
}Alpha Wallets
Scored from 1.5M+ wallets that bought Pump.fun tokens within the first 20 buyers.
Leaderboard of statistically profitable wallets scored from 1.5M+ early buyers tracked across Pump.fun tokens. Returns wallets ranked by win rate, realized PnL, or ROI. Bot wallets can be excluded. Up to 100 results per page on every tier (offset pagination); ULTRA widens the ranked window to 500 wallets and adds behavioral signals (bot_confidence, bundle_rate, buy_size_stddev, active_hours).
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | all | Time period: "7d", "30d", or "all" |
| min_tokens | integer | 5 | Minimum tokens traded to qualify (1–20) |
| sort | string | win_rate | Sort by: "win_rate", "pnl", or "roi" |
| exclude_bots | boolean | true | Exclude medium/high bot-confidence wallets |
{
"leaderboard": [
{
"rank": 1,
"wallet": "7xKv...9mPQ",
"tokens_traded": 12,
"wins": 9,
"losses": 3,
"win_rate": 0.75,
"net_pnl_sol": 42.3,
"total_sol_bought": 95,
"total_sol_sold": 137.3,
"roi": 0.445,
"last_seen": "2026-04-10T14:22:00Z"
}
],
"total": 1,
"period": "all",
"sort": "win_rate",
"min_tokens": 5,
"exclude_bots": true
}Full alpha profile for a single wallet. Returns per-token trade breakdown from early buyer data, aggregate stats, and a bot_signals array explaining the confidence rating (signals that triggered the classification). ULTRA only — BASIC/PRO receive HTTP 403.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Solana wallet address (base58) |
{
"wallet": "7xKv...9mPQ",
"summary": {
"tokens_traded": 12,
"wins": 9,
"losses": 3,
"win_rate": 0.75,
"net_pnl_sol": 42.3,
"bundle_rate": 0.08,
"buy_size_stddev": 0.12,
"active_hours": 14,
"bot_confidence": "low"
},
"positions": [
{
"token_symbol": "BONK",
"pnl_sol": 18.4,
"roi_pct": 92,
"result": "win",
"first_buy_at": "2026-04-01T08:00:00Z"
}
],
"bot_signals": [
{
"signal": "consistent_buy_size",
"detail": "stddev=0.12 SOL — not fixed-amount"
}
]
}Find wallets behaviorally linked to a target wallet — these are wallets that co-bought 3+ of the same tokens within a 2-second window. Likely the same actor split across wallets, or a coordinated group. Returns similarity scores. ULTRA only — BASIC/PRO receive HTTP 403.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Solana wallet address (base58) |
{
"wallet": "7xKv...9mPQ",
"linked_wallets": [
{
"wallet_address": "CDiw...XsT9",
"shared_tokens": 9,
"similarity_score": 0.81
},
{
"wallet_address": "65NR...oh7B",
"shared_tokens": 5,
"similarity_score": 0.45
}
],
"total": 2
}Aggregated KOL consensus view for a token mint — how many KOLs are accumulating vs distributing, net SOL flow, conviction score, and per-KOL stance breakdown. Useful for gauging whether smart money is bullish or bearish on a specific token right now.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Solana token mint address (base58) |
{
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"consensus": {
"accumulating_kols": 7,
"distributing_kols": 2,
"net_sol_flow": 47.82,
"conviction_score": 78,
"signal": "accumulating"
},
"kols": [
{
"name": "Cented",
"stance": "accumulating",
"net_sol": 12.4
},
{
"name": "Marcell",
"stance": "accumulating",
"net_sol": 8.7
}
]
}Historical peak market cap snapshots for a token — ATH market cap, timestamps of each peak, drawdown from ATH, and recovery windows. Useful for gauging whether a token has already peaked or is re-approaching highs. Shows the full MC trajectory from first-seen to present.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Solana token mint address (base58) |
{
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"ath_mc_usd": 750000,
"ath_at": "2026-05-18T14:22:00Z",
"current_mc_usd": 423000,
"drawdown_from_ath_pct": 43.6,
"peaks": [
{
"mc_usd": 285000,
"peaked_at": "2026-05-15T08:00:00Z",
"drawdown_pct": 62
},
{
"mc_usd": 750000,
"peaked_at": "2026-05-18T14:22:00Z",
"drawdown_pct": 43.6
}
],
"first_seen_at": "2026-05-14T10:00:00Z"
}First 10–20 non-deployer early buyers for a token mint, enriched with historical win rates, realized PnL, bot confidence, KOL identity, and bundle flags. Each buyer also carries their in-token exit/PnL: realized_pnl_sol (SOL profit taken on this mint), total_sol_bought / total_sol_sold, first_sell_at (null if never sold), and still_holding — so you can see which smart-money buyers have already cashed out vs are still in. Includes a summary block with buyer_quality_score. PRO: top 10 with full wallets. ULTRA: top 20 with full wallets.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
{
"mint": "EPjF...Dt1v",
"buyers": [
{
"rank": 1,
"wallet": "7xKv...9mPQ",
"first_buy_sol": 2.5,
"first_buy_at": "2026-04-01T10:00:00Z",
"is_bundle": false,
"is_kol": true,
"kol_name": "Cented",
"bot_confidence": "low",
"historical_win_rate": 0.72,
"historical_pnl_sol": 38.4,
"historical_tokens": 15,
"realized_pnl_sol": 12.8,
"total_sol_bought": 2.5,
"total_sol_sold": 15.3,
"first_sell_at": "2026-04-01T11:42:00Z",
"still_holding": true
}
],
"summary": {
"known_alpha_wallets": 6,
"known_kols": 2,
"bundle_buyers": 1,
"buyer_quality_score": 74,
"confidence": "medium",
"signal": "positive"
}
}Every wallet that traded this mint, ranked by realized SOL PnL (or ROI) — the most profitable traders on the token, early or late. Uses the same average-cost-basis method as /wallet/{address}/pnl, so a wallet's realized_pnl_sol matches across endpoints. Each trader is enriched with reputation flags (is_kol, is_alpha_tracked, bot_confidence, historical win rate) so you can tell smart money from a bot round-trip. Distinct from cap-table (first ~20 early buyers by entry order). PRO: top 25. ULTRA: top 100.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
| limit | int | 25 | Max traders (capped at 25 PRO / 100 ULTRA) |
| sort | string | pnl | pnl | roi |
| window_days | int | 90 | Look-back window (1–180) |
| min_bought_sol | number | 0.1 | Drop dust wallets below this bought-SOL |
{
"mint": "EPjF...Dt1v",
"sort": "pnl",
"window_days": 90,
"traders": [
{
"rank": 1,
"wallet": "7xKv...9mPQ",
"trades": 14,
"buys": 6,
"sells": 8,
"bought_sol": 12.5,
"sold_sol": 41.2,
"realized_pnl_sol": 28.7,
"unrealized_pnl_sol": 3.1,
"total_pnl_sol": 31.8,
"held_value_sol": 3.1,
"roi": 2.29,
"still_holding": true,
"first_trade_at": "2026-06-01T10:00:00Z",
"last_trade_at": "2026-06-02T14:20:00Z",
"is_kol": true,
"kol_name": "Cented",
"is_alpha_tracked": true,
"bot_confidence": "low",
"historical_win_rate": 0.72,
"historical_pnl_sol": 38.4,
"historical_tokens": 15
}
],
"summary": {
"returned": 25,
"known_kols": 3,
"known_alpha_wallets": 9,
"net_realized_pnl_sol": 142.6
}
}Live holder census read from the ledger at confirmed: every token account of the mint (mint-scoped getProgramAccounts), merged per owner — so holder_count is EXACT (distinct non-zero owners minus pools/curves/burns), not a trade-derived estimate. Ranks 1–100 retained; top1/10/20/50/100 shares over the circulating denominator. Each disclosed owner is labelled from our wallet intelligence (deployer / kol / early_buyer / bundle / bot / dump_cluster — empty means unknown to us, not clean). Excluded owners are NAMED: pool (+ dex, pool_address), bonding_curve (pump.fun / LaunchLab), burn, or program_account when we cannot attribute the PDA; pool_pct / burned_pct / program_pct split the exclusion. Mega-caps the provider refuses fall back to a top-20 view with holder_count null and census_fallback_reason set. Big tokens can take 5–30 s upstream: you get 503 holder_scan_in_progress with Retry-After 20 while the scan finishes into the cache. Amounts are raw u64 strings. PRO shows ranks 1–10, ULTRA 1–50, BUSINESS 1–100; the maths is tier-independent.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
{
"mint": "RmtM…NDYk",
"slot": 361248112,
"holders": [
{
"rank": 1,
"owner": "7xKq…p3Vw",
"token_accounts": [
"9Ab2…kL1m"
],
"amount_raw": "48210000000000",
"amount": 48210000,
"pct_of_supply": 4.821,
"pct_of_circulating": 5.18,
"labels": [
"early_buyer",
"bundle"
],
"kol_name": null,
"early_buyer_rank": 3,
"bot_confidence": "low",
"historical_win_rate": null
}
],
"count": 10,
"disclosed": 10,
"excluded": [
{
"owner": "DBdt…Qk9e",
"token_accounts": [
"3Fh1…yT8u"
],
"amount_raw": "26288000000000",
"pct_of_supply": 2.6288,
"reason": "pool",
"dex": "pumpswap",
"pool_address": "DBdt…Qk9e"
},
{
"owner": "3b3W…hLWQ",
"token_accounts": [
"Hn7z…c2Qa"
],
"amount_raw": "13155000000000",
"pct_of_supply": 1.3155,
"reason": "pool",
"dex": "meteora",
"pool_address": "3b3W…hLWQ"
}
],
"concentration": {
"holder_count": 7724,
"holder_count_source": "census",
"token_accounts_nonzero": 7731,
"supply_raw": "1000000000000000",
"circulating_raw": "960676000000000",
"decimals": 6,
"top1_share": 5.18,
"top10_share": 21.4,
"top20_share": 29.7,
"top50_share": 41.2,
"top100_share": 49.9,
"pool_and_program_pct": 6.9324,
"pool_pct": 3.9444,
"burned_pct": 0,
"program_pct": 2.988,
"deployer_pct": 0,
"kol_pct": 1.2,
"early_buyer_pct": 9.8,
"bundle_pct": 6.1,
"bot_pct": 2.3,
"dump_cluster_pct": 0.4,
"ranked_owners_available": 100
},
"source": {
"method": "getProgramAccounts_census",
"token_program": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
"rpc_cap": 100,
"commitment": "confirmed",
"scan_ms": 412,
"census_fallback_reason": null
}
}Every on-chain token lock / vesting contract on a mint — Streamflow streams, Jupiter Lock vesting escrows, Bonfida token-vesting — decoded from the locker programs' account state, with a LIVE view computed at request time: what is still locked right now, what has unlocked, what is claimable, and the next unlock event. The summary rolls up locked/deposited totals (raw, ui, usd, % of supply), the 7d/30d forward unlock schedule, distinct lockers, and how many active contracts the sender can still cancel — 'did the team lock, how much, until when, and can they pull it'. Token/vesting locks only (LP locks are a different thing). Amounts are base-unit strings; ui/usd/pct are null when decimals/price are unknown. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
| status | string | — | Optional filter: active | completed | cancelled | closed |
| program | string | — | Optional filter: streamflow | jupiter_lock | bonfida_vesting |
| limit | number | 200 | 1–500 rows (summary always covers the whole mint) |
{
"mint": "eZBK…pump",
"token": {
"symbol": "ENTROPY",
"decimals": 6,
"price_usd": 0.0000266,
"supply": 999120418,
"facts_resolved": true
},
"summary": {
"lock_count": 4,
"active_count": 3,
"by_program": {
"streamflow": 4
},
"by_kind": {
"lock": 4
},
"distinct_lockers": 1,
"locked": 70460647,
"locked_usd": 1875.53,
"locked_pct_of_supply": 7.05,
"unlocking_7d": 0,
"unlocking_30d": 0,
"next_unlock": {
"at": "2026-12-25T05:00:00Z",
"kind": "cliff",
"amount": 20000000,
"amount_usd": 532.36,
"lock_account": "825J…4ei1"
},
"active_cancelable_by_sender": 0
},
"locks": [
{
"lock_account": "825J…4ei1",
"program": "streamflow",
"kind": "lock",
"status": "active",
"sender": "3Vd4…kQ2p",
"recipient": "3Vd4…kQ2p",
"name": "Team lock",
"amount_raw": "20000000000000",
"amount": 20000000,
"amount_usd": 532.36,
"locked": 20000000,
"unlocked": 0,
"claimable": 0,
"cliff_at": "2026-12-25T05:00:00Z",
"end_at": "2026-12-25T05:00:01Z",
"period_seconds": 1,
"continuous": true,
"cancelable_by_sender": false,
"transferable": false,
"next_unlock": {
"at": "2026-12-25T05:00:00Z",
"kind": "cliff",
"amount": 20000000
},
"created_at": "2026-08-17T18:44:37Z",
"tx_signature": "5kZ…Qw"
}
]
}Token momentum fires, newest first. surge = a token < 30 min old running vs its LAUNCH market cap — tier early (≤10 min, ≥$12k, ≥3× launch MC), strong (≤30 min, ≥$30k, ≥6× and ≥2× the lowest sample of the last 3 min), breakout (≤2 min, ≥$45k, ≥8×); each tier fires once per mint. revival = a token with no trade candle for ≥24 h (measured across every DEX we price, so graduated Raydium/Meteora tokens count) that started trading again, CONFIRMED by ≥5 buys + ≥$500 buy volume + ≥1.5× the pre-dormancy MC — never by the price mark alone. Every fire carries the burst tape (unique buyers where wallet data exists, null otherwise), KOL involvement, the first-20 early-buyer cohort (bundled / sold / sniper wallets), deployer reputation and risk_flags[] with documented thresholds. Fires ≥65 min old carry their +1 h outcome; stats=1 prints per-tier hit-rates, out-of-sample by construction. Pushed live on the token:surges WebSocket channel and as webhook events token:surge / token:revival. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| kind | string | all | surge | revival |
| tier | string | all | early | strong | breakout (surges only — 400 with kind=revival) |
| since | before | ISO datetime | none | Cursors on fired_at (pagination.next_since / next_before) |
| mint | launchpad | deployer_tier | string | none | Scope to one mint / launchpad (pumpfun, launchlab, pumpswap…) / deployer tier |
| min_mc_usd | max_mc_usd | min_buys | number | none | Market-cap band and buys-on-the-tape floor at fire time |
| exclude_flags | string | none | Comma list of risk flags; rows carrying ANY are dropped (unknown flag → 400 + known_flags[]) |
| only_clean | boolean | false | Only fires with no risk flags |
| stats | days | boolean | number | false | 7 | Add per-(kind, tier) +1h hit-rates over the last N days (1–30) |
| limit | number | 50 | 1–200 |
{
"events": [
{
"id": 4812,
"kind": "surge",
"tier": "strong",
"mint": "9kQu…pump",
"symbol": "RUNNER",
"launchpad": "pumpfun",
"fired_at": "2026-08-28T10:00:00Z",
"birth_source": "sniper",
"age_seconds": 450,
"market_cap_usd": 38400,
"liquidity_usd": 9100,
"baseline_mc_usd": 5600,
"baseline_source": "launch",
"mc_multiple": 6.857,
"mc_change_3m_pct": 112.4,
"tape": {
"buys": 96,
"sells": 31,
"buy_volume_usd": 21300,
"sell_volume_usd": 6900,
"unique_buyers": 58,
"trades_per_wallet": 2.02,
"wallet_data_available": true
},
"kol": {
"buyers": 2,
"names": [
"alpha_scout",
"degen_dan"
]
},
"early_buyers": {
"count": 20,
"bundled": 0,
"cohort_sol": 18.4,
"sold": 3,
"sniper_wallets": 1
},
"deployer": {
"tier": "good",
"runner_rate": 0.27,
"labeled_tokens": 22
},
"risk_flags": [],
"outcome": {
"mc_usd_1h_after": 51200,
"peak_mc_usd_1h_after": 74800,
"mc_1h_multiple": 1.333,
"peak_1h_multiple": 1.948,
"priced_after_1h": true
}
},
{
"id": 4813,
"kind": "revival",
"tier": null,
"mint": "8vdc…pump",
"symbol": "GHOST",
"primary_dex": "pumpswap",
"fired_at": "2026-08-28T10:05:00Z",
"dormant_hours": 412.6,
"prev_mc_usd": 22100,
"market_cap_usd": 61000,
"mc_vs_prev_multiple": 2.76,
"peak_mc_usd": 340000,
"pct_of_peak": 17.94,
"tape": {
"buys": 27,
"sells": 4,
"buy_volume_usd": 8900,
"unique_buyers": 19,
"wallet_data_available": true
},
"kol": {
"buyers": 1,
"names": [
"cto_hunter"
]
},
"early_buyers": {
"count": 20,
"bundled": 2,
"sold": 17,
"sniper_wallets": 0
},
"deployer_tier": "moderate",
"risk_flags": [
"early_buyers_exiting"
],
"outcome": null
}
],
"pagination": {
"limit": 50,
"count": 2,
"has_more": true,
"next_since": "2026-08-28T10:05:00Z",
"next_before": "2026-08-28T10:00:00Z"
},
"stats": {
"days": 7,
"rows": [
{
"kind": "surge",
"tier": "strong",
"fires": 412,
"with_outcome": 398,
"up_1h_pct": 46.5,
"median_peak_multiple": 1.41,
"doubled_1h_pct": 18.3
}
]
},
"stream": {
"channel": "token:surges",
"url": "wss://madeonsol.com/ws/v1/stream"
},
"definitions": {
"surge": {
"early": {
"max_age_seconds": 600,
"min_market_cap_usd": 12000,
"min_launch_multiple": 3
}
},
"revival": {
"min_dormant_hours": 24,
"min_buys": 5,
"min_buy_volume_usd": 500
}
}
}Cross-token feed of NEW lock / vesting contracts, newest first — who just locked tokens, of what mint, how much, until when. Same row shape as /tokens/{mint}/locks plus a token block (symbol, decimals, price, market cap). Poll with since= (cursor = pagination.next_since) or subscribe to the token:locks WebSocket channel for a push the moment the contract lands (with filters.lifecycle:true the channel also pushes claims, cancels, closes, updates and upcoming / available unlocks, since 2026-09-23). Filters: mint, sender, recipient, program, kind, status, min_usd, min_pct_of_supply. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| since | ISO datetime | none | Only contracts created after this instant (use pagination.next_since) |
| before | ISO datetime | none | Page back — only contracts created before this instant |
| mint | sender | recipient | string | none | Scope to one mint / locker / beneficiary |
| program | kind | status | string | — | streamflow|jupiter_lock|bonfida_vesting · lock|vesting · active|completed|cancelled|closed |
| min_usd | min_pct_of_supply | number | none | Deposited amount floor (needs price / supply) |
| limit | number | 50 | 1–100 |
{
"locks": [
{
"lock_account": "825J…4ei1",
"program": "streamflow",
"kind": "lock",
"status": "active",
"mint": "eZBK…pump",
"token": {
"symbol": "ENTROPY",
"decimals": 6,
"price_usd": 0.0000266
},
"sender": "3Vd4…kQ2p",
"amount": 20000000,
"amount_usd": 532.36,
"amount_pct_of_supply": 2,
"locked": 20000000,
"cliff_at": "2026-12-25T05:00:00Z",
"end_at": "2026-12-25T05:00:01Z",
"cancelable_by_sender": false,
"created_at": "2026-08-17T18:44:37Z"
}
],
"pagination": {
"limit": 50,
"count": 1,
"has_more": true,
"next_since": "2026-08-17T18:44:37Z",
"next_before": "2026-08-17T18:44:37Z"
},
"stream": {
"channel": "token:locks",
"url": "wss://madeonsol.com/ws/v1/stream"
}
}Upcoming unlock EVENTS across all active lock / vesting contracts inside a window — cliffs, periodic releases (hourly or coarser) and final unlocks — i.e. which tokens have locked supply hitting the market this week, how much, from whose lock. One entry per active contract = its next unlock event in the window (event = cliff | period | final | tranche) plus window_amount_* = that contract's total release over the whole window. Continuous per-second streams contribute only their cliff/final events. Sort soonest (default), largest_usd or largest_pct. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| within | string | 7d | 1h | 6h | 24h | 3d | 7d | 14d | 30d | 90d |
| mint | program | kind | string | — | Optional filters to scope the scan |
| min_usd | min_pct_of_supply | number | none | Floor on the next-event amount |
| sort | string | soonest | soonest | largest_usd | largest_pct |
| limit | number | 50 | 1–200 |
{
"window": {
"within": "7d",
"from": "2026-08-17T18:47:00Z",
"to": "2026-08-24T18:47:00Z"
},
"unlocks": [
{
"unlock_at": "2026-08-24T04:00:00Z",
"in_seconds": 551580,
"event": "cliff",
"amount": 91588000,
"amount_usd": 6490.74,
"amount_pct_of_supply": 9.16,
"window_amount": 91588000,
"mint": "Epht…FwSg",
"token": {
"symbol": "XYZ",
"decimals": 6,
"price_usd": 0.0000709
},
"lock": {
"lock_account": "9nQd…Lp3a",
"program": "streamflow",
"kind": "lock",
"sender": "7Hh2…mR1c",
"locked": 91588000,
"cliff_at": "2026-08-24T04:00:00Z",
"cancelable_by_sender": false
}
}
],
"pagination": {
"limit": 50,
"count": 1,
"total_in_window": 902,
"has_more": true
}
}Every decoded pump.fun fee event, newest first: distribution (creator fees paid out pro-rata to the SharingConfig shareholders — fees redirected to others — with payouts[] per address), social_claim (fees earmarked for a platform identity — platform 2 = X, user_id = platform-native numeric id — claimed to a wallet), shares_created / updated / reset, creator_transferred, creator_claim and holder_distribution (creator fees airdropped pro-rata to token holders; both excluded unless requested). Default 100%-to-creator configs and zero-amount distributions are not stored. since= cursor or WS channel token:fee_claims. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| type | string | all but creator_claim, holder_distribution | Comma list: distribution, social_claim, shares_created, shares_updated, shares_reset, creator_transferred, creator_claim, holder_distribution |
| mint | recipient | actor | string | none | Scope to a coin / payout recipient / signer |
| social_platform | social_user_id | string | none | Scope to a social identity (2 = X; numeric user id) |
| min_sol | number | none | Amount floor in SOL |
| since | before | ISO datetime | none | Cursors on the event time |
| limit | number | 50 | 1–100 |
{
"events": [
{
"type": "distribution",
"at": "2026-08-17T19:21:18Z",
"mint": "E2rQ…npump",
"amount": 5.1427,
"amount_usd": 389.71,
"quote": "SOL",
"payouts": [
{
"address": "BYp2…QoY4",
"share_bps": 10000,
"amount": 5.1427,
"amount_usd": 389.71
}
]
},
{
"type": "social_claim",
"at": "2026-08-17T16:46:26Z",
"mint": null,
"recipient": "9kQz…Ht2c",
"amount": 42.18,
"amount_usd": 3196.4,
"social": {
"platform": 2,
"platform_label": "x",
"user_id": "22895867",
"pda": "71zp…BmSd"
}
}
],
"pagination": {
"limit": 50,
"count": 2,
"has_more": true,
"next_since": "2026-08-17T19:21:18Z"
},
"stream": {
"channel": "token:fee_claims"
}
}Resolves a token's same-slot bundle cohort — the wallets that bought within a single slot at/near launch (≥3 wallets in one slot flags is_bundle) — and, critically, how much of supply that cohort still holds. Computed from confirmed token_trades, not a shred/mempool feed. The headline field is held_pct_of_supply: the cohort's net held tokens ÷ token supply, clamped to 0–1. bundle_kind is atomic_tx (all buys in one transaction), same_slot (multiple txs, one slot), or none. Caveats: buy_volume is cumulative buy volume and can exceed supply; tokens_held is a swap-derived net proxy (buys minus sells), not a holder-account census. All tiers, field-gated: BASIC gets the scalar bundle block only; PRO adds the top-10 cohort wallets with flags (held_ratio, has_sold, atomic, is_kol); ULTRA adds the full cohort plus KOL/alpha identity (kol_name, win_rate, bot_confidence, tokens_held).
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
{
"mint": "EPjF...Dt1v",
"bundle": {
"wallet_count": 5,
"bundle_kind": "same_slot",
"held_ratio": 0.32,
"held_pct_of_supply": 0.28,
"fully_exited": false,
"buy_volume": 41800000,
"tokens_held": 280000000
},
"wallets": [
{
"rank": 1,
"wallet": "7xKv...9mPQ",
"held_ratio": 0.71,
"has_sold": false,
"atomic": true,
"is_kol": true,
"kol_name": "Cented",
"win_rate": 0.68,
"bot_confidence": "low",
"tokens_held": 120000000
},
{
"rank": 2,
"wallet": "3nQd...42aa",
"held_ratio": 0,
"has_sold": true,
"atomic": true,
"is_kol": false,
"kol_name": null,
"win_rate": null,
"bot_confidence": "high",
"tokens_held": 0
}
]
}0–100 buyer quality score for a token's early-buyer cohort. Score formula: average win rate of non-bot early buyers × 100, plus KOL bonus (first KOL = +10, each extra = +5, max +20), minus bundle penalties (−5 per bundled buyer in top 10, −15 if >10/20 are bundles), minus bot domination (−20 if >50% are high-confidence bots). Returns score, confidence, signal, and full breakdown (alpha_wallet_count, kol_count, bundle_buyer_count, avg_historical_win_rate, bot_dominated, dump_cluster_count, recycled_early_buyer_count). dump_cluster_count = first-20 buyers on the rolling dump-cluster list (wallets whose recent first-20 appearances are exclusively on tokens that peaked <15 min after deploy — out-of-sample, 3+ such wallets predicted a sub-15-min peak 94% of the time vs 61% base). recycled_early_buyer_count = first-20 buyers with 5+ recent first-20 appearances of any kind; a heavily recycled cohort with dump_cluster_count 0 historically leans the other way. Informational fields — they do not move the score. List refreshes daily over a trailing 42-day window. When dump_cluster_count ≥ 1 the response also carries signal_stats.dump_cluster_count — the live out-of-sample efficacy of that value (bucket k>=1 / k>=3 / k>=5, hit_rate, base_rate, lift, sample_n, window_days, as_of, summary) so the raw count is readable. Results cached 5 minutes per mint. For bulk scoring use POST /tokens/batch/buyer-quality.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
{
"mint": "EPjF...Dt1v",
"score": 74,
"confidence": "medium",
"signal": "positive",
"cached_at": "2026-04-14T12:00:00Z",
"breakdown": {
"alpha_wallet_count": 6,
"kol_count": 2,
"bundle_buyer_count": 1,
"avg_historical_win_rate": 68.5,
"bot_dominated": false,
"wallets_with_history": 9,
"qualified_win_rate_wallets": 7,
"dump_cluster_count": 3,
"recycled_early_buyer_count": 4
},
"signal_stats": {
"dump_cluster_count": {
"value": 3,
"bucket": "k>=3",
"outcome": "dump",
"hit_rate": 0.94,
"base_rate": 0.61,
"lift": 1.54,
"sample_n": 349,
"window_days": 14,
"as_of": "2026-04-14T03:00:00Z",
"summary": "dump_cluster_count=3 (k>=3): 94% of such tokens dumped vs 61% base (1.54× lift), n=349, 14d OOS"
}
}
}Resolve a ticker or name to a mint. Ranked, not just matched: exact ticker first, then symbol prefix, then name prefix, then trigram-fuzzy — and inside each tier by liquidity, so "bonk" returns the $2M-liquidity Bonk before ten thousand squats named BONK. Every row carries market cap, liquidity, primary DEX and last trade so you can disambiguate in one hop and pass the mint on to /token/{mint}. Searches OUR universe (memecoin / launchpad tokens the price tracker has seen): native SOL and blue-chips are excluded by design and the response says so in a note. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| q | string | required | Ticker or name fragment (1-64 chars) |
| limit | integer | 10 | Max results (1-50) |
{
"q": "bonk",
"count": 2,
"results": [
{
"mint": "DezXAZ8z...B263",
"symbol": "BONK",
"name": "Bonk",
"match": "exact_symbol",
"market_cap_usd": 1420000000,
"liquidity_usd": 2100000,
"primary_dex": "raydium",
"last_trade_at": "2026-08-18T09:41:02Z"
},
{
"mint": "7GCihgDB...pump",
"symbol": "BONKAI",
"name": "Bonk AI",
"match": "symbol_prefix",
"market_cap_usd": 84000,
"liquidity_usd": 12400,
"primary_dex": "pumpswap",
"last_trade_at": "2026-08-18T09:12:44Z"
}
]
}Transparent 0–100 rug-risk score for a token mint, with a full per-factor breakdown so the score is auditable — not an opaque verdict. Higher = riskier. Returns risk_score, band (safe / caution / danger), factors[] (each with key, label, status, points, and a human-readable detail — including supply_inflation, which flags when on-chain supply has grown above the expected amount, i.e. post-launch minting), the raw inputs the score was computed from, and score_version. inputs.sniper_footprint is the slot-window snipe rollup (slots deploy−1 to deploy+3): buys, buyers, SOL, supply_pct, sniper_wallet_buys, data_available, as_of — informational, it does not move the score, and data_available: false means the mint is outside our capture window, not zero snipes. The top-level dev block is the deployer self-activity read: the dev's exact self-buy in the create transaction (buy_sol / buy_tokens / buy_supply_pct), post-create buys and sells on their own mint (bought_tokens_after, sold_tokens, sold_sol, first_sell_at / last_sell_at), and live on-chain truth — holdings_tokens / holdings_supply_pct, wallet_empty ("has the dev already exited?"), each with its observation time (holdings_observed_at, activity_rollup_through), and transfer_status: none_detected | suspected | unknown — a balance below what the dev's own trades explain is only ever SUSPECTED (a sell on an uncaptured venue looks identical), and misaligned observations are unknown (transferred_out is its deprecated boolean view). dev is null for mints that pre-date the deploy pipeline (dev_status says why). Score v2 (2026-09-21): a token-SUPPLY burn no longer earns the LP-burn credit (0 → the same 8 points every token without verified LP evidence gets; not a burn penalty) — LP custody is not observed for Solana pools, so lp_burn is warn unless verified; missing required creator information never yields safe, even for well-known tokens; an unknown transfer fee is warn, not "No transfer fee". assessment.status = incomplete lists unknown_inputs and the band is never safe; a failed score-critical lookup is a retryable 503 (risk_inputs_unavailable), never a score. Batch and x402 use the same loader and return identical scores. Unlike a black-box safety check, every point of the score is attributed to a named factor you can inspect and weight yourself. Sent a pool address instead of a mint? A PumpSwap pool is resolved to its mint and answered for the mint, with resolved_from { address, kind: "pool", dex, source } set and `mint` holding the resolved mint. Other non-mint addresses get a named error: 400 wallet_not_mint (points at /wallet/{address}), 400 token_account_not_mint (carries the account's mint + risk_url when readable), 400 pool_not_mint (a Raydium / Meteora / Orca pool — `dex` names it), 404 not_on_chain (no such account on mainnet, checked live), and a real mint we have never seen stays 404 not_tracked. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
{
"mint": "EPjF...Dt1v",
"risk_score": 31,
"band": "caution",
"factors": [
{
"key": "mint_authority",
"label": "Mint authority",
"status": "ok",
"points": 0,
"detail": "Mint authority revoked — supply can't be inflated."
},
{
"key": "freeze_authority",
"label": "Freeze authority",
"status": "danger",
"points": 18,
"detail": "Freeze authority NOT revoked — holders' tokens can be frozen."
},
{
"key": "liquidity",
"label": "Liquidity",
"status": "ok",
"points": 0,
"detail": "Healthy liquidity ($48,200)."
},
{
"key": "liquidity_ratio",
"label": "Liquidity vs market cap",
"status": "warn",
"points": 5,
"detail": "Liquidity is 3.9% of market cap."
},
{
"key": "transfer_fee",
"label": "Transfer fee",
"status": "ok",
"points": 0,
"detail": "No transfer fee."
},
{
"key": "lp_burn",
"label": "LP burn/lock",
"status": "warn",
"points": 8,
"detail": "LP burn/lock status unknown — LP-token custody is not observed for this pool; liquidity may be removable. (A token-supply burn is not LP evidence.)"
},
{
"key": "bundled_open",
"label": "Launch concentration",
"status": "ok",
"points": 0,
"detail": "Launch wasn't heavily bundled."
},
{
"key": "deployer_reputation",
"label": "Deployer track record",
"status": "ok",
"points": 0,
"detail": "Deployer bonds 71% of its tokens."
}
],
"inputs": {
"mint_authority_revoked": true,
"freeze_authority_revoked": false,
"liquidity_usd": 48200,
"liquidity_to_mc_ratio": 0.039,
"transfer_fee_bps": 0,
"is_token_2022": false,
"token_supply_burn_detected": false,
"burn_detected": false,
"lp_burn_status": "unknown",
"launch_cohort_sol": 6.1,
"launch_cohort_size": 12,
"sniper_footprint": {
"buys": 14,
"buyers": 9,
"sol": 12.4,
"supply_pct": 3.8,
"sniper_wallet_buys": 4,
"data_available": true,
"as_of": "2026-07-10T12:00:00Z"
},
"deployer_bonding_rate": 0.71,
"deployer_total_deployed": 17,
"deployer_history_status": "established",
"deployer_reputation_scored": true,
"kol_signal": "accumulating",
"is_blacklisted": false,
"supply_inflation_pct": null
},
"assessment": {
"status": "complete",
"unknown_inputs": [],
"not_assessed": [
"lp_custody"
]
},
"dev_status": "ok",
"dev": {
"wallet": "4qTz...8xNb",
"launchpad": "pumpfun",
"deployed_at": "2026-07-11T09:14:02Z",
"buy_sol": 0.85,
"buy_tokens": 24183920.5,
"buy_supply_pct": 2.42,
"bought_tokens_after": 0,
"sold_tokens": 24183920.5,
"sold_sol": 1.12,
"first_sell_at": "2026-07-11T09:14:09Z",
"last_sell_at": "2026-07-11T09:14:09Z",
"holdings_tokens": 0,
"holdings_supply_pct": 0,
"wallet_empty": true,
"holdings_observed_at": "2026-07-11T09:19:31Z",
"activity_rollup_through": "2026-07-11T09:18:00Z",
"activity_rollup_ran_at": "2026-07-11T09:19:00Z",
"expected_tokens_from_trades": 0,
"transfer_status": "none_detected",
"transfer_reason": "on-chain balance is consistent with the dev's recorded trades",
"transferred_out": false
},
"coverage": {
"history_start": 1775984757,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true,
"data_observed": true,
"eligibility": "eligible",
"eligibility_basis": "persisted_rows",
"completeness": "not_verified"
},
"score_version": "v2",
"as_of": "2026-07-11T09:20:00Z"
}Time-series OHLCV candles for a token mint, aggregated from the trade firehose into fixed buckets. Each candle carries open/high/low/close price, USD volume, trade count, and market cap at the bucket. PRO: OHLCV fields, last 30 days of history. ULTRA: adds per-candle net-flow (buy/sell volume + counts, MEV volume) and liquidity (open/close liquidity, high/low market cap) fields, plus full retained history. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
| tf | string | 1h | Timeframe: one of 1m, 5m, 15m, 1h, 4h, 1d |
| limit | number | 200 | Number of candles to return (1–1500) |
| from | string | optional | ISO8601 timestamp — only candles at or after this time |
| to | string | optional | ISO8601 timestamp — only candles at or before this time |
{
"mint": "EPjF...Dt1v",
"timeframe": "1h",
"from": "2026-06-17T00:00:00Z",
"to": "2026-06-17T02:00:00Z",
"count": 2,
"net_flow_included": true,
"candles": [
{
"t": "2026-06-17T00:00:00Z",
"open": 0.00042,
"high": 0.00051,
"low": 0.0004,
"close": 0.00048,
"volume_usd": 184200,
"trades": 1320,
"market_cap_usd": 480000,
"buy_volume_usd": 102400,
"sell_volume_usd": 81800,
"net_volume_usd": 20600,
"buy_count": 740,
"sell_count": 580,
"volume_mev_usd": 5200,
"open_liquidity_usd": 92000,
"close_liquidity_usd": 98500,
"high_mc_usd": 510000,
"low_mc_usd": 400000
},
{
"t": "2026-06-17T01:00:00Z",
"open": 0.00048,
"high": 0.00049,
"low": 0.00044,
"close": 0.00045,
"volume_usd": 96100,
"trades": 710,
"market_cap_usd": 450000,
"buy_volume_usd": 41300,
"sell_volume_usd": 54800,
"net_volume_usd": -13500,
"buy_count": 305,
"sell_count": 405,
"volume_mev_usd": 2100,
"open_liquidity_usd": 98500,
"close_liquidity_usd": 95300,
"high_mc_usd": 490000,
"low_mc_usd": 440000
}
]
}Per-token trade-flow aggregate computed straight from the trade firehose — the organic-vs-fake-volume read for a single mint. Returns unique wallets / buyers / sellers, buy & sell counts, total trades, buy / sell / net SOL flow, and trades_per_wallet (a wash-trading proxy: a high trade count spread across very few wallets is the classic synthetic-volume tell). Counts and flows only — no holder or balance data. Read the `coverage` block first: `in_scope: false` means we hold no persisted trades for it — `coverage.eligibility` says why (outside the launchpad write-gate, a lapsed capture window, or a brand-new in-scope mint that has not traded yet) — so the zeros mean "not captured", NOT "no activity". PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
| window | string | 1h | Lookback window: 1h or 24h |
{
"mint": "EPjF...Dt1v",
"window": "24h",
"from": "2026-06-20T12:00:00Z",
"coverage": {
"history_start": 1775984757,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true
},
"unique_wallets": 1840,
"unique_buyers": 1620,
"unique_sellers": 910,
"buy_count": 4210,
"sell_count": 2680,
"total_trades": 6890,
"buy_sol": 1284.42,
"sell_sol": 902.17,
"net_sol": 382.25,
"trades_per_wallet": 3.74
}Every captured trade for one token mint, newest first — cursor-paginated raw trades, the backfill complement to the live firehose (seed history, then keep current via the stream). Each trade carries tx_signature, wallet_address, action, sol_amount, token_amount, price_sol/price_usd, early_buyer_rank, slot, and block_time. Default window is full history. The response's coverage block carries the honesty markers: capture starts 2026-04-12 and is pump.fun-pipeline scoped, and `in_scope: false` means no persisted trades (see `coverage.eligibility` for why) — read an empty result as "not captured", NOT "no activity". PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
| limit | integer | 100 | Trades per page (1-500) |
| cursor | string | — | From next_cursor of previous page |
| action | string | — | Filter: "buy" or "sell" |
| wallet | string | — | Filter to one wallet's trades on this token |
| since | integer | full history | Unix epoch seconds lower bound |
| until | integer | now | Unix epoch seconds upper bound |
{
"mint": "EPjF...Dt1v",
"trades": [
{
"tx_signature": "5Hyj...kMnP",
"wallet_address": "7xKv...9mPQ",
"action": "buy",
"sol_amount": 1.42,
"token_amount": 4567890.12,
"price_sol": 3.1e-7,
"price_usd": 0.0000284,
"early_buyer_rank": 7,
"slot": 305418729,
"block_time": 1747746000,
"traded_at": "2026-05-20T12:20:00Z"
}
],
"next_cursor": "MTc0Nzc0NjAwMDoxMjM0NQ==",
"has_more": true,
"filters": {
"action": null,
"wallet": null,
"since": 1775984757,
"until": 1783036800
},
"coverage": {
"history_start": 1775984757,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true
}
}Per-venue liquidity map for a token mint — every DEX pool the token trades in (pump.fun bonding curve, PumpSwap, Raydium, Meteora, Orca), which are live vs parked (is_active), and how fragmented liquidity is across them. This is the venue-level breakdown behind the single aggregate liquidity number on /token/{mint}: use it to see whether liquidity is concentrated in one deep pool or scattered across many thin ones, and which venue is primary. Returns pools[] (pool_address, dex, quote_mint, liquidity_usd, last_price_sol, last_swap_at, amm_id, is_active) plus a summary (pool_count, active_pool_count, dex_count, dexes, total_liquidity_usd, primary_pool, primary_dex, top_pool_share_pct — the primary pool's share of total liquidity, a fragmentation read). PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
{
"mint": "EPjF...Dt1v",
"pools": [
{
"pool_address": "9WzD...aB3k",
"dex": "PumpSwap",
"quote_mint": "So11...1112",
"liquidity_usd": 184200,
"last_price_sol": 4.2e-7,
"last_swap_at": "2026-07-05T11:58:00Z",
"amm_id": "pAMM...4xQ",
"is_active": true
},
{
"pool_address": "3nQd...42aa",
"dex": "Raydium",
"quote_mint": "So11...1112",
"liquidity_usd": 41800,
"last_price_sol": 4.1e-7,
"last_swap_at": "2026-07-05T11:41:00Z",
"amm_id": "675k...RB8",
"is_active": true
},
{
"pool_address": "7xKv...9mPQ",
"dex": "pump.fun",
"quote_mint": "So11...1112",
"liquidity_usd": 0,
"last_price_sol": 3.9e-7,
"last_swap_at": "2026-07-01T08:12:00Z",
"amm_id": null,
"is_active": false
}
],
"summary": {
"pool_count": 3,
"active_pool_count": 2,
"dex_count": 3,
"dexes": [
"PumpSwap",
"Raydium",
"pump.fun"
],
"total_liquidity_usd": 226000,
"primary_pool": "9WzD...aB3k",
"primary_dex": "PumpSwap",
"top_pool_share_pct": 81.5
}
}Per-pool price-impact and slippage for a token mint — "how much SOL does it take to move the price N%", and what each buy size actually costs. For every pool: spot_price_sol, quotes[] per requested SOL size (tokens_out, avg_price_sol, price_impact_pct) and to_move_price (SOL required to move price 1% / 5% / 10%). Exact for constant-product AMMs (PumpSwap, Raydium v4/CPMM, Meteora DAMM — computed from our streamed reserves, zero RPC) and correct for pump.fun / bonk bonding curves via one live read of the curve's virtual reserves (source: "live_rpc"). Honesty markers: impact is per-pool, not router-optimal; concentrated-liquidity pools (Raydium CLMM, Orca, Meteora DLMM) and unclassified pools are listed under unsupported_pools with a reason instead of returning a wrong number. A sharper liquidity-quality read than raw liquidity_usd — the data a bot needs to size entries. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | string (path) | required | Token mint address (base58) |
| sizes | string | 0.5,1,5,10 | Comma-separated SOL buy sizes to quote (max 8, each ≤ 10000) |
{
"mint": "EPjF...Dt1v",
"found": true,
"sol_usd": 178.42,
"sizes_sol": [
0.5,
1,
5
],
"primary_pool": "9WzD...aB3k",
"pools": [
{
"pool_address": "9WzD...aB3k",
"dex": "pumpswap",
"quote_mint": "So11...1112",
"pool_model": "constant_product",
"liquidity_usd": 184200,
"is_active": true,
"depth_available": true,
"model": "constant_product",
"fee_pct": 0.25,
"source": "stream",
"reserves_age_ms": 1840,
"spot_price_sol": 4.2e-7,
"quotes": [
{
"size_sol": 0.5,
"tokens_out": 1180212.4,
"avg_price_sol": 4.24e-7,
"price_impact_pct": 0.19
},
{
"size_sol": 1,
"tokens_out": 2352114.8,
"avg_price_sol": 4.25e-7,
"price_impact_pct": 0.39
},
{
"size_sol": 5,
"tokens_out": 11548020.1,
"avg_price_sol": 4.33e-7,
"price_impact_pct": 1.97
}
],
"to_move_price": {
"1pct": 2.55,
"5pct": 12.61,
"10pct": 24.87
}
}
],
"unsupported_pools": [
{
"pool_address": "5rTe...2wQx",
"dex": "raydium_clmm",
"quote_mint": "So11...1112",
"pool_model": "concentrated",
"liquidity_usd": 22100,
"is_active": true,
"reason": "concentrated_liquidity_depth_not_supported"
}
],
"note": "Impact is per-pool (not router-optimal). Constant-product served from stream reserves; pump.fun/bonk from live virtual reserves."
}The same transparent 0–100 rug-risk / safety score as GET /tokens/{mint}/risk — risk_score, band, auditable factors[], inputs, score_version — for up to 50 mints in one call. Same loader as the single route (score v2, assessment, dev block, coverage); only the live dev-holdings RPC is skipped (dev.holdings_* null, transfer_status unknown). Untracked mints come back as { mint, error: "not_tracked" } instead of a fabricated score; a mint whose score-critical inputs could not be read comes back as { mint, error: "unavailable", retryable: true } — never a partial score (error: "error" if that one mint's computation failed). Counts as one request. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| mints | string[] | required | 1-50 base58 mint addresses (array or comma-separated string) |
{
"tokens": [
{
"mint": "9aK2rFy1...pump",
"risk_score": 41,
"band": "caution",
"factors": [
{
"key": "mint_authority",
"label": "Mint authority",
"status": "ok",
"points": 0,
"detail": "Mint authority revoked — supply can't be inflated."
},
{
"key": "freeze_authority",
"label": "Freeze authority",
"status": "danger",
"points": 18,
"detail": "Freeze authority NOT revoked — holders' tokens can be frozen."
},
{
"key": "liquidity",
"label": "Liquidity",
"status": "danger",
"points": 15,
"detail": "Very thin liquidity ($1,240)."
},
{
"key": "lp_burn",
"label": "LP burn/lock",
"status": "warn",
"points": 8,
"detail": "LP burn/lock status unknown — LP-token custody is not observed for this pool; liquidity may be removable. (A token-supply burn is not LP evidence.)"
}
],
"inputs": {
"mint_authority_revoked": true,
"freeze_authority_revoked": false,
"liquidity_usd": 1240,
"liquidity_to_mc_ratio": null,
"transfer_fee_bps": 0,
"is_token_2022": false,
"token_supply_burn_detected": false,
"burn_detected": false,
"lp_burn_status": "unknown",
"launch_cohort_sol": null,
"launch_cohort_size": null,
"sniper_footprint": null,
"deployer_bonding_rate": null,
"deployer_total_deployed": null,
"deployer_history_status": null,
"deployer_reputation_scored": false,
"kol_signal": "neutral",
"is_blacklisted": false,
"supply_inflation_pct": null
},
"assessment": {
"status": "complete",
"unknown_inputs": [],
"not_assessed": [
"lp_custody",
"deployer_reputation"
]
},
"dev_status": "not_found",
"dev": null,
"score_version": "v2",
"as_of": "2026-09-12T10:00:00Z"
},
{
"mint": "5xQq...9zZ",
"error": "not_tracked"
}
],
"count": 2
}Bulk scoring for up to 50 mints. Shares the same 5-minute LRU cache as the single-mint endpoint — already-warm mints return at near-zero cost. Response includes a `cache_hits` counter so you can see how many were served from cache.
| Name | Type | Default | Description |
|---|---|---|---|
| body | JSON | required | { "mints": ["...", "..."] } — 1 to 50 base58 Solana token mints |
{
"tokens": [
{
"mint": "EPjF...Dt1v",
"score": 74,
"confidence": "medium",
"signal": "positive",
"breakdown": {
"alpha_wallet_count": 6,
"kol_count": 2,
"bundle_buyer_count": 1,
"bot_dominated": false,
"wallets_with_history": 9,
"qualified_win_rate_wallets": 7,
"dump_cluster_count": 0,
"recycled_early_buyer_count": 4
}
},
{
"mint": "So11...1112",
"score": 50,
"confidence": "insufficient_data",
"signal": "neutral",
"breakdown": {
"alpha_wallet_count": 0,
"kol_count": 0,
"bundle_buyer_count": 0,
"bot_dominated": false,
"wallets_with_history": 0,
"qualified_win_rate_wallets": 0,
"dump_cluster_count": 0,
"recycled_early_buyer_count": 0
}
}
],
"count": 2,
"cache_hits": 1
}The list of enrichment signals that carry a live, out-of-sample Signal Scorecard — name, methodology and the /signals/{name}/performance URL for each (dump_cluster_count, runner_rate, recycled_early_buyer_count, coordination_count, scout_first_touch). Any key.
{
"name": "MadeOnSol Signal Scorecard",
"signals": [
{
"name": "dump_cluster_count",
"methodology": "Tokens whose early buyers overlap ≥N dump cohorts vs the base rate of a ≥2× runner…",
"performance_url": "/api/v1/signals/dump_cluster_count/performance"
},
{
"name": "runner_rate",
"performance_url": "/api/v1/signals/runner_rate/performance"
}
]
}Live, out-of-sample backtest for an enrichment signal so bots can weight it programmatically instead of guessing. Returns per-bucket hit rate, base rate, and lift over base. `?history=true` adds the per-day series for drift. Open to any authenticated tier — aggregate, non-sensitive efficacy data.
| Name | Type | Default | Description |
|---|---|---|---|
| name | string | required | Signal name (path): dump_cluster_count, runner_rate, or recycled_early_buyer_count |
| history | boolean | false | Include the per-day snapshot series (up to 90 days) for drift tracking |
{
"signal": "dump_cluster_count",
"metric_type": "count",
"outcome": "dumped",
"window_days": 14,
"base_rate": 0.75,
"test_from": "2026-05-28",
"test_to": "2026-06-11",
"as_of": "2026-06-11T03:50:00Z",
"methodology": "Out-of-sample: rolling train/test split, >=15 early-buyer filter, peak-timing labels (<15min=dump / >=60min=runner), no lookahead.",
"buckets": [
{
"bucket": "0",
"hit_rate": 0.71,
"base_rate": 0.75,
"lift": 0.95,
"sample_n": 4210
},
{
"bucket": ">=8",
"hit_rate": 0.98,
"base_rate": 0.75,
"lift": 1.31,
"sample_n": 190
}
]
}Tools
1,200+ tools indexed across the Solana ecosystem.
Search across 1,200+ Solana & Robinhood Chain tools by name, tagline, or category. Returns ratings, health scores, pricing, and direct links.
| Name | Type | Default | Description |
|---|---|---|---|
| q | string | — | Search query (2-200 characters) |
| category | string | — | Filter by category slug (e.g. trading-bots, dex, analytics) |
| limit | integer | 20 | Max results (1-50) |
{
"tools": [
{
"name": "Jupiter",
"slug": "jupiter",
"tagline": "The best swap aggregator on Solana",
"average_rating": 4.8,
"review_count": 12,
"pricing_model": "free",
"health_score": 100,
"categories": [
"dex"
],
"url": "https://madeonsol.com/tools/jupiter"
}
],
"count": 1
}Comprehensive token data in one call: real-time VWAP price from our DEX trade stream, market cap, 24h volume, **velocity deltas** (`mc_change_pct`, `volume_usd`, `mev_volume_pct` across 5m/15m/1h/2h/4h windows), deployer reputation, KOL smart money flow (including top buyers by SOL volume), `first_seen_at` + `age_seconds` (from our indexer), and `is_blacklisted` + `blacklist_category` (stablecoin / wrapped_sol / lst / rug-flagged). The `mev_volume_pct` field — unique to MadeOnSol — surfaces the % of each window's volume tagged as sandwich/Jito-bundle MEV, so traders can flag wash-pump patterns where headline volume is bot-driven rather than organic demand. All data self-hosted — no external API dependencies. ULTRA adds individual KOL wallet addresses in the top-buyer list. For bulk lookups use POST /token/batch. Freshness + honesty (2026-09-21): `price_source` + `price_observed_at` anchor `price_age_seconds`/`price_is_stale` to the pipeline that actually produced the price (`price_is_stale` is null — unknown — when that source carries no timestamp; `last_trade_at` is the newest trade of either source). `kol_activity` is the complete 7-day aggregate (not a newest-N sample). `deployer` is resolved for unbonded launches too; `deployer_identity` says why it is null (unknown vs lookup_failed) and `history_status` whether its bonding rate is a real track record. `data_status` names any block whose lookup failed — its fields are null (unknown), never a default like 0 KOLs or not-blacklisted. `burn_detected` is a token-SUPPLY burn, not LP evidence. `as_of` is response time, not an observation time.
| Name | Type | Default | Description |
|---|---|---|---|
| mint | path | required | Token mint address (base58) |
{
"token": {
"mint": "CYTUg8qLd45EGbx7MXxSwR5PSMNoUck5SetxxSDHpump",
"price_usd": 0.001024,
"price_sol": 0.0000136,
"market_cap": 1024000,
"volume_24h_usd": 4858,
"volume_24h_sol": 46.17,
"trades_24h": 71,
"last_trade_at": "2026-04-06T14:50:00Z",
"price_source": "mc_tracker",
"price_observed_at": "2026-04-06T14:50:00Z",
"price_age_seconds": 42,
"price_is_stale": false,
"is_token_2022": false,
"transfer_fee_bps": 0,
"burn_detected": false,
"first_seen_at": "2026-04-03T08:12:44Z",
"age_seconds": 276136,
"is_blacklisted": false,
"blacklist_category": null,
"mc_change_pct": {
"5m": 8.2,
"15m": 31.4,
"1h": 62.1,
"2h": 75,
"4h": 87
},
"volume_usd": {
"5m": 42000,
"15m": 118000,
"1h": 340000,
"2h": 500000,
"4h": 920000
},
"mev_volume_pct": {
"5m": 18.4,
"15m": 22.1,
"1h": 27.3,
"2h": 31.8,
"4h": 34.2
},
"history_age_seconds": 14400,
"deployer": {
"wallet": "6vxo...5UhE",
"address": "6vxo...5UhE",
"tier": "rising",
"bonding_rate": 0.5,
"total_deployed": 2,
"total_bonded": 1,
"identity_status": "resolved",
"identity_source": "pending_deploys",
"history_status": "limited_history",
"observed_launch_count": 2,
"resolved_outcome_count": 1
},
"deployer_identity": {
"identity_status": "resolved",
"history_status": "limited_history",
"address": "6vxo...5UhE",
"source": "pending_deploys"
},
"kol_activity": {
"status": "ok",
"buying_kols": 14,
"selling_kols": 18,
"net_flow_sol": -228.52,
"signal": "distributing",
"top_buyers": [
{
"name": "Scharo",
"sol_amount": 65.69
}
],
"window_hours": 168,
"unique_kols": 27,
"buys": 61,
"sells": 88,
"buy_sol": 402.1,
"sell_sol": 630.62,
"counts_basis": "complete_window"
},
"data_status": {
"market": "ok",
"deployer": "ok",
"kol_activity": "ok",
"first_seen": "ok",
"blacklist": "ok",
"metadata": "ok",
"launch_cohort": "ok",
"sniper_footprint": "ok",
"volume_candles": "ok",
"twitter_reuse": "ok"
},
"liquidity_to_mc_ratio": 0.0278,
"launch_cohort_sol": 12.45,
"launch_cohort_size": 18
},
"as_of": "2026-04-06T14:50:42Z"
}Bulk lookup of up to 50 mints in a single request. Returns the same per-mint shape as `GET /token/{mint}` — including the full velocity / volume / MEV-share deltas across 5m/15m/1h/2h/4h windows. DB queries batched with `IN (...)`; dex-stream + RPC supply fan-outs run in parallel — ~10-20× cheaper than N sequential single-mint calls. Ideal for sniper pipelines and dashboards scoring many tokens at once. KOL activity is a complete per-mint aggregate (one busy mint can no longer starve another's sample); a failed lookup block is listed once in top-level `degraded_fields` and its per-token fields are null (unknown), never an empty-state default.
| Name | Type | Default | Description |
|---|---|---|---|
| body | JSON | required | { "mints": ["...", "..."] } — 1 to 50 base58 Solana token mints |
{
"tokens": [
{
"mint": "CYTUg8qLd45EGbx7MXxSwR5PSMNoUck5SetxxSDHpump",
"price_usd": 0.001024,
"market_cap": 1024000,
"age_seconds": 276136,
"is_blacklisted": false,
"mc_change_pct": {
"5m": 8.2,
"1h": 62.1,
"4h": 87
},
"volume_usd": {
"5m": 42000,
"1h": 340000,
"4h": 920000
},
"mev_volume_pct": {
"5m": 18.4,
"1h": 27.3,
"4h": 34.2
},
"price_source": "mc_tracker",
"price_observed_at": "2026-04-06T14:50:00Z",
"price_is_stale": false,
"deployer": {
"tier": "rising",
"bonding_rate": 0.5,
"identity_status": "resolved",
"history_status": "limited_history"
},
"deployer_identity": {
"identity_status": "resolved",
"history_status": "limited_history",
"address": "6vxo...5UhE",
"source": "pending_deploys"
},
"kol_activity": {
"buying_kols": 14,
"signal": "distributing",
"buys": 61,
"sells": 88,
"window_hours": 168,
"complete": true
},
"liquidity_to_mc_ratio": 0.0278,
"launch_cohort_sol": 12.45,
"launch_cohort_size": 18
}
],
"count": 1,
"as_of": "2026-04-06T14:50:42Z"
}Public system health endpoint. Seven core services are monitored: kol_tracker, deployer_listener, nextjs_app, supabase_db, websocket, rhc_node_sync and rhc_trades_freshness (the last two cover Robinhood Chain). Returns per-service status, uptime percentages and stream lag. `slo_target_pct` is our published availability TARGET, not a measurement of the current period — compare it against the `uptime` block, which is the measured figure. No authentication required. Checks run every 5 minutes.
{
"status": "operational",
"services": {
"kol_tracker": {
"status": "healthy",
"last_trade_age_s": 5
},
"deployer_listener": {
"status": "healthy"
},
"nextjs_app": {
"status": "healthy",
"response_ms": 41
},
"supabase_db": {
"status": "healthy",
"response_ms": 3
},
"websocket": {
"status": "healthy"
},
"rhc_node_sync": {
"status": "healthy",
"block_lag": 2
},
"rhc_trades_freshness": {
"status": "healthy",
"last_trade_age_s": 11
}
},
"uptime": {
"last_24h": 99.5,
"last_7d": 99.8
},
"slo_target_pct": 99.5,
"slo_note": "slo_target_pct is a target, not a measurement — the measured figures are in `uptime`.",
"checked_at": "2026-04-06T15:00:00Z"
}Inspect your tier, daily/burst quota state, subscription expiry, and per-feature usage. `features` carries all 12 keys: webhooks, ws_connections, dex_connections, copytrade_wallets, copytrade_wallets_per_rule, copytrade_rules, coordination_rules, first_touch_subscriptions, price_alerts, rhc_copytrade_rules, wallet_tracker_watchlist, rhc_wallet_tracker_watchlist. `copytrade_wallets` counts distinct wallets across all your rules; `copytrade_wallets_per_rule` is the limit the create/PATCH routes enforce on ONE rule (`used` = your largest rule). Robinhood Chain quotas are PER CHAIN — the rhc_* keys are separate budgets that never eat into their Solana counterparts. Reads from the same in-memory counters that drive rate-limit enforcement, so `quota.daily.remaining` is authoritative — no header parsing. Available to every authenticated tier; useful for self-throttling and quota-aware retry logic.
{
"subscriber": "9189666b-0b8f-4606-b39d-a6a52918c587",
"tier": "PRO",
"tier_label": "Pro",
"subscription": {
"status": "active",
"billing_cycle": "monthly",
"current_period_end": "2026-06-12T10:00:00Z",
"started_at": "2026-04-12T10:00:00Z"
},
"quota": {
"daily": {
"limit": 10000,
"used": 3427,
"remaining": 6573,
"resets_at": "2026-05-13T00:00:00Z"
},
"burst": {
"limit": 300,
"used": 14,
"remaining": 286,
"window_seconds": 60
}
},
"features": {
"webhooks": {
"limit": 3,
"used": 1
},
"ws_connections": {
"limit": 2
},
"dex_connections": {
"limit": 0
},
"copytrade_wallets": {
"limit": 5,
"used": 2
},
"copytrade_wallets_per_rule": {
"limit": 5,
"used": 2
},
"copytrade_rules": {
"limit": 3,
"used": 1
},
"coordination_rules": {
"limit": 5,
"used": 1
},
"first_touch_subscriptions": {
"limit": 0,
"used": 0
},
"price_alerts": {
"limit": 5,
"used": 2
},
"rhc_copytrade_rules": {
"limit": 3,
"used": 0
},
"wallet_tracker_watchlist": {
"used": 12
},
"rhc_wallet_tracker_watchlist": {
"used": 4
}
}
}One row per dataset per night: rows written in the previous 24h, min/max event timestamp, a catalogue row estimate, a schema_hash (columns + types) and a fingerprint. Cite the fingerprint in a report or evidence export and it names the exact dataset state the numbers came from — two manifests with a different schema_hash mean the shape of the data changed between them. Also carries `methodology_version` (see /methodology). Default returns the latest manifest per dataset; pass `dataset` to walk one dataset's history. Metadata, not data — available on any valid key. Cached 60s.
| Name | Type | Default | Description |
|---|---|---|---|
| dataset | string | — | Walk one dataset's history instead of the latest-per-dataset default (lowercase, digits, underscore) |
| limit | integer | 30 | History rows when `dataset` is set (1-365) |
{
"manifests": [
{
"dataset": "deployer_tokens",
"data_as_of": "2026-09-06T04:20:02.160582+00:00",
"produced_at": "2026-09-06T04:22:17.9941+00:00",
"ts_column": "deployed_at",
"rows_24h": 635,
"min_ts": "2024-01-25T19:35:33.993+00:00",
"max_ts": "2026-09-06T04:15:35.976+00:00",
"row_count_estimate": 73607,
"schema_hash": "c71901159ac11f94dd12aa42d17db70a",
"fingerprint": "52ec41a1f79f3c231ef8f66fe73f3f72"
}
],
"count": 1,
"dataset": null,
"methodology_version": "2026.08.28-2",
"note": "rows_24h/min_ts/max_ts are measured on the streaming replica at data_as_of; row_count_estimate is the planner estimate. Produced nightly ~06:10 Europe/Berlin."
}Filtered, sortable token directory. Complements `/token/{mint}` (single lookup) and the KOL-scoped `/kol/tokens/{hot,trending}`. Server-side filters: `min_mc`, `max_mc`, `min_liq` (**default 2000** — set 0 to disable phantom-MC dust floor), `active_h`, `primary_dex` (pumpfun/pumpswap/raydium/meteora/orca/raydium_clmm), `authority_revoked`, `exclude_token2022`, `lp_burned` (verified LP evidence only — matches nothing until an LP-custody writer exists), `supply_burn` (token-supply burn). `deployer_tier` / `launchpad` are served by a bounded scan: `pagination.scan_truncated` + `next_offset` say where to continue, and `has_more` is false only when the candidates ran out. Offset pages walk a live order (`order_is_live`). Computed post-filters: `min_volume_1h_usd`, `max_mev_share_pct`, `mc_change_1h_min_pct`, `mc_change_1h_max_pct`. **Trending / momentum sorts** (PRO/ULTRA): `mc_change_5m_desc`, `mc_change_1h_desc`, `volume_1h_desc`, and `trending` (recent volume × momentum) turn the directory into a reputation-aware momentum board — combine with `deployer_tier` and `max_mev_share_pct` for clean-volume movers from trusted deployers. Full sort set: `mc_desc` | `mc_asc` | `last_trade_desc` | `liquidity_desc` | `cumulative_volume_desc` | `mc_change_5m_desc` | `mc_change_1h_desc` | `volume_1h_desc` | `trending`. Pagination includes `post_filtered: true` so clients can detect when computed filters trim a page below `limit`.
| Name | Type | Default | Description |
|---|---|---|---|
| min_mc | query | — | Minimum market_cap_usd |
| max_mc | query | — | Maximum market_cap_usd |
| min_liq | query | 2000 | Minimum liquidity_usd. Set 0 to disable the default dust floor. |
| active_h | query | — | Only tokens traded within the last N hours (0.1–168) |
| primary_dex | query | — | pumpfun | pumpswap | raydium | meteora | orca | raydium_clmm |
| authority_revoked | query | — | true = both mint + freeze revoked; false = at least one not revoked |
| exclude_token2022 | query | false | Drop Token-2022 mints (common transfer-fee scam vector) |
| lp_burned | query | — | DEPRECATED. VERIFIED LP evidence only: true = lp_burnt_pct ≥ 99, false = measured below it; unknown LP custody matches neither, so both match no token today. Never a token-supply burn (use supply_burn). Sending it adds a deprecations entry to the response |
| supply_burn | query | — | Token-SUPPLY burn flag (the mint's supply decreased) — what lp_burned used to filter on; not LP evidence |
| min_volume_1h_usd | query | — | Computed organic-volume floor (post-filter) |
| max_mev_share_pct | query | — | Computed MEV/bot volume ceiling % (post-filter) |
| mc_change_1h_min_pct | query | — | Computed minimum 1h MC change % (post-filter; momentum scans) |
| mc_change_1h_max_pct | query | — | Computed maximum 1h MC change % (post-filter; dip scans) |
| min_liq_mc_ratio | query | — | Minimum liquidity-to-MC ratio (liquidity_usd / market_cap_usd) |
| max_liq_mc_ratio | query | — | Maximum liquidity-to-MC ratio (liquidity_usd / market_cap_usd) |
| deployer_tier | query | — | Filter by deployer tier: elite | good | moderate | rising | cold | unranked |
| sort | query | mc_desc | mc_desc | mc_asc | last_trade_desc | liquidity_desc | cumulative_volume_desc | mc_change_5m_desc | mc_change_1h_desc | volume_1h_desc | trending (last four are momentum sorts, PRO/ULTRA) |
| limit | query | 50 | 1–100 |
| offset | query | 0 | 0–10000 |
{
"tokens": [
{
"mint": "CYTUg8qLd45EGbx7MXxSwR5PSMNoUck5SetxxSDHpump",
"symbol": "ALPHA",
"name": "Alpha Coin",
"price_usd": 0.001024,
"market_cap_usd": 1024000,
"fdv_usd": 1024000,
"liquidity_usd": 28430,
"primary_dex": "raydium",
"authorities_revoked": true,
"lp_burned": null,
"lp_burn_status": "unknown",
"lp_burnt_pct": null,
"token_supply_burn_detected": false,
"is_token_2022": false,
"last_trade_time": "2026-05-12T17:15:05Z",
"mc_change_5m_pct": 4.2,
"mc_change_1h_pct": 31.8,
"organic_volume_1h_usd": 12480,
"mev_share_pct": 18.4,
"liquidity_to_mc_ratio": 0.0278,
"deployer_tier": "elite"
}
],
"pagination": {
"limit": 50,
"offset": 0,
"returned": 50,
"has_more": true,
"order_is_live": true,
"next_offset": 50
},
"filters": {
"min_liq": 2000,
"min_liq_default_applied": true,
"sort": "mc_desc",
"lp_burned": null,
"lp_burned_source": "lp_burnt_pct >= 99 (verified LP evidence only)",
"supply_burn": null
}
}Pre-bond pump.fun tokens approaching graduation — catch them *before* they bond. Each token carries its live bonding-curve `progress_pct` (from on-chain `real_token_reserves` depletion) and, uniquely, its **velocity** (`velocity_pct_per_min` = Δprogress/min) plus a linear `eta_minutes` projection — so "95% and accelerating" ranks above "92% stalled". Enriched with deployer reputation (`deployer_tier`) from our deployer-hunter graph, so you can filter to elite/good deployers only. Defaults to the 80–99.99% band, already-bonded tokens excluded. Self-hosted from our firehose — no external API.
| Name | Type | Default | Description |
|---|---|---|---|
| min_progress | query | 80 | Minimum bonding-curve progress % (0–100) |
| max_progress | query | 99.99 | Maximum bonding-curve progress % (< 100; bonded tokens always excluded) |
| min_velocity_pct_per_min | query | — | Minimum Δprogress/min — drops tokens without a 5m snapshot when set |
| max_age_minutes | query | — | Only tokens deployed within the last N minutes (≥1) |
| deployer_tier | query | — | elite | good | moderate | rising | cold | unranked |
| authority_revoked | query | — | true = both mint + freeze revoked |
| min_liq | query | — | Minimum liquidity_usd |
| sort | query | velocity_desc | velocity_desc | progress_desc | eta_asc |
| limit | query | 50 | 1–100 |
{
"tokens": [
{
"mint": "8xKv9aK2rFy1mPQ...pump",
"symbol": "MOON",
"name": "Moon Soon",
"progress_pct": 96.4,
"velocity_pct_per_min": 1.82,
"eta_minutes": 2,
"stalled": false,
"real_sol_reserves": 78.3,
"market_cap_usd": 61200,
"liquidity_usd": 24100,
"authorities_revoked": true,
"deployer_tier": "elite",
"age_minutes": 14
}
],
"filters": {
"min_progress": 80,
"max_progress": 99.99,
"deployer_tier": null,
"sort": "velocity_desc"
},
"returned": 1,
"note": "Pre-bond pump.fun tokens. velocity = Δprogress/min (null until a 5m snapshot exists). eta_minutes is a linear projection from current velocity."
}Wallet Tracker
PRO/ULTRA only — 50 / 100 / 500 wallets (Pro / Ultra / Business). Events stored 120 days. ULTRA gets the WS push channel. Robinhood Chain has its own watchlist at /rhc/wallet-tracker/* with a separate per-chain quota.
List all tracked wallets with labels, added_at timestamps, and remaining capacity. PRO: 50 wallets. ULTRA: 100. BUSINESS: 500. PRO/ULTRA only — BASIC keys receive 403 on every /wallet-tracker/* route.
{
"wallets": [
{
"wallet_address": "7xKX...3bPq",
"label": "whale #1",
"added_at": "2026-04-15T10:00:00Z"
}
],
"count": 1,
"limit": 50,
"remaining": 49
}Add a Solana wallet to your watchlist. Returns HTTP 409 if the wallet is already tracked or the tier limit is reached. PRO: 50, ULTRA: 100, BUSINESS: 500. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet_address | string | required | Solana wallet address (base58) |
| label | string | null | Optional human-readable label |
{
"wallet_address": "7xKX...3bPq",
"label": "whale #1",
"added_at": "2026-04-15T10:00:00Z",
"remaining": 9
}Remove a wallet from your watchlist. Returns 404 if not found. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Solana wallet address (base58) |
{
"success": true
}Update the label for a tracked wallet. Pass null to clear the label. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Solana wallet address (base58) |
| label | string | null | required | New label or null to clear |
{
"wallet_address": "7xKX...3bPq",
"label": "renamed whale",
"added_at": "2026-04-15T10:00:00Z"
}Historical swap and transfer events across all your watched wallets. Swaps = token trades (action buy or sell; no counterparty key). Transfers = SOL moved without a token change (action null; counterparty = the wallet on the other side when it could be matched, key omitted otherwise). block_time is our ingest clock, not chain time: order by slot for chain order (the default when no before cursor is sent). replayed = true when the row was recovered by a gap replay rather than delivered live. Retention: 120 days. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string | — | Filter to a specific wallet address |
| action | string | — | Filter swaps by action: buy or sell (transfers have action null) |
| event_type | string | — | Filter by event type: swap or transfer |
| limit | integer | 50 | Max results (1–200) |
| order | string | slot | slot (on-chain order) or block_time (ingest order). Defaults to slot, or to block_time when a before cursor is sent; an explicit value always wins |
| before_slot | integer | — | Cursor for order=slot: pass next_cursor_slot from the previous page |
| before | integer | — | Legacy cursor for order=block_time: pass next_cursor from the previous page |
{
"events": [
{
"wallet_address": "7xKX...3bPq",
"label": "whale #1",
"event_type": "swap",
"action": "buy",
"token_mint": "EPjF...fEgN",
"token_symbol": "EXAMPLE",
"token_name": "Example Token",
"sol_amount": 2.5,
"token_amount": 1000000,
"tx_signature": "5Hyj...kMnP",
"block_time": 1744720800,
"slot": 449476673,
"replayed": false,
"ingested_at": "2026-04-15T10:00:00.000Z",
"timestamp": "2026-04-15T10:00:00.000Z"
},
{
"wallet_address": "7xKX...3bPq",
"label": "whale #1",
"event_type": "transfer",
"action": null,
"token_mint": null,
"token_symbol": null,
"token_name": null,
"sol_amount": 12,
"token_amount": null,
"counterparty": "9WzD...AWWM",
"tx_signature": "3kPq...Zx7d",
"block_time": 1744720740,
"slot": 449476520,
"replayed": false,
"ingested_at": "2026-04-15T09:59:00.000Z",
"timestamp": "2026-04-15T09:59:00.000Z"
}
],
"count": 2,
"ordered_by": "slot",
"next_cursor": null,
"next_cursor_slot": null
}Per-wallet stats over a time window: swap counts, buys, sells, SOL bought/sold, last activity time. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 7d | Time window: 24h, 7d, or 30d |
| wallet | string | — | Filter to a specific wallet address |
{
"wallets": [
{
"wallet_address": "7xKX...3bPq",
"label": "whale #1",
"swap_count": 24,
"buys": 18,
"sells": 6,
"sol_bought": 45.2,
"sol_sold": 38.7,
"last_event_at": "2026-04-15T09:45:00Z"
}
],
"period": "7d"
}Wallet
Stats, FIFO PnL with daily curve, open positions with live unrealized, and paginated raw trades — for any Solana wallet, not just curated KOLs.
Aggregate counters for any Solana wallet over the last 90 days (trade count, buys/sells split, SOL in/out, unique tokens) plus cross-product flags from kol_wallets, mv_alpha_wallets, and deployers. The flags block carries reputation classification: is_sniper (≥5 tokens early-bought with ≥80% flipped <5min, behavior-updated hourly), is_bundler (bought >1 token in the same block, lifetime flag), is_dumper (≥5 dump cohorts + 0 runners in a rolling 42d window) with the raw dump_cluster cohort counts, and bot_confidence (string enum: none/low/medium/high). Reputation flags are pump.fun-pipeline scoped — false means not observed, not verified clean. v1.8.1 adds four enrichment fields: top traded tokens with realized PnL + peak MC, trading-style signals (sniper rate, round-trip rate, median hold), deployer-tier distribution, and a recent-trades timeline. Sub-100ms even on heavy wallets. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Base58 wallet address |
{
"address": "ASVz...ybJk",
"stats": {
"first_seen": "2026-04-12T14:18:33Z",
"last_seen": "2026-05-20T18:27:34Z",
"total_trades": 1123,
"buys": 564,
"sells": 559,
"bought_sol": 1494.39,
"sold_sol": 1562.55,
"unique_tokens": 324,
"window_days": 90
},
"flags": {
"is_kol": true,
"kol_name": "Reljoo",
"is_alpha_tracked": true,
"bot_confidence": "low",
"alpha_win_rate": 0.43,
"alpha_net_pnl_sol": 68.2,
"alpha_tokens_traded": 324,
"is_deployer": false,
"deployer_tokens_deployed": null,
"deployer_bonding_rate": null,
"is_sniper": false,
"is_bundler": false,
"is_dumper": false,
"dump_cluster": {
"dump_cohorts": 2,
"runner_cohorts": 3,
"total_cohorts": 5,
"as_of": "2026-07-10T04:00:00Z"
}
},
"top_tokens": [
{
"token_mint": "C7Py...pump",
"token_symbol": "ALVEUS",
"buys": 12,
"sells": 9,
"sol_in": 18.47,
"sol_out": 22.13,
"realized_pnl_sol": 3.66,
"current_mc_usd": 124000,
"peak_mc_usd": 380000,
"last_traded_at": "2026-05-19T08:42:11Z"
}
],
"trading_style": {
"total_trades": 1123,
"avg_trade_size_sol": 2.72,
"sniper_rate": 0.18,
"early_entries": 202,
"round_trip_rate": 0.84,
"tokens_with_round_trips": 272,
"median_hold_minutes": 47,
"dominant_action": "balanced"
},
"deployer_breakdown": {
"total_tokens": 89,
"tracked_deployers": 4,
"by_tier": [
{
"tier": "elite",
"count": 38
},
{
"tier": "good",
"count": 27
},
{
"tier": "rising",
"count": 14
},
{
"tier": "cold",
"count": 10
}
]
},
"recent_trades": [
{
"token_mint": "ASVz...pump",
"token_symbol": "PEPE2",
"action": "buy",
"sol_amount": 1.42,
"block_time": 1747900800,
"traded_at": "2026-05-22T12:00:00Z",
"tx_signature": "5Tx...abc"
}
]
}What did we know about this wallet on a given date — as a first-class answer, not a reconstruction. For every flag source (deployer, alpha, dump_cluster, kol, sniper, bundler), returns the latest write-on-change snapshot with snapshot_at ≤ as_of, i.e. the state that was actually current at that moment. A source with `carried: true` was recorded before as_of and hadn't changed by then; a source that is `null` means no snapshot existed yet — we knew nothing about this wallet on that source at that time. Without `as_of`, returns the latest state (same values as the flags block on GET /wallet/{address}). Snapshots exist from 2026-08-28 (daily 01:20 UTC); deployer tier trajectory before that lives in /deployer-hunter/{wallet}/history. `history=true` adds the full change series for that wallet (newest first, ≤200 rows) for audit trails. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Base58 wallet address |
| as_of | ISO datetime | now | Must include a UTC offset, e.g. 2026-07-15T00:00:00Z. Returns the state as of this moment. |
| history | boolean | false | Include the full change series (≤200 rows, newest first) |
{
"wallet": "CyaE1VxvBrahnPWkqm5VsdCvyS2QmNht2UFrKJHga54o",
"as_of": "2026-09-06T07:15:18.203Z",
"flagged": [
"deployer",
"dump_cluster",
"kol",
"sniper",
"bundler"
],
"sources": {
"deployer": {
"tier": "cold",
"bonding_rate": 0.0989,
"total_bonded": 65,
"labeled_tokens": 59,
"total_tokens_deployed": 657,
"snapshot_at": "2026-08-28T18:48:36.884407+00:00",
"active": true,
"carried": true
},
"alpha": null,
"dump_cluster": {
"member": true,
"dump_cohorts": 234,
"total_cohorts": 263,
"runner_cohorts": 29,
"snapshot_at": "2026-09-06T01:20:00.008966+00:00",
"active": true,
"carried": true
},
"kol": {
"name": "Cented",
"member": true,
"is_active": true,
"strategy_tag": "scalper",
"snapshot_at": "2026-08-28T18:48:36.884407+00:00",
"active": true,
"carried": true
},
"sniper": {
"member": true,
"snapshot_at": "2026-08-28T18:48:36.884407+00:00",
"active": true,
"carried": true
},
"bundler": {
"member": true,
"snapshot_at": "2026-08-28T18:48:36.884407+00:00",
"active": true,
"carried": true
}
},
"note": "Write-on-change snapshots since 2026-08-28 (daily 01:20 UTC). carried=true means the state was recorded before as_of and had not changed by then. null = no snapshot at or before as_of for that source."
}The recent trade tape for up to 50 wallets in ONE call — the poll-loop replacement for anyone tracking a wallet set over REST. Per wallet: newest trades since a unix-second cursor (default and floor: 90 days back), each with tx_signature, token_mint, action, sol/token amounts, price fields and traded_at. next_since in the response is the newest block_time seen — pass it back as since on the next poll to get only new trades. Counts as one request. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| wallets | string[] | required | 1-50 base58 wallet addresses (array or comma-separated string) |
| since | integer | now − 90d | Unix seconds — only trades after this (floored at 90 days back) |
| limit_per_wallet | integer | 20 | Trades per wallet (1-100) |
| action | string | — | "buy" or "sell" |
{
"wallets": [
{
"wallet": "7xKX...3bPq",
"count": 2,
"trades": [
{
"tx_signature": "5K7j...",
"token_mint": "9aK2rFy1...pump",
"action": "buy",
"sol_amount": 1.5,
"token_amount": 4200000,
"price_sol": 3.57e-7,
"block_time": 1755507662,
"traded_at": "2026-08-18T09:41:02Z"
}
]
}
],
"since": 1747731662,
"next_since": 1755507662,
"limit_per_wallet": 20,
"coverage": {
"history_start_days": 90,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)"
}
}Bulk reputation flags for up to 100 wallet addresses in one call — the batch counterpart to the flags block on GET /wallet/{address} (values are identical). Per wallet: is_sniper, is_bundler, is_dumper, is_kol + kol_name, bot_confidence (string enum: none/low/medium/high), and the raw dump_cluster cohort counts (null unless the wallet has recent cohort appearances). Flags are pump.fun-pipeline scoped — false means not observed, not verified clean. Pure index lookups: ~10ms per 100-address batch. Counts as 1 request against quota. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| wallets | string[] | string | required | 1-100 base58 wallet addresses — JSON array or comma-separated string |
{
"wallets": [
{
"address": "7xKv...9mPQ",
"is_sniper": true,
"is_bundler": false,
"is_dumper": false,
"is_kol": true,
"kol_name": "Cented",
"bot_confidence": "low",
"dump_cluster": {
"dump_cohorts": 2,
"runner_cohorts": 3,
"total_cohorts": 5,
"as_of": "2026-07-10T04:00:00Z"
}
},
{
"address": "3nQd...42aa",
"is_sniper": false,
"is_bundler": true,
"is_dumper": true,
"is_kol": false,
"kol_name": null,
"bot_confidence": "high",
"dump_cluster": {
"dump_cohorts": 6,
"runner_cohorts": 0,
"total_cohorts": 6,
"as_of": "2026-07-10T04:00:00Z"
}
}
],
"count": 2,
"as_of": "2026-07-10T12:00:00Z"
}Full per-wallet PnL: realized + unrealized SOL, profit factor, max drawdown, avg + median hold minutes, daily UTC PnL curve, closed positions sorted by pnl desc (with ROI %, hold minutes, win/loss), and open positions hydrated with current prices from market-cap tracker. Cached in wallet_analyses with dynamic TTL (5min / 1h / 24h based on activity). Cache hits don't count against your daily quota. Check `coverage.in_scope` before trusting a flat PnL: `false` means we hold no persisted trades for this wallet, so the numbers mean "not captured", NOT "no activity". Every cache hit is validated against the wallet's trade head — a wallet that traded after the cached analysis is recomputed (`cache_invalidated: new_activity`); `cache_age_seconds` + `cache_validation` report the source age. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Base58 wallet address |
{
"address": "ASVz...ybJk",
"window_days": 90,
"coverage": {
"history_start": 1775984757,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true
},
"summary": {
"realized_sol": 211.68,
"unrealized_sol": -197.22,
"total_pnl_sol": 14.46,
"wins": 12,
"losses": 8,
"win_rate": 0.6,
"profit_factor": 2.35,
"avg_hold_minutes": 47,
"median_hold_minutes": 22,
"max_drawdown_sol": 4.81,
"open_positions_count": 27,
"closed_positions_count": 268,
"total_tokens_traded": 324
},
"pnl_curve": [
{
"date": "2026-04-12",
"day_pnl": 1.5,
"cumulative_pnl": 1.5,
"trades": 3
},
{
"date": "2026-04-13",
"day_pnl": -0.3,
"cumulative_pnl": 1.2,
"trades": 2
}
],
"closed_positions": [
{
"token_mint": "81QY...pump",
"buy_count": 3,
"sell_count": 2,
"bought_sol": 2.5,
"sold_sol": 7.7,
"pnl_sol": 5.2,
"roi_pct": 208,
"hold_minutes": 47,
"result": "win"
}
],
"open_positions": [
{
"token_mint": "5KSo...bonk",
"token_amount": 1000,
"cost_basis_sol": 1.5,
"avg_entry_price_sol": 0.0015,
"current_price_sol": 0.002,
"current_value_sol": 2,
"unrealized_sol": 0.5,
"unrealized_pct": 33.3
}
],
"notes": {
"cost_basis_observable_from": "2026-04-12T00:00:00Z"
},
"cache_hit": false,
"ttl_seconds": 300
}Open lots only — same data as /pnl's open_positions[] but without summary or curve. Shares the wallet_analyses cache: if you recently called /pnl for this wallet, this is an immediate cache hit. Mints without a current price in market-cap tracker return unrealized_sol: null rather than fabricated zero. An empty positions[] with `coverage.in_scope: false` means we hold no persisted trades for the wallet — "not captured", NOT "no activity". Cache hits are validated against the trade head (`cache_validation`, `cache_age_seconds`). PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Base58 wallet address |
{
"address": "ASVz...ybJk",
"coverage": {
"history_start": 1775984757,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true
},
"positions": [
{
"token_mint": "5KSo...bonk",
"token_amount": 1000,
"cost_basis_sol": 1.5,
"avg_entry_price_sol": 0.0015,
"current_price_sol": 0.002,
"current_value_sol": 2,
"unrealized_sol": 0.5,
"unrealized_pct": 33.3,
"first_buy_at": "2026-05-01T10:00:00Z",
"buys_in_position": 2
}
],
"cache_hit": true,
"computed_at": "2026-05-20T18:25:42Z"
}Current on-chain token balances, read live from the wallet's SPL + Token-2022 accounts (getTokenAccountsByOwner) plus native SOL — not trade-derived like /positions, so transfers, airdrops and CEX withdrawals are all reflected. Each holding is enriched from our data with price/market-cap, name/symbol, and transfer_delta: the on-chain amount minus the 90-day trade-derived net, which surfaces tokens that arrived or left WITHOUT a swap (airdrops, insider funding, wallet-hopping). Sorted by USD value (unpriced last). 90s cache; 6 uncached lookups/min (cache hits don't count). ULTRA.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Base58 wallet address |
| limit | integer | 200 | 1-500 holdings returned |
| min_value_usd | number | 0 | Hide holdings below this USD value |
{
"address": "ASVz...ybJk",
"sol_balance": 12.45,
"holdings": [
{
"mint": "5KSo...bonk",
"symbol": "BONK",
"name": "Bonk",
"amount": 1500000,
"amount_raw": "1500000000000",
"decimals": 5,
"token_program": "spl",
"price_usd": 0.0000132,
"value_usd": 19.8,
"market_cap_usd": 890000000,
"is_bonded": true,
"trade_derived_amount": 1200000,
"transfer_delta": 300000
}
],
"summary": {
"token_accounts": 42,
"non_zero": 14,
"returned": 14,
"priced": 13,
"total_value_usd": 1082.89,
"truncated": false
},
"verified_at": "2026-07-05T18:25:42Z",
"trade_window_days": 90,
"cache_hit": false,
"ttl_seconds": 90
}Cursor-paginated raw trades from token_trades. Cursor encodes (block_time, id) for stable DESC ordering across pages. Filters: action (buy/sell), token_mint, since/until Unix timestamps (default 90-day window), limit 1-500 (default 100). The `coverage` block disambiguates an empty page: `in_scope: false` means the wallet is outside our launchpad write-gate, so no rows means "not covered by our capture", NOT "no activity". PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | path | required | Base58 wallet address |
| limit | integer | 100 | 1-500 |
| cursor | string | — | From next_cursor of previous page |
| action | string | — | buy or sell |
| token_mint | string | — | Filter to one token |
| since | integer | now-90d | Unix epoch seconds |
| until | integer | now | Unix epoch seconds |
{
"address": "ASVz...ybJk",
"trades": [
{
"tx_signature": "5Hyj...kMnP",
"token_mint": "81QY...pump",
"action": "buy",
"sol_amount": 1.002,
"token_amount": 278537,
"block_time": 1747746000,
"traded_at": "2026-05-20T12:20:00Z"
}
],
"next_cursor": "MTc0Nzc0NjAwMDoxMjM0NQ==",
"has_more": true,
"filters": {
"action": null,
"token_mint": null,
"since": 1739970000,
"until": 1747746000
},
"coverage": {
"history_start": 1775984757,
"scope": "pump.fun pipeline (pump.fun, LaunchLab/bonk, bags)",
"in_scope": true
}
}Copy-Trade
PRO: 3 rules × 5 wallets. ULTRA: 20 × 50. Signals fire to webhook / WS in ~1–1.5 s. Robinhood Chain has its own rules at /rhc/copytrade/* with a separate per-chain quota.
Register a copy-trade rule targeting one or more tracked KOL wallets (rules fire only for wallets in the KOL tracker; for any other Solana wallet, use the Wallet Tracker). When a matching trade hits our gRPC pipeline, a signal fires to your webhook and/or WebSocket in ~1–1.5s. Returns the HMAC secret once — save it for signature verification. PRO: 3 rules × 5 source wallets each. ULTRA: 20 rules × 50 wallets each. BUSINESS: 100 rules × 250 wallets each. Your own per-rule limit is `features.copytrade_wallets_per_rule.limit` on GET /me.
| Name | Type | Default | Description |
|---|---|---|---|
| source_wallets | string[] | required | Solana wallets to copy (1 to your tier's per-rule limit: PRO 5, ULTRA 50, BUSINESS 250 base58 addresses) |
| sizing_amount | number | required | Size of the suggested copy trade (fixed SOL or multiplier, depending on sizing_mode) |
| delivery_mode | string | webhook | "webhook", "websocket", or "both" |
| webhook_url | string | — | HTTPS URL — required when delivery_mode includes webhook |
| name | string | — | Human-readable label (≤64 chars) |
| min_trade_sol | number | 0 | Only fire on source trades ≥ this SOL size |
| only_action | string | buy | "buy", "sell", or "both" |
| sizing_mode | string | fixed | "fixed" (SOL amount), "proportional" (multiplier × source size), or "percent_source" |
| min_mc_usd | number | null | — | Only fire when the source trade's market cap ≥ this (USD, 0–1e12). Trades with unknown MC are dropped when set. |
| max_mc_usd | number | null | — | Only fire when the source trade's market cap ≤ this (USD, 0–1e12); must be ≥ min_mc_usd |
{
"subscription": {
"id": 12,
"name": "Cented buys only",
"source_wallets": [
"CyaE1Vxv...ga54o"
],
"min_trade_sol": 0.5,
"only_action": "buy",
"sizing_mode": "fixed",
"sizing_amount": 0.25,
"delivery_mode": "both",
"webhook_url": "https://your-app.com/copytrade/hook",
"is_active": true,
"created_at": "2026-04-18T14:00:00Z"
},
"webhook_secret": "a1b2c3...save-me",
"note": "Save the webhook_secret — it will not be shown again. Use it to verify HMAC-SHA256 signatures on incoming webhook payloads."
}List all your active and inactive copy-trade rules. Webhook secret is never returned here — only at creation time.
{
"subscriptions": [
{
"id": 12,
"name": "Cented buys only",
"source_wallets": [
"CyaE1Vxv...ga54o"
],
"min_trade_sol": 0.5,
"only_action": "buy",
"sizing_mode": "fixed",
"sizing_amount": 0.25,
"delivery_mode": "both",
"webhook_url": "https://your-app.com/copytrade/hook",
"is_active": true,
"created_at": "2026-04-18T14:00:00Z"
}
]
}Fetch a single rule by ID. Returns 404 if the rule doesn't belong to your account.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Subscription ID |
{
"subscription": {
"id": 12,
"name": "Cented buys only",
"source_wallets": [
"CyaE1Vxv...ga54o"
],
"only_action": "buy",
"sizing_mode": "fixed",
"sizing_amount": 0.25,
"delivery_mode": "both",
"webhook_url": "https://your-app.com/copytrade/hook",
"is_active": true
}
}Update any rule field. Pass only the fields you want to change. Tier limits on source_wallets still apply.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Subscription ID |
| source_wallets | string[] | — | Replace tracked wallets |
| min_trade_sol | number | — | New SOL threshold |
| only_action | string | — | buy / sell / both |
| sizing_mode | string | — | fixed / proportional / percent_source |
| sizing_amount | number | — | New size or multiplier |
| delivery_mode | string | — | webhook / websocket / both |
| webhook_url | string | null | — | New HTTPS URL or null to clear |
| is_active | boolean | — | Pause (false) or resume (true) |
{
"subscription": {
"id": 12,
"min_trade_sol": 1,
"is_active": true,
"updated_at": "2026-04-18T15:00:00Z"
}
}Permanently delete a rule and its signal history. Returns 404 if not found.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Subscription ID |
{
"deleted": true
}Last 7 days of signals fired by your rules. Each signal carries the source token's MC at trade time AND the current velocity context (`mc_change_pct`, `volume_usd`, `mev_volume_pct`) so you can see at a glance whether the signal token is still pumping or already faded — the difference between 'good entry' and 'chasing the top'. Useful for backfilling missed webhook deliveries or for audit. Always scoped to your account.
| Name | Type | Default | Description |
|---|---|---|---|
| subscription_id | integer | — | Filter to a single rule |
| since | ISO 8601 | — | Return signals fired at or after this time |
| limit | integer | 50 | Max signals (1–500) |
{
"signals": [
{
"id": 4521,
"subscription_id": 12,
"fired_at": "2026-04-18T14:12:03.142Z",
"source_wallet": "CyaE1Vxv...ga54o",
"action": "buy",
"token_mint": "tca3tbFf...pump",
"token_symbol": "Hands Off",
"source_sol_amount": 5.27,
"suggested_sol_amount": 0.25,
"tx_signature": "4fu8vvsF...v1sb",
"market_cap_usd_at_trade": 28430,
"market_cap_usd": 423000,
"mc_change_pct": {
"5m": 8.2,
"15m": 31.4,
"1h": 62.1,
"2h": 75,
"4h": 87
},
"volume_usd": {
"5m": 42000,
"15m": 118000,
"1h": 340000,
"2h": 500000,
"4h": 920000
},
"mev_volume_pct": {
"5m": 18.4,
"1h": 27.3,
"4h": 34.2
},
"delivered": true,
"delivered_at": "2026-04-18T14:12:04.019Z"
}
]
}Price Alerts
Evaluated on every 250 ms tick of the in-memory mc-tracker price feed. PRO: 5 alerts. ULTRA: 25. Robinhood Chain has its own alerts at /rhc/price-alerts/* with a separate per-chain quota.
List all your token MC dip/recovery alert rules — active and inactive. PRO: 5 alerts max. ULTRA: 25 alerts max. Evaluated on every 250 ms tick of the in-memory mc-tracker price feed.
{
"alerts": [
{
"id": 4127,
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"name": "PEPE2 dip watch",
"drop_pct": 20,
"recovery_pct": 15,
"delivery_mode": "webhook",
"webhook_url": "https://my.bot/hooks/dip",
"is_active": true,
"created_at": "2026-05-25T08:00:00Z",
"last_fired_at": "2026-05-25T12:42:11Z"
}
],
"count": 1
}Create a token MC dip/recovery alert. When the token's market cap drops by `drop_pct` from its recent high, the alert fires via your chosen delivery mode. Optional `recovery_pct` triggers a follow-up when MC recovers from the dip low. Returns `webhook_secret` once if webhook delivery is configured. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| token_mint | string | required | Solana token mint address (base58) |
| drop_pct | number | required | MC drop threshold (0.01-99.99). E.g. 20 = fire when MC drops 20% from recent high |
| recovery_pct | number | — | Optional recovery threshold — fires when MC recovers this % from the dip low |
| name | string | — | Optional human-readable label (max 64 chars) |
| delivery_mode | string | websocket | "websocket", "webhook", or "both" |
| webhook_url | string | — | HTTPS URL — required when delivery_mode includes webhook |
{
"alert": {
"id": 4127,
"token_mint": "9aK2rFy1...pump",
"name": "PEPE2 dip watch",
"drop_pct": 20,
"recovery_pct": 15,
"delivery_mode": "webhook",
"webhook_url": "https://my.bot/hooks/dip",
"is_active": true,
"created_at": "2026-05-25T08:00:00Z"
},
"webhook_secret": "<32-byte hex -- shown once>",
"note": "Save the webhook_secret -- it will not be shown again."
}Fetch a single price alert rule by ID. Returns 404 if not found or not owned by the caller. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Alert rule ID (integer) |
{
"alert": {
"id": 4127,
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"name": "PEPE2 dip watch",
"drop_pct": 20,
"recovery_pct": 15,
"delivery_mode": "webhook",
"is_active": true,
"created_at": "2026-05-25T08:00:00Z",
"last_fired_at": null
}
}Update fields on a price alert rule. Patchable: name, delivery_mode, webhook_url, is_active. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Alert rule ID (integer) |
| name | string | — | New label |
| delivery_mode | string | — | websocket / webhook / both |
| webhook_url | string | null | — | New HTTPS URL or null to clear |
| is_active | boolean | — | Pause (false) or resume (true) |
{
"alert": {
"id": 4127,
"name": "Updated dip alert",
"is_active": true,
"updated_at": "2026-05-25T15:00:00Z"
}
}Permanently delete a price alert and its event history. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Alert rule ID (integer) |
{
"deleted": true
}Historical dip/recovery events fired by your price alerts. Each event carries the token's MC at fire time, the drop/recovery percentage, and delivery status. Useful for backtesting alert quality and auditing missed deliveries. PRO/ULTRA only.
| Name | Type | Default | Description |
|---|---|---|---|
| alert_id | integer | — | Filter to events from a specific alert rule |
| event_type | string | — | "dip" or "recovery" |
| since | ISO datetime | — | Only events after this timestamp |
| limit | integer | 50 | Max results (1-500) |
{
"events": [
{
"id": "e7c12f88-...",
"alert_id": 4127,
"event_type": "dip",
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"mc_at_fire_usd": 340000,
"recent_high_mc_usd": 750000,
"drop_pct_actual": 54.7,
"fired_at": "2026-05-25T12:42:11Z",
"delivered": true
},
{
"id": "f8d23a99-...",
"alert_id": 4127,
"event_type": "recovery",
"token_mint": "9aK2rFy1...pump",
"token_symbol": "PEPE2",
"mc_at_fire_usd": 480000,
"dip_low_mc_usd": 340000,
"recovery_pct_actual": 41.2,
"fired_at": "2026-05-25T14:18:33Z",
"delivered": true
}
],
"count": 2
}Bulk exports
The archived history as Parquet files, for backtests and model training — no paging through the API. Business and Enterprise.
Every fully archived closed month of token_trades, rhc_trades, token_ohlc_1m and rhc_ohlc_1m as zstd Parquet — the same immutable dual-provider archive behind /data-integrity. Call with no params to list what is archived (rows, bytes, files per month); add dataset + month for presigned S3 download links valid about 1 hour. The open month is never listed — it is live in Postgres behind the paginated endpoints. BUSINESS+ (Enterprise passes). Errors: 400 invalid_params, 404 month_not_archived, 502 archive_unavailable.
| Name | Type | Default | Description |
|---|---|---|---|
| dataset | string | — | token_trades · rhc_trades · token_ohlc_1m · rhc_ohlc_1m — required together with month; omit both to list |
| month | string | — | Closed month YYYY-MM (UTC) — required together with dataset |
{
"dataset": "token_trades",
"month": "2026-07",
"rows": 412884920,
"bytes": 41206118400,
"files": [
{
"name": "token_trades/2026-07/part-0001.parquet",
"bytes": 2147483648,
"url": "https://…/token_trades/2026-07/part-0001.parquet?X-Amz-Signature=…",
"expires_at": "2026-09-12T15:04:05Z"
}
],
"license_note": "Business embed license — display inside your product; redistribution requires Enterprise."
}Sniper Alerts (Pro + Ultra)
New token launches surface a median ~290ms before they confirm on-chain. Webhook + WS delivery. PRO covers elite + good deployers; ULTRA adds every tier, custom watchlists, and the by-deployer feed.
Newest-first list of pump.fun deploys detected before chain settlement. Median ~290ms head start in production vs CONFIRMED-stream alerts (24 h to 2026-09-20, n = 37,957 reconciled deploys, p10 160ms / p90 401ms; the confirmed stream is first on ~3.4% of launches, which is why the head start is published as a median, not a floor). Payload carries mint + deployer + signature + slot + a snapshot of the deployer's reputation (tier, lifetime bond rate, last-10 outcome bitmap). Each deploy also carries a `footprint` block — the slot-window snipe rollup (slots deploy−1 to deploy+3): buys, buyers, sol, supply_pct, sniper_wallet_buys, data_available, as_of; null for deploys younger than the rollup's 10-minute settle window. attribution_status says whether the deshred read of the deployer was cross-checked against the confirmed create tx (every 15 min): unverified until checked, confirmed when it matched, corrected when the deployer/mint was fixed from the confirmed tx (attribution_checked_at = when that ran). For live alerts use the `sniper:deploy` webhook event or the `sniper:deploys` WebSocket channel; this endpoint is for backtesting / catch-up after a gap. PRO is curated to elite/good-tier deployers; ULTRA sees every tier, and ULTRA can pass watchlist=true to narrow the feed to its custom deployer watchlist (an empty watchlist answers deploys: [] with watchlist_empty: true). Every 200 carries the launch-pipeline `coverage` block.
| Name | Type | Default | Description |
|---|---|---|---|
| since | ISO datetime | — | Only deploys detected after this timestamp |
| deployer_tier | string | — | elite / good / moderate / rising / cold / unranked |
| min_bond_rate | number | — | Minimum lifetime bond rate (0-1, e.g. 0.5 = 50%) |
| watchlist | boolean | false | ULTRA only — true narrows to the deployers on your sniper watchlist (any tier, overrides the tier scope); 403 on PRO |
| limit | integer | 50 | Max results (1-200) |
{
"deploys": [
{
"mint": "9kQu...3xY7pump",
"name": "Waffle Inu",
"symbol": "WAFFLE",
"deployer_wallet": "7dEx...4pQ8",
"signature": "5xT...8jK",
"slot": 305418729,
"detected_at": "2026-05-28T19:32:29.068Z",
"detection_region": "Frankfurt",
"detection_confirmed": false,
"attribution_status": "confirmed",
"attribution_checked_at": "2026-05-28T19:45:01Z",
"deployer_tier": "elite",
"deployer_bond_rate": 0.734,
"deployer_total_bonded": 11,
"deployer_recent": "BBBBBFBBB",
"confirmed_on_chain": null,
"confirmed_at": null,
"footprint": {
"buys": 14,
"buyers": 9,
"sol": 12.4,
"supply_pct": 3.8,
"sniper_wallet_buys": 4,
"data_available": true,
"as_of": "2026-05-28T19:42:30Z"
}
}
],
"count": 1,
"data_age_seconds": 4
}Recent pre-confirm deploys filtered to a single deployer wallet. Useful for auditing a deployer's recent track record before adding them to a custom watchlist.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Deployer wallet address (base58, 32-44 chars) |
| limit | integer | 50 | Max results (1-200) |
{
"deployer": "7dEx...4pQ8",
"deploys": [
{
"mint": "9kQu...3xY7pump",
"symbol": "WAFFLE",
"signature": "5xT...8jK",
"slot": 305418729,
"detected_at": "2026-05-28T19:32:29.068Z",
"detection_region": "Frankfurt",
"deployer_tier": "elite",
"deployer_bond_rate": 0.734,
"deployer_total_bonded": 11,
"confirmed_on_chain": null
}
],
"count": 1
}Your custom sniper watchlist — specific deployer wallets you want alerts for regardless of tier. Enable it as a scope on any channel: `?watchlist=true` here, `sniper_scope: "watchlist"` in a webhook/WS subscription. Ultra only, up to 50 deployers.
{
"deployers": [
{
"deployer_wallet": "7dEx...4pQ8",
"label": "alpha dev",
"created_at": "2026-05-29T18:00:00Z"
}
],
"count": 1,
"limit": 50,
"remaining": 49
}Add one deployer (`wallet`) or many (`wallets[]`, the paste-50-addresses case), with an optional `label`. Idempotent — already-watchlisted deployers are skipped. DELETE /sniper/watchlist/{wallet} removes one.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string | — | Single deployer wallet (base58) |
| wallets | string[] | — | Bulk add (max 50) |
| label | string | — | Optional note for the added deployer(s) |
{
"added": 2,
"deployers": [
"7dEx...4pQ8",
"9aBc...2zZ1"
]
}Remove one deployer wallet from your custom sniper watchlist. 404 if it isn't on your list. Ultra only.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string (path) | required | Deployer wallet (base58) |
{
"removed": "7dEx...4pQ8"
}Robinhood Chain
Every plan covers Robinhood Chain (chain id 4663) at no extra cost — one key, one shared quota pool, EVM-native field names.
Robinhood Chain (chain id 4663)
Same KOL wallets (bridge-traced to their EVM addresses), plus any-wallet PnL and positions, chain-wide token discovery, the DEX trade tape, candles and deployer reputation — from our self-hosted node. Copy-trade rules, price alerts, coordination alerts and first-touch subscriptions all have RHC equivalents here, each on its own PER-CHAIN quota that never touches your Solana budget. 14 WebSocket channels ride the standard stream: rhc:kol_trades, rhc:dex_trades, rhc:dex_trades_unattributed, rhc:new_tokens, rhc:copytrade:signals, rhc:price_alert:events, rhc:kol:coordination, rhc:kol:first_touches, rhc:token_locks, rhc:token_prices, rhc:lp_events, rhc:token_candles, rhc:token_risk and rhc:wallet_scores.
Live buys and sells on Robinhood Chain from the same verified KOL wallets we track on Solana — EVM addresses traced through their bridge deposits. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of trades (1-100) |
| action | string | — | Filter: "buy" or "sell" |
| wallet | string | — | Filter by KOL 0x address |
| min_kol_winrate | number | — | Minimum 7d winrate of the KOL, 0–1 (a FRACTION, not the 0–100 percent the Solana /kol/feed takes). Resolved from the RHC KOL score matview, which scores CLOSED positions only — a KOL who never sells has a null winrate and is DROPPED, not treated as zero. So ?min_kol_winrate=0 is not a no-op: it means "scored, and non-negative". |
| strategy | string | — | Hold-time bucket from the RHC score matview: "scalper", "day_trader", "swing", "inactive" or "unscored". These values DIFFER from the Solana /kol/feed strategy set (swing_trader / hodler / mixed) — it is a separate classifier, not a port. Like min_kol_winrate, KOLs absent from the matview are dropped. |
{
"chain": "robinhood",
"trades": [
{
"kol_name": "danny",
"evm_address": "0x4a9f…c21e",
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"action": "buy",
"eth_amount": 0.215,
"market_cap_usd_at_trade": 3300000,
"dex": "uniswap-v4",
"tx_hash": "0x7d1a…8e3f",
"traded_at": "2026-07-15T17:04:12+00:00"
}
],
"count": 1
}Net-flow leaderboard of KOLs on Robinhood Chain over 24h/7d/30d windows. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 24h | "24h", "7d" or "30d" |
{
"chain": "robinhood",
"leaderboard": [
{
"kol_name": "danny",
"trades": 42,
"buy_eth": 12.4,
"sell_eth": 3.1
}
]
}Consensus signal — tokens bought by 2+ distinct tracked KOLs on Robinhood Chain inside the window, ranked by KOL-buyer count then volume, enriched with launchpad, deployer tier, graduation and current MC. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| window | string | 1h | Rolling consensus window: "5m", "15m", "1h", "6h" or "24h" |
{
"chain": "robinhood",
"window": "1h",
"tokens": [
{
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"kol_buyer_count": 4,
"buy_volume_eth": 6.2,
"market_cap_usd": 3300000,
"deployer_tier": "good"
}
],
"count": 1
}Tokens on Robinhood Chain that ≥ min_kols distinct tracked KOLs bought inside the window — the read/poll twin of the /rhc/kol/coordination/alerts rule engine. Each cluster carries buy/sell/net ETH, accumulating vs distributing signal, exited vs holding KOL counts, time-to-consensus and MC at first buy / now / peak. RHC has no KOL winrate / strategy / coordination-score MVs, so those Solana fields are intentionally absent. Optional MC band on entry MC. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 24h | "1h", "6h", "24h" or "7d" ("window" accepted as an alias) |
| min_kols | integer | 2 | Minimum distinct KOL buyers (2-50) |
| limit | integer | 20 | Max results (1-50) |
| min_mc_usd | number | — | Entry-MC floor in USD |
| max_mc_usd | number | — | Entry-MC ceiling in USD |
{
"chain": "robinhood",
"period": "24h",
"min_kols": 2,
"coordination": [
{
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"token_name": "Cash Cat",
"launchpad": "clanker",
"is_graduated": true,
"deployer_tier": "good",
"token_age_minutes": 92,
"kol_count": 4,
"total_buys": 7,
"buy_eth": 6.2,
"sell_eth": 0.4,
"net_eth": 5.8,
"signal": "accumulating",
"exited_count": 1,
"holders_count": 3,
"first_buy_at": "2026-08-18T08:02:11Z",
"last_buy_at": "2026-08-18T08:41:50Z",
"time_to_consensus_sec": 2379,
"market_cap_usd_at_first_buy": 412000,
"current_mc_usd": 1180000,
"peak_mc_usd": 1420000,
"liquidity_usd": 96000,
"kols": [
{
"kol_name": "danny",
"evm_address": "0x4a9f…c21e"
}
]
}
],
"count": 1
}The earliest tracked-KOL buy per Robinhood Chain token — one row per token, ever (a second KOL buying the same token is not a first touch). Enriched with launchpad, graduation flag, token age and MC at first buy / now / peak. Free tier (limit clamped to 20 on BASIC); ULTRA adds the KOL's evm_address on each row.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Max events (1-100) |
| since / before | ISO datetime | — | Cursor window on first_buy_at (pass next_before from the previous page) |
| min_eth | number | — | Minimum first-buy size in ETH |
| token_age_max_min | integer | — | Only tokens younger than N minutes at first touch (1-43200) |
| launchpad | string | — | Launchpad slug filter |
| min_mc_usd / max_mc_usd | number | — | MC band at first touch (USD) |
{
"chain": "robinhood",
"events": [
{
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"token_name": "Cash Cat",
"launchpad": "clanker",
"is_graduated": true,
"first_buy_at": "2026-07-15T17:04:12Z",
"eth_amount": 0.215,
"token_amount": 1240000,
"tx_hash": "0x7d1a…8e3f",
"token_age_minutes": 4,
"market_cap_usd_at_first_buy": 412000,
"price_usd_at_first_buy": 0.00041,
"current_mc_usd": 101000000,
"peak_mc_usd": 206000000,
"first_kol": {
"name": "danny",
"twitter_url": "https://x.com/danny"
}
}
],
"count": 1,
"next_before": "2026-07-15T17:04:12Z",
"data_age_seconds": 41
}A single KOL's Robinhood Chain profile — identity (name, Twitter), aggregate stats, and their 50 most recent RHC trades with MC-at-trade and tx hash. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string | — | KOL EVM wallet address (0x, 40 hex) — path param |
{
"chain": "robinhood",
"evm_address": "0x4a9f…c21e",
"kol_name": "danny",
"kol_twitter": "danny",
"stats": {
"trades": 128,
"buy_eth": 42.1,
"sell_eth": 18.7
},
"trades": [
{
"token_symbol": "CASHCAT",
"action": "buy",
"eth_amount": 0.215,
"tx_hash": "0x7d1a…8e3f"
}
]
}Every v2/v3/v4 + launchpad-curve swap on the chain with real trader EOA, gas, MC-at-trade and pool liquidity. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| token | string | — | Filter by token 0x address |
| dex | string | — | "uniswap-v2", "uniswap-v3" or "uniswap-v4" |
| action | string | — | "buy" or "sell" |
| limit | integer | 50 | Number of trades (1-200) |
| before | string | — | Keyset cursor — pass `next_before` from the previous page |
{
"chain": "robinhood",
"trades": [
{
"token_address": "0x8c3b…9f04",
"action": "sell",
"eth_amount": 1.2,
"trader_eoa": "0x4a9f…c21e",
"dex": "uniswap-v3",
"block_number": 11133411
}
]
}Alias of /rhc/trades?token={address} — one token's swaps, newest first, keyset-paginated; same params (dex, action, limit 1-200, before). Inherits the /rhc/trades gate. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string (path) | required | RHC token 0x address |
| limit | integer | 50 | Number of trades (1-200) |
| before | string | — | Keyset cursor (next_before) |
{
"chain": "robinhood",
"trades": [
{
"token_address": "0x8c3b…9f04",
"action": "buy",
"eth_amount": 0.4,
"trader_eoa": "0x4a9f…c21e",
"dex": "uniswap-v4",
"tx_hash": "0x7d1a…8e3f",
"block_time": "2026-07-15T17:04:12Z"
}
],
"has_more": true,
"next_before": "eyJi…"
}Screener over every token on the chain: sort by last trade, market cap, liquidity, peak MC or newest; launchpad, asset-class and liquidity/MC floors. /rhc/tokens/new and /rhc/tokens/recent are aliases pinned to sort=newest. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| sort | string | last_trade | "last_trade", "market_cap", "liquidity", "peak_mc", "newest" |
| limit | integer | 50 | Number of tokens (1-100) |
| since | ISO datetime | — | Only tokens first seen after this time (with sort=newest) |
| asset_class | string | — | "equity" (tokenized stocks/ETFs) or "other" |
| launchpad | string | — | Launchpad slug filter |
| min_mc_usd | number | — | Minimum market cap in USD |
| min_liquidity_usd | number | — | Minimum pool liquidity in USD |
{
"chain": "robinhood",
"tokens": [
{
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"market_cap_usd": 101000000,
"liquidity_usd": 3360000,
"deployer_tier": "good"
}
]
}Alias of /rhc/tokens?sort=newest — the newest tokens on the chain, first-seen order; every other /rhc/tokens param (since, limit, asset_class, launchpad, min_mc_usd, min_liquidity_usd) passes through. /rhc/tokens/recent is the same alias. Inherits the /rhc/tokens gate. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| since | ISO datetime | — | Only tokens first seen after this time |
| limit | integer | 50 | Number of tokens (1-100) |
{
"chain": "robinhood",
"tokens": [
{
"address": "0x1f2e…aa10",
"symbol": "NEWCAT",
"launchpad": "clanker",
"first_seen_at": "2026-08-18T09:40:11Z",
"market_cap_usd": 18400,
"liquidity_usd": 6100
}
]
}Alias of /rhc/tokens?sort=newest — identical to /rhc/tokens/new. Inherits the /rhc/tokens gate. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Number of tokens (1-100) |
{
"chain": "robinhood",
"tokens": [
{
"address": "0x1f2e…aa10",
"symbol": "NEWCAT",
"first_seen_at": "2026-08-18T09:40:11Z"
}
]
}Every Robinhood-issued tokenized stock / ETF on the chain with live price, MC, liquidity and 24h trades / ETH volume / buyers / sellers. Identity is the issuer BEACON (EIP-1967 beacon proxy on Robinhood's issuer beacon 0xe10b…151b00, read from our own node) — never the name, so the fake "GameStop • Robinhood Token" contracts never appear and verified is true by construction. The response states the identity method and beacon. Free tier; per-token drill-downs (/trades, /holders, /candles) keep their own gates.
| Name | Type | Default | Description |
|---|---|---|---|
| sort | string | volume | "volume" (24h ETH), "trades" (24h), "market_cap", "last_trade", "symbol" |
| limit | integer | 100 | Number of equities (1-300) |
| symbol | string | — | Exact ticker, e.g. NVDA |
| q | string | — | Substring of symbol or name (1-40 chars) |
{
"chain": "robinhood",
"equities": [
{
"address": "0x33a1…c9d2",
"symbol": "NVDA",
"name": "Nvidia • Robinhood Token",
"underlying_name": "Nvidia",
"verified": true,
"last_price_usd": 181.42,
"market_cap_usd": 44200000,
"liquidity_usd": 1900000,
"trades_24h": 12034,
"volume_eth_24h": 2229.4,
"buyers_24h": 3120,
"sellers_24h": 2870,
"primary_dex": "uniswap-v4",
"last_trade_time": "2026-08-18T09:41:50Z"
}
],
"count": 1,
"total_equities": 206,
"sort": "volume",
"identity": {
"method": "beacon",
"issuer_beacon": "0xe10b6f6b275de231345c20d14ab812db62151b00"
}
}Full token bundle: price, MC, peak + drawdown, pools with reserves, deployer reputation block and KOL activity. Free tier.
{
"chain": "robinhood",
"token": {
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"market_cap_usd": 101000000,
"peak_mc_usd": 206000000,
"deployer": {
"address": "0x91ab…77c2",
"tier": "good",
"graduation_rate": 0.31,
"runner_rate": 0.28
}
}
}The /rhc/tokens/{address} bundle for up to 50 tokens in one request (body: { addresses: [...] }; "mints" is accepted as an alias key). Unknown addresses come back with found: false. GET /rhc/token/{address} is a compat alias of /rhc/tokens/{address}. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| addresses | string[] | required | 1-50 RHC token 0x addresses |
{
"chain": "robinhood",
"tokens": [
{
"address": "0x8c3b…9f04",
"found": true,
"symbol": "CASHCAT",
"market_cap_usd": 101000000,
"liquidity_usd": 3360000,
"deployer_tier": "good"
},
{
"address": "0x0000…dead",
"found": false
}
],
"requested": 2,
"found": 1
}Per-minute OHLC + volume + close liquidity for any RHC token, built from the raw trade stream. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 240 | Number of candles (1-1500) |
{
"chain": "robinhood",
"candles": [
{
"t": "2026-07-15T17:04:00Z",
"o": 0.0021,
"h": 0.0024,
"l": 0.002,
"c": 0.0023,
"v_usd": 18240
}
]
}How the tracked-KOL cohort is positioned on an RHC token: distinct buyers vs sellers, exit rate, net ETH flow, and median entry market cap. PRO+ (ULTRA adds the buyer/exited wallet lists).
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"consensus": {
"total_kol_buyers": 83,
"total_kol_sellers": 72,
"kol_exit_rate": 0.735,
"net_flow_eth": -407.5,
"median_entry_mc_usd": 63279213,
"entry_mc_samples": 256
}
}A 0-100 quality score on an RHC token's earliest buyer cohort — full parity with the Solana scorer: win-rate + KOL + bot + bundle-buyer + dump-cluster legs. `coverage` reports both bundle_detection and dump_cluster_signal available (dump-cluster is informational and out-of-sample validated). Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"cohort_selection": "distinct_first_buy",
"quality": {
"score": 28,
"confidence": "high",
"signal": "negative",
"breakdown": {
"early_buyers_analyzed": 20,
"wallets_with_history": 14,
"qualified_win_rate_wallets": 12,
"alpha_wallet_count": 8,
"kol_count": 0,
"bundle_buyer_count": 6,
"dump_cluster_count": 6,
"recycled_early_buyer_count": 15,
"avg_historical_win_rate": 48.41,
"bot_dominated": true
}
},
"coverage": {
"bundle_detection": "available",
"dump_cluster_signal": "available"
}
}Buyer-quality scores for up to 20 RHC tokens in one call — same scorer and coverage block as the single-token endpoint. Body: { addresses: [...] }. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| addresses | string[] | required | 1-20 RHC token 0x addresses |
{
"chain": "robinhood",
"tokens": [
{
"token_address": "0x8c3b…9f04",
"score": 64,
"signal": "positive",
"breakdown": {
"kol_buyers": 3,
"bot_buyers": 1,
"dump_cluster_count": 0
}
}
],
"requested": 1,
"max_addresses": 20,
"coverage": {
"bundle_detection": true,
"dump_cluster_signal": true
}
}Detects a coordinated launch bundle in an RHC token's earliest buyers: ranks the first 20 by on-chain order, flags a bundle when 3+ buy in the same block, then reports how much of what they bought they still hold — or if they fully exited (bought the launch, then dumped). BASIC gets the scalar signal; PRO the top-10 wallets; ULTRA the full cohort with alpha-wallet identity.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"bundle": {
"wallet_count": 9,
"bundle_kind": "same_block",
"held_ratio": 0,
"held_pct_of_supply": 0,
"fully_exited": true,
"buy_volume": 261900000,
"tokens_held": 0
},
"wallets": []
}Who actually made money on a token. Lifetime per-trader performance ranked by realized ETH, enriched with win-rate, bot heuristic, KOL identity, dump-cluster membership and early-buyer rank. net_eth is REALIZED flow (sell − buy), not PnL — it doesn't value a trader's remaining bag, so a wallet that bought and still holds ranks last. PRO returns 50 rows; ULTRA/BUSINESS 200.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
| limit | number | 50 | 1–200 (capped at 50 on PRO) |
| offset | number | 0 | Page offset |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"traders": [
{
"trader_eoa": "0x3387…9694",
"buy_eth": 412.3,
"sell_eth": 759.5,
"net_eth": 347.18,
"trades": 992,
"win_rate": 0.61,
"likely_bot": true,
"is_known_kol": false,
"kol_name": null,
"dump_cohorts": 0,
"early_buyer_rank": null
}
],
"count": 1,
"limit": 50,
"offset": 0,
"has_more": true,
"metric": "net_eth = realized ETH flow (sell - buy); excludes unrealized bag value"
}Who is accumulating and who is distributing. Splits a token's flow into mutually-exclusive trader cohorts — KOL, bot, dump-cluster, early-buyer, unprofiled, smart-money, retail. Sign convention: net_eth = sell − buy, so a positive value means that cohort DISTRIBUTED. smart_money is derived (win-rate ≥ 0.5 and net positive); unprofiled is a real answer, not missing data.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
| window | string | 24h | "1h", "6h", "24h", "7d" |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"window": "24h",
"cohorts": [
{
"cohort": "bot",
"traders": 1142,
"trades": 10404,
"buy_eth": 485.64,
"sell_eth": 827.37,
"net_eth": 341.73
},
{
"cohort": "smart_money",
"traders": 385,
"trades": 1144,
"buy_eth": 86.62,
"sell_eth": 176.09,
"net_eth": 89.46
},
{
"cohort": "retail",
"traders": 1365,
"trades": 4054,
"buy_eth": 465.83,
"sell_eth": 325.97,
"net_eth": -139.86
}
],
"sign_convention": "net_eth = sell - buy; positive = cohort distributed, negative = accumulated"
}Peak market cap, drawdown from peak, and a running high-water curve. Returns TWO peaks because they disagree: peak_mc_usd_recorded is the stored high-water mark other surfaces key off (sampled from write batches, so it can undercount an intra-batch spike), while peak_mc_usd_observed is the max of 1-minute candle highs — trade-level truth, always ≥ recorded. Candle history starts 2026-07-15.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
| window | string | 7d | "24h", "7d", "30d", "all" |
| curve | string | true | "false" for the summary only |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"symbol": "NVDA",
"current": {
"market_cap_usd": 1412003,
"liquidity_usd": 88120
},
"peak": {
"peak_mc_usd_recorded": 1814857,
"peak_mc_usd_observed": 2694095,
"observed_covers_full_history": false,
"pct_of_peak": 0.524,
"drawdown_from_peak": 0.476
},
"curve": {
"window": "7d",
"bucket": "5m",
"count": 1338
}
}Risk computed LIVE against our own Robinhood Chain node. Not the Solana model — EVM has no mint or freeze authority, and only 2.3% of RHC tokens even expose an owner function, so an absent flag is the norm rather than a safety signal. What discriminates here: proxy upgradeability, LP custody, and above all sellability — a sell is simulated at the chain head to catch a token you can buy but not sell. Never cached.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"checked_at": "2026-07-29T20:12:46.369Z",
"code_size": 4830,
"is_contract": true,
"proxy": {
"kind": "eip1967_beacon",
"implementation": "0xb354…5ae2",
"upgradeable": true
},
"owner": {
"model": "none",
"address": null
},
"capabilities": {
"can_mint": true,
"can_pause": true,
"has_access_control": true,
"selectors_found": [
"mint(address,uint256)",
"pause()",
"hasRole(bytes32,address)"
]
},
"liquidity": {
"dex": "uniswap-v3",
"lp_custody": "burned",
"lp_burned_pct": 99.27
},
"sellability": {
"sellable": "yes",
"reason": null
},
"flags": [
"mintable",
"pausable",
"upgradeable"
],
"score": 55
}The first buyers of an RHC token, in order, joined to what happened next — realized ETH, trade count, average entry market cap, and whether they still hold. Read position alongside realized_eth: it is sell minus buy, so a buyer who is still holding shows a NEGATIVE figure because they have spent and not yet sold. still_holding comes from the same Transfer-log fold as /holders, so treat it as exact only when holdings_verified is true. Ranks are as of computed_at (a daily sweep), so a token that started trading since then returns an empty list with a stated reason — never an assertion that it had no early buyers.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
| limit | number | 20 | 1–20 (ranking depth is 20 by construction) |
{
"chain": "robinhood",
"token_address": "0x85f2…34f9",
"computed_at": "2026-07-30T19:13:40Z",
"holdings_verified": true,
"early_buyers": [
{
"rank": 3,
"wallet": "0x902c9d30…",
"first_buy_at": "2026-07-29T11:02:14Z",
"first_buy_block": 34118207,
"still_holding": false,
"balance": "0",
"position": "closed",
"bought_eth": 0.362932,
"sold_eth": 0.483379,
"realized_eth": 0.120447,
"trades": 13,
"avg_entry_mc_usd": 41288.55
}
],
"count": 20,
"summary": {
"ranked": 20,
"with_holding_data": 20,
"still_holding": 1,
"exited": 19,
"closed_positions": 19,
"realized_eth_closed_only": 0.229843
}
}Exact holder set and concentration — folded from ERC-20 Transfer logs, not derived from trades, and reconciled against on-chain totalSupply(). Check verified first: false means the reconstruction is incomplete for that token and the response says why. Concentration excludes liquidity pools and burn addresses from the circulating denominator (the top holder is otherwise the token's own pool) and reports them separately. Balances are raw uint256 strings. holder_growth gives entered / exited / net holders over 1h, 24h and 7d — possible because the Transfer-log fold keeps first-seen and last-moved blocks per address.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string | — | RHC token 0x address |
| limit | number | 50 | 1–200 (capped at 50 on PRO) |
| offset | number | 0 | Page offset |
{
"chain": "robinhood",
"token_address": "0x8c3b…9f04",
"verified": true,
"unverified_reason": null,
"holders": [
{
"holder": "0xb98b…ded7",
"balance": "72595847095530512318459056",
"share": 0.080699,
"is_pool": true,
"is_burn": false,
"is_deployer": false
}
],
"concentration": {
"holder_count": 214314,
"top1_share": 0.04663,
"top10_share": 0.245752,
"top50_share": 0.62173,
"hhi": 0.011576,
"pool_held_pct": 0.072596,
"burned_pct": 0.069595
},
"holder_growth": {
"1h": {
"cutoff_block": 38939885,
"entered": 370,
"entered_still_holding": 357,
"exited": 12,
"net": 345
},
"24h": {
"cutoff_block": 38114592,
"entered": 593,
"entered_still_holding": 560,
"exited": 0,
"net": 560
}
},
"source": {
"method": "erc20_transfer_log_replay",
"backfill_complete": true
}
}Liquidity REMOVAL events across the chain from our own node's log subscription — Uniswap v2/v3 Burn logs and v4 ModifyLiquidity with a negative delta — newest first, keyset-paginated. By default removals only: a request without action returns exactly the historical feed, event is always "remove" and coverage.events says so. Since 2026-09-23 action=add|pool_created|all opts into liquidity adds (v2/v3 Mint, v4 positive ModifyLiquidity; kept 7 days) and live pool creations; earlier windows hold removals only, so an empty add page there means not recorded, not no adds. Every row also carries the depth fields tick_lower / tick_upper, liquidity_delta, in_range, active_liquidity_delta, active_share (v3/v4: a share of liquidity at the current price, not of TVL), share_of_reserves (v2) and material (a removal of at least 25 %), null where the pool state was unknown and on older rows. amount0/amount1/liquidity are raw uint256 strings; token_amount_raw / quote_amount_raw pre-resolve the token's side; v4 rows carry only liquidity (the pool manager emits no token amounts). provider is usually the router or position manager on v3/v4, not the beneficial owner. Live push: WS channel rhc:lp_events (ULTRA+). Per-token alias: GET /rhc/tokens/{address}/lp-events. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| token | string | — | Filter by token 0x address |
| pool | string | — | Pool address (v2/v3, 40 hex) or v4 poolId (64 hex) |
| dex | string | — | "uniswap-v2", "uniswap-v3" or "uniswap-v4" |
| provider | string | — | Liquidity provider 0x address |
| action | string | remove | "remove" (default, unchanged), "add" (kept 7 days), "pool_created" or "all" (2026-09-23) |
| limit | integer | 50 | Number of events (1-200) |
| before | string | — | Keyset cursor (next_before) |
{
"chain": "robinhood",
"events": [
{
"id": "8812734",
"event": "remove",
"pool": "0x5a1c…e0f9",
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"dex": "uniswap-v3",
"provider": "0x91ab…77c2",
"liquidity": "184223391004312",
"amount0": "12000000000000000000",
"amount1": "3922000000000000000000000",
"token_amount_raw": "3922000000000000000000000",
"quote_amount_raw": "12000000000000000000",
"block_number": 11402211,
"tx_hash": "0x9c…41",
"log_index": 12,
"block_time": "2026-08-18T08:12:40Z",
"tick_lower": -887220,
"tick_upper": 887220,
"liquidity_delta": "-184223391004312",
"in_range": true,
"active_liquidity_delta": "-184223391004312",
"active_share": 0.41,
"share_of_reserves": null,
"material": true
}
],
"count": 1,
"has_more": true,
"next_before": "eyJi…",
"coverage": {
"events": [
"remove"
],
"adds_persisted": true,
"adds_retention_days": 7
}
}Alias of /rhc/lp-events?token={address} — one token's liquidity removals, newest first; same params and the same removals-only coverage note. Inherits the /rhc/lp-events gate. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string (path) | required | RHC token 0x address |
| action | string | remove | Same as /rhc/lp-events: remove (default) | add | pool_created | all |
| limit | integer | 50 | Number of events (1-200) |
{
"chain": "robinhood",
"events": [
{
"event": "remove",
"pool": "0x5a1c…e0f9",
"dex": "uniswap-v3",
"provider": "0x91ab…77c2",
"block_time": "2026-08-18T08:12:40Z"
}
],
"count": 1,
"has_more": false,
"next_before": null,
"coverage": {
"events": [
"remove"
],
"adds_persisted": true,
"adds_retention_days": 7
}
}Newest token lock / vesting contracts CREATED on Robinhood Chain, newest first — who just locked tokens, of what, how much, until when. Decoded from the locker contracts' own events on our node (PinkLock-compatible, HoodLock + vesting, Team Finance-compatible, Titan Locker, UNCX-compatible LP lockers, Sablier Lockup v4); a new deployment of a known locker shape is caught on its first event. Each row carries the on-chain schedule (start / cliff / end, cliff amount, tranches) and a live derived view: locked_* (still locked right now), unlocked_*, next_unlock (cliff | final | tranche), status. sender = depositor / creator (compare with the token's deployer for a dev lock), recipient = beneficiary; cancelable_by_sender from Sablier's flag. Raw base units as strings; ui / usd / pct null when decimals or price are unknown. Create-only tape: withdrawals are NOT tracked (withdrawn is null, coverage.withdrawals_tracked false). LP locks are excluded unless subject=lp|all and never claim usd / pct. Pushed live on WS channel rhc:token_locks. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| since | string | — | ISO cursor — only locks created after it (pagination.next_since) |
| before | string | — | ISO cursor — page back (pagination.next_before) |
| token | string | — | Filter by token 0x address |
| sender | string | — | Depositor / creator 0x address |
| recipient | string | — | Beneficiary 0x address |
| locker | string | — | Locker contract 0x address |
| family | string | — | "pinklock", "hoodlock", "hoodlock-vesting", "teamfinance", "teamfinance-nft", "titan", "titan-position", "titan-vesting", "sablier", "uncx-v2-lp", "uncx-v3-lp", … |
| kind | string | — | "lock" or "vesting" |
| subject | string | token | "token" (excludes LP locks), "lp" or "all" |
| status | string | — | "active" or "completed" |
| min_usd | number | — | Post-filter on the deposited amount in USD |
| min_pct_of_supply | number | — | Post-filter on the deposited amount as % of supply |
| limit | integer | 50 | Number of locks (1-100) |
{
"chain": "robinhood",
"locks": [
{
"lock_id": "0x5fad…bc33:12",
"locker": "0xd0f7…c32f",
"locker_name": "HoodLock",
"family": "hoodlock",
"kind": "lock",
"subject": "token",
"status": "active",
"token_address": "0x2331…589b",
"sender": "0x79c1…cea2",
"amount_raw": "528871244309257833409813",
"amount": 528871.24,
"amount_usd": 4120.5,
"amount_pct_of_supply": 52.89,
"locked_raw": "528871244309257833409813",
"end_at": "2026-10-14T01:41:48Z",
"next_unlock": {
"at": "2026-10-14T01:41:48Z",
"kind": "final",
"amount": 528871.24
},
"cancelable_by_sender": null,
"withdrawn_raw": null,
"created_at": "2026-09-13T21:52:08Z",
"tx_hash": "0x5fad…bc33",
"token": {
"symbol": "PONS",
"decimals": 18,
"price_usd": 0.00779
}
}
],
"pagination": {
"limit": 50,
"count": 1,
"has_more": true,
"next_since": "2026-09-13T21:52:08Z",
"next_before": "2026-09-13T21:52:08Z"
},
"stream": {
"channel": "rhc:token_locks"
},
"coverage": {
"withdrawals_tracked": false,
"cancels_tracked": false
}
}Every lock / vesting contract on one Robinhood Chain token with a live summary — did the team lock, how much, until when, and can they cancel. summary covers the token-subject rows: locked / deposited (raw + ui + usd + % of supply), unlocking_7d_* / unlocking_30d_* (forward schedule), nearest next_unlock, active_cancelable_by_sender (funds are locked against the recipient, not the locker, when the sender can cancel), counts by family / kind, distinct depositing wallets; LP locks on the token's pools are counted apart (lp_lock_count) because their amounts are pair units. Rows active-first, largest locked first. token.facts_resolved false means decimals are unknown and every ui / usd / pct is null. Same create-only caveat as the feed. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string (path) | required | RHC token 0x address |
| status | string | — | "active" or "completed" |
| family | string | — | Locker family (see /rhc/tokens/locks) |
| subject | string | all | "token", "lp" or "all" |
| limit | integer | 200 | Rows returned (1-500); the summary always covers every row |
{
"chain": "robinhood",
"token_address": "0x2331…589b",
"token": {
"symbol": "PONS",
"decimals": 18,
"price_usd": 0.00779,
"supply": 1000000,
"market_cap_usd": 7790,
"facts_resolved": true
},
"summary": {
"lock_count": 3,
"active_count": 2,
"token_lock_count": 2,
"lp_lock_count": 1,
"by_family": {
"hoodlock": 2,
"pinklock": 1
},
"distinct_lockers": 1,
"locked_raw": "528871244309257833409813",
"locked": 528871.24,
"locked_pct_of_supply": 52.89,
"unlocking_7d": 0,
"unlocking_30d": 528871.24,
"next_unlock": {
"at": "2026-10-14T01:41:48Z",
"kind": "final",
"lock_id": "0x5fad…bc33:12"
},
"active_cancelable_by_sender": 0
},
"locks": [
{
"lock_id": "0x5fad…bc33:12",
"family": "hoodlock",
"status": "active",
"locked": 528871.24,
"end_at": "2026-10-14T01:41:48Z"
}
],
"coverage": {
"withdrawals_tracked": false
}
}Upcoming unlock EVENTS across all active Robinhood Chain lock / vesting contracts — which tokens have supply hitting the market inside the window, how much, from whose lock. One entry per active contract = its NEXT cliff / tranche / final unlock inside within=1h..90d, with amount_* (the event, raw / ui / usd / % of supply) and window_amount_* (that contract's total release over the whole window). Linear per-second streams (Sablier linear, HoodLock / Titan vesting) contribute cliff / final events only. Token subject only. Prices implying a market cap above $100B are treated as phantom (usd null). PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| within | string | 7d | "1h", "6h", "24h", "3d", "7d", "14d", "30d" or "90d" |
| token | string | — | Filter by token 0x address |
| family | string | — | Locker family |
| kind | string | — | "lock" or "vesting" |
| min_usd | number | — | On the next-event amount |
| min_pct_of_supply | number | — | On the next-event amount |
| sort | string | soonest | "soonest", "largest_usd" or "largest_pct" |
| limit | integer | 50 | Number of entries (1-200) |
{
"chain": "robinhood",
"window": {
"within": "7d",
"from": "2026-09-15T16:00:00Z",
"to": "2026-09-22T16:00:00Z"
},
"unlocks": [
{
"unlock_at": "2026-09-18T09:30:00Z",
"in_seconds": 235800,
"event": "tranche",
"amount_raw": "402532625000000000000000",
"amount": 402532.63,
"amount_usd": 3135.7,
"amount_pct_of_supply": 4.17,
"window_amount": 402532.63,
"token_address": "0x2531…3ba3",
"token": {
"symbol": "ROBIN",
"decimals": 18
},
"lock": {
"lock_id": "0xb47f…3165:7",
"family": "sablier",
"kind": "vesting",
"name": "tranchedMonthly",
"sender": "0xd7bd…e446",
"recipient": "0x7844…ac18",
"locked": 9258250.4,
"cancelable_by_sender": false
}
}
],
"pagination": {
"limit": 50,
"count": 1,
"total_in_window": 1,
"has_more": false
}
}370K+ Robinhood Chain deployers ranked by graduation rate, runner rate, tokens deployed or best peak MC — spam factories tiered out. Reputation tiers are scored on runner rate ($100K+ peak MC), not the $40K graduation bar. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| sort | string | graduation_rate | "graduation_rate", "runner_rate", "tokens_deployed", "best_peak_mc_usd", "last_deploy_at" |
| tier | string | — | "elite", "good", "neutral" or "spammer" |
| min_tokens | integer | 3 | Minimum tokens deployed |
{
"chain": "robinhood",
"deployers": [
{
"deployer_address": "0x91ab…77c2",
"tokens_deployed": 28,
"graduated": 9,
"graduation_rate": 0.321,
"runners": 8,
"runner_rate": 0.286,
"tier": "good"
}
],
"total": 99237
}Chain-wide deployer reputation summary for Robinhood Chain: total deployers and tokens, reputable (elite + good) count, population per tier, spam-token share — and the active tier thresholds (elite/good are earned on the $100K runner_rate, spammer on graduation_rate) so a consumer can see what "elite" currently means. Free tier.
{
"chain": "robinhood",
"total_deployers": 163942,
"total_tokens": 744708,
"reputable_deployers": 180,
"by_tier": {
"elite": 56,
"good": 124,
"neutral": 160825,
"spammer": 2937
},
"spam_token_share": 0.395,
"tier_rules": {
"elite": "tokens_deployed >= 5 AND runner_rate >= 0.50",
"good": "tokens_deployed >= 5 AND runner_rate >= 0.25",
"spammer": "tokens_deployed >= 20 AND graduation_rate < 0.05"
}
}Real-time launch + graduation alerts from ranked Robinhood Chain deployers, enriched with tier, priority, launchpad and current MC. Untradeable launches (no pool yet) are hidden unless include_untradeable=true. Cursor (before/since) or offset pagination. Free tier — limit is capped at 50 per page below ULTRA (ULTRA up to 500).
| Name | Type | Default | Description |
|---|---|---|---|
| deployer_tier | string | — | "elite", "good", "neutral" or "spammer" |
| priority | string | — | "high" or "medium" |
| alert_type | string | — | "new_deploy" or "graduated" |
| launchpad | string | — | Launchpad slug filter |
| min_mc | number | — | Minimum current market cap (USD) |
| include_untradeable | boolean | false | Also return launches with no pool yet |
| limit | integer | 50 | 1-50 (ULTRA: 1-500) |
| since / before | ISO datetime | — | Cursor window on detected_at |
| offset | integer | 0 | Offset pagination (0-10000) |
{
"chain": "robinhood",
"alerts": [
{
"id": 918273,
"alert_type": "new_deploy",
"priority": "high",
"title": "Elite deployer launched NEWCAT",
"message": "0x91ab…77c2 (elite, 5/9 runners) deployed NEWCAT on clanker",
"deployer_address": "0x91ab…77c2",
"tier": "elite",
"tier_at_alert": "elite",
"tier_is_stale": false,
"token_address": "0x1f2e…aa10",
"token_symbol": "NEWCAT",
"token_name": "New Cat",
"launchpad": "clanker",
"mc_at_alert": 18400,
"current_mc_usd": 22100,
"liquidity_usd": 6100,
"risk": {
"sellable": "yes",
"upgradeable": false,
"score": 18,
"checked_at": "2026-08-18T09:40:25Z"
},
"created_at": "2026-08-18T09:40:20Z",
"event_at": "2026-08-18T09:40:18Z"
}
],
"count": 1,
"next_before": "2026-08-18T09:40:20Z"
}Recently graduated tokens from ranked deployers — graduation on RHC = the $40K peak-market-cap milestone (most RHC launchpads are direct-to-DEX, not bonding-curve), stated in the response as graduation_mc. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| deployer_tier | string | — | "elite", "good", "neutral" or "spammer" |
| min_peak | number | — | Minimum peak market cap (USD) |
| limit | integer | 50 | Number of tokens (1-200) |
{
"chain": "robinhood",
"graduation_mc": 40000,
"tokens": [
{
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"deployer_address": "0x91ab…77c2",
"deployer_tier": "good",
"peak_mc_usd": 206000000,
"peak_mc_at": "2026-07-16T02:10:00Z"
}
],
"count": 1
}Top launches from elite/good RHC deployers in a period, ranked by peak market cap — a curated "what did the good deployers ship" list. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 7d | "24h", "7d", "30d" or "all" |
| limit | integer | 10 | Number of tokens (1-50) |
{
"chain": "robinhood",
"period": "7d",
"tokens": [
{
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"deployer_address": "0x91ab…77c2",
"deployer_tier": "good",
"peak_mc_usd": 206000000,
"market_cap_usd": 101000000
}
],
"reputable_deployers": 180
}One deployer's full reputation row plus their recent tokens with current + peak market caps. Free tier.
{
"chain": "robinhood",
"deployer": {
"deployer_address": "0x91ab…77c2",
"tier": "good",
"graduation_rate": 0.321,
"runner_rate": 0.286
},
"recent_tokens": [
{
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"is_graduated": true,
"peak_mc_usd": 206000000
}
]
}Full paginated list of every token one RHC deployer launched — current + peak MC, graduation flag, first_seen_at — sortable by first_seen_at or peak_mc_usd. Non-deployers return is_deployer: false with an empty list rather than a 404. Free tier.
| Name | Type | Default | Description |
|---|---|---|---|
| sort | string | first_seen_at | "first_seen_at" or "peak_mc_usd" |
| limit | integer | 50 | Number of tokens (1-100) |
| offset | integer | 0 | Offset pagination (0-10000) |
{
"chain": "robinhood",
"is_deployer": true,
"address": "0x91ab…77c2",
"tokens": [
{
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"is_graduated": true,
"peak_mc_usd": 206000000,
"market_cap_usd": 101000000,
"first_seen_at": "2026-07-14T20:01:00Z"
}
],
"total": 14
}One RHC deployer's launch history in chronological order — every token with its outcome (graduated / peak MC), deep-paginated up to 1,000 rows per page for backtests. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 100 | Rows per page (1-1000) |
| offset | integer | 0 | Offset (0-100000) |
{
"chain": "robinhood",
"is_deployer": true,
"address": "0x91ab…77c2",
"tokens": [
{
"address": "0x8c3b…9f04",
"symbol": "CASHCAT",
"first_seen_at": "2026-07-14T20:01:00Z",
"is_graduated": true,
"peak_mc_usd": 206000000
}
],
"total": 14
}Is this RHC deployer improving or decaying? Rolling 10-launch graduation-rate windows, streaks, recent-vs-lifetime rate and trend, with success_metric stated as "graduated ($40K+ peak market cap)" — Solana-parity shape. Free tier.
{
"chain": "robinhood",
"is_deployer": true,
"address": "0x91ab…77c2",
"success_metric": "graduated ($40K+ peak market cap)",
"trajectory": {
"total_launches": 14,
"lifetime_rate": 0.321,
"recent_rate": 0.4,
"trend": "improving",
"current_streak": {
"kind": "graduated",
"length": 2
},
"rolling": [
{
"window_end": 10,
"bond_rate": 0.3
},
{
"window_end": 14,
"bond_rate": 0.4
}
]
}
}Smart-money ranking on Robinhood Chain — the highest-performing non-bot wallets, filterable by net ETH, win-rate, memecoin share, size and identity (known-KOL vs net-new smart money). Also reachable at /rhc/alpha/leaderboard (identical response — an alias for parity with Solana's /alpha/leaderboard). PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| classification | string | all | "human", "smart_money" or "all" |
| identity | string | all | "known_kol", "unknown" or "all" |
| sort | string | net_eth | Sort key (net_eth, win_rate, …) |
| limit | integer | 25 | Rows returned (offset paging) |
{
"chain": "robinhood",
"wallets": [
{
"address": "0x4a9f…c21e",
"net_eth": 18.4,
"win_rate": 0.63,
"trades": 84,
"likely_bot": false
}
],
"total": 320,
"has_more": true
}Any Robinhood Chain wallet's 90-day profile: FIFO cost-basis PnL in ETH, per-token breakdown, recent trades, and reputation flags (tracked KOL, known deployer + tier, alpha-ranked, dump-cluster, early-buyer count). Keyed on trader_eoa — the effective trading account, never the router or an ERC-4337 bundler. PRO+.
{
"chain": "robinhood",
"address": "0xada5…f7cb",
"stats": {
"total_trades": 1420,
"analyzed_trades": 1180,
"unattributed_trades": 240,
"realized_pnl_eth": 1.46,
"unrealized_pnl_eth": -0.32,
"total_pnl_eth": 1.14,
"unique_tokens": 96
},
"flags": {
"is_kol": false,
"is_deployer": true,
"deployer_tier": "neutral",
"likely_bot": false,
"is_dumper": false
}
}Full FIFO cost-basis PnL: realized/unrealized split, daily realized curve, every closed position with ROI and token-weighted hold time, open positions marked to live price. Same FIFO engine as the Solana /wallet/{address}/pnl, ETH-denominated. Shares one loader with the profile endpoint, so the two cannot disagree. PRO+.
{
"chain": "robinhood",
"summary": {
"realized_eth": 1.46,
"unrealized_eth": -0.32,
"win_rate": 0.36,
"profit_factor": 1.8,
"max_drawdown_eth": 1.44,
"open_positions_count": 12,
"closed_positions_count": 84
},
"pnl_curve": [
{
"date": "2026-07-24",
"day_pnl": 0.42,
"cumulative_pnl": 1.46,
"trades": 9
}
]
}Only what the wallet still holds, marked to the current price. "Open" means FIFO-unmatched buys — a trading position, not an on-chain balance, so tokens acquired by transfer or bridge don't appear. PRO+.
{
"chain": "robinhood",
"summary": {
"open_positions": 12,
"total_cost_basis_eth": 3.9,
"total_current_value_eth": 3.1,
"total_unrealized_eth": -0.8,
"unpriced_positions": 0
},
"positions": [
{
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"cost_basis_eth": 0.42,
"current_value_eth": 1.31,
"unrealized_pct": 211.9
}
]
}One wallet's swaps, newest first, cursor-paginated on block_time. Distinct from /rhc/trades?token= — that filters by token, this by wallet. token_amount is reconstructed from raw uint256 leg amounts and is null when it can't be, never a guess. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Rows returned (max 200) |
| before | string | — | Cursor: trades older than this block_time |
| action | string | — | "buy" or "sell" |
| token | string | — | Restrict to one token address |
{
"chain": "robinhood",
"trades": [
{
"token_symbol": "GME",
"action": "buy",
"eth_amount": 0.0026,
"token_amount": 46070.24,
"dex": "uniswap-v3",
"block_time": "2026-07-25T18:23:32Z"
}
],
"count": 1,
"next_before": "2026-07-25T18:23:32Z"
}Watchlist CRUD (/rhc/wallet-tracker/watchlist) plus a per-wallet activity rollup and a merged trade feed across everything you track. Quotas are PER CHAIN — 50/100/500 RHC wallets — so this never eats into your Solana watchlist. Read straight from the chain rather than a capture log, so a wallet added today reports its full history. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| period | string | 7d | "24h", "7d" or "30d" |
| wallet | string | — | Restrict to one watched wallet |
{
"chain": "robinhood",
"period": "7d",
"wallets": [
{
"wallet_address": "0xada5…f7cb",
"label": "whale-1",
"stats": {
"trades": 447,
"buys": 222,
"sells": 147,
"net_eth": -14.1,
"tokens_traded": 56
}
}
]
}Your tracked Robinhood Chain wallets with labels and remaining per-chain capacity (PRO 50 / ULTRA 100 / BUSINESS 500 — separate from the Solana quota). PRO+.
{
"chain": "robinhood",
"wallets": [
{
"wallet_address": "0xada5…f7cb",
"label": "whale-1",
"added_at": "2026-07-20T10:00:00Z"
}
],
"count": 1,
"limit": 50,
"remaining": 49
}Track a Robinhood Chain wallet (0x address, optional label). 409 if already tracked or the per-chain limit is reached. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet_address | string | required | EVM address (0x + 40 hex) |
| label | string | — | Optional label (≤64 chars) |
{
"chain": "robinhood",
"wallet": {
"wallet_address": "0xada5…f7cb",
"label": "whale-1",
"added_at": "2026-07-20T10:00:00Z"
}
}Update the label of a tracked RHC wallet; null clears it. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string (path) | required | EVM address |
| label | string | null | required | New label or null |
{
"chain": "robinhood",
"wallet": {
"wallet_address": "0xada5…f7cb",
"label": "whale-1 (rotated)"
}
}Stop tracking an RHC wallet. 404 if it isn't on your list. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| address | string (path) | required | EVM address |
{
"chain": "robinhood",
"removed": "0xada5…f7cb"
}Merged, newest-first trade tape across every RHC wallet you track (labels attached), keyset-paginated on (block_time, id) — pass next_before back as before; a bare ISO timestamp is accepted for compatibility. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| wallet | string | — | Restrict to one tracked wallet |
| token | string | — | Filter by token 0x address |
| action | string | — | "buy" or "sell" |
| limit | integer | 50 | Number of trades (1-200) |
| before | string | — | Keyset cursor (next_before) |
{
"chain": "robinhood",
"trades": [
{
"wallet_address": "0xada5…f7cb",
"label": "whale-1",
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"action": "sell",
"eth_amount": 2.1,
"tx_hash": "0x7d1a…8e3f",
"block_time": "2026-08-18T09:30:00Z"
}
],
"count": 1,
"has_more": true,
"next_before": "eyJi…"
}Every copy-trade rule you have on Robinhood Chain, active or not, newest first. The webhook secret is never returned here — only once, at creation. Each rule also reports source_wallets_tracked / source_wallets_untracked: only wallets in the tracked KOL set (kol_evm_wallets — the set behind /rhc/kol/wallets) can ever fire, and a rule with untracked wallets carries warnings[{ code: "untracked_source_wallets" }]. Quotas are PER CHAIN: PRO 3 rules × 5 source wallets, ULTRA 20 × 50, BUSINESS 100 × 250 — none of it consumes your Solana copy-trade budget. PRO+.
{
"chain": "robinhood",
"subscriptions": [
{
"id": 7,
"name": "danny buys",
"source_wallets": [
"0x4a9f…c21e",
"0x59a1…0b3d"
],
"min_trade_eth": 0.05,
"only_action": "buy",
"sizing_mode": "fixed",
"sizing_amount": 0.01,
"delivery_mode": "both",
"webhook_url": "https://your-app.com/rhc/hook",
"is_active": true,
"created_at": "2026-07-28T09:00:00Z",
"updated_at": "2026-07-28T09:00:00Z",
"source_wallets_tracked": [
"0x4a9f…c21e"
],
"source_wallets_untracked": [
"0x59a1…0b3d"
],
"warnings": [
{
"code": "untracked_source_wallets",
"message": "1 of 2 source wallet(s) are not tracked Robinhood Chain KOL wallets … this rule will never fire for: 0x59a1…0b3d"
}
]
}
]
}Register a rule against one or more TRACKED Robinhood Chain KOL wallets (the set behind /rhc/kol/wallets). Any 0x address is accepted, but the engine only evaluates trades of tracked wallets — the response's source_wallets_untracked + warnings tell you which wallets can never fire (use /rhc/wallet-tracker for arbitrary addresses). Sizing is ETH-denominated (min_trade_eth / sizing_amount), not SOL. There is NO market-cap band on RHC copy-trade — the producer's event carries no MC, so min_mc_usd / max_mc_usd simply do not exist here; filter on MC after the fact via /rhc/tokens/{address}. Source addresses are lowercased on write, so a checksummed address still matches. Returns webhook_secret once. PRO+ (rules 3/20/100, wallets 5/50/250 per rule — per-chain quota).
| Name | Type | Default | Description |
|---|---|---|---|
| source_wallets | string[] | required | 1–250 EVM addresses (0x + 40 hex) to copy; tier-capped at 5 / 50 / 250 |
| sizing_amount | number | required | Suggested copy size in ETH (or the multiplier, depending on sizing_mode). Must be > 0 |
| name | string | — | Human-readable label (1–64 chars) |
| min_trade_eth | number | 0 | Only fire on source trades ≥ this ETH size |
| only_action | string | buy | "buy", "sell" or "both" |
| sizing_mode | string | fixed | "fixed" (ETH amount), "proportional" (multiplier × source size) or "percent_source" |
| delivery_mode | string | webhook | "webhook", "websocket" or "both" — websocket delivers on the rhc:copytrade:signals channel |
| webhook_url | string | — | HTTPS URL — required unless delivery_mode is "websocket" |
{
"chain": "robinhood",
"subscription": {
"id": 7,
"name": "danny buys",
"source_wallets": [
"0x4a9f…c21e"
],
"min_trade_eth": 0.05,
"only_action": "buy",
"sizing_mode": "fixed",
"sizing_amount": 0.01,
"delivery_mode": "both",
"webhook_url": "https://your-app.com/rhc/hook",
"is_active": true,
"created_at": "2026-07-28T09:00:00Z",
"updated_at": "2026-07-28T09:00:00Z",
"source_wallets_tracked": [
"0x4a9f…c21e"
],
"source_wallets_untracked": []
},
"webhook_secret": "<32-byte hex — shown once>",
"note": "Save the webhook_secret — it will not be shown again. Payloads are signed HMAC-SHA256 over `<timestamp>.<body>` in the X-MadeOnSol-Signature header."
}Fetch one rule by integer id. Scoped to your account — a rule you don't own returns 404, never someone else's row. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Rule ID |
{
"chain": "robinhood",
"subscription": {
"id": 7,
"name": "danny buys",
"source_wallets": [
"0x4a9f…c21e"
],
"min_trade_eth": 0.05,
"only_action": "buy",
"sizing_mode": "fixed",
"sizing_amount": 0.01,
"delivery_mode": "both",
"webhook_url": "https://your-app.com/rhc/hook",
"is_active": true,
"source_wallets_tracked": [
"0x4a9f…c21e"
],
"source_wallets_untracked": []
}
}Partial update — send only the fields you want changed; an empty body is a 400. The per-chain source_wallets cap is re-checked on update. No new webhook_secret is issued: to rotate one, delete and recreate the rule. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Rule ID |
| source_wallets | string[] | — | Replace the tracked EVM wallets (tier cap re-applied) |
| min_trade_eth | number | — | New ETH threshold |
| only_action | string | — | buy / sell / both |
| sizing_mode | string | — | fixed / proportional / percent_source |
| sizing_amount | number | — | New ETH size or multiplier |
| delivery_mode | string | — | webhook / websocket / both |
| webhook_url | string | null | — | New HTTPS URL, or null to clear |
| name | string | null | — | New label, or null to clear |
| is_active | boolean | — | Pause (false) or resume (true) |
{
"chain": "robinhood",
"subscription": {
"id": 7,
"min_trade_eth": 0.1,
"is_active": true,
"updated_at": "2026-07-28T11:00:00Z",
"source_wallets_tracked": [
"0x4a9f…c21e"
],
"source_wallets_untracked": []
}
}Permanently delete a rule. Returns 404 if it isn't yours. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Rule ID |
{
"chain": "robinhood",
"deleted": true
}The catch-up path for signals your RHC rules fired — use it to backfill a missed webhook or a dropped WebSocket. Retained 7 days. Always scoped to your own rules. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| subscription_id | integer | — | Filter to one rule (404 if not yours) |
| since | ISO 8601 | — | Signals fired at or after this timestamp |
| limit | integer | 50 | Max signals (1–500) |
{
"chain": "robinhood",
"signals": [
{
"id": 8812,
"subscription_id": 7,
"fired_at": "2026-07-28T09:14:02.318Z",
"source_wallet": "0x4a9f…c21e",
"action": "buy",
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"token_name": "Cash Cat",
"source_eth_amount": 0.215,
"suggested_eth_amount": 0.01,
"price_usd": 0.0021,
"dex": "uniswap-v4",
"tx_hash": "0x7d1a…8e3f",
"delivered": true,
"delivered_at": "2026-07-28T09:14:02.904Z"
}
],
"count": 1
}Your Robinhood Chain MC dip/recovery alerts, active and inactive. Per-chain quota: PRO 5, ULTRA 25, BUSINESS 125 active alerts — independent of your Solana price-alert budget. PRO+.
{
"chain": "robinhood",
"alerts": [
{
"id": 31,
"name": "CASHCAT dip watch",
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"baseline_mc_usd": 3300000,
"drop_pct": 20,
"recovery_pct": 15,
"status": "armed",
"dip_low_mc_usd": null,
"dip_fired_at": null,
"delivery_mode": "webhook",
"webhook_url": "https://your-app.com/rhc/dip",
"is_active": true,
"expires_at": null,
"created_at": "2026-07-28T09:00:00Z",
"updated_at": "2026-07-28T09:00:00Z"
}
]
}Create an MC dip alert on an RHC token. The token's current market cap is captured at create time as baseline_mc_usd — an untracked token, or one with no MC to baseline against, is a 400 rather than a silently dead alert. IMPORTANT: RHC alerts are event-driven off the chain trade feed (rhc:dex_trade), with price-table polls (5 s / 60 s) and a trade-tape replay as safety nets. Latency is a few seconds (the chain trade flush is about 2 s), not the sub-second in-memory loop the Solana alerts use. Do not assume Solana parity. Returns webhook_secret once. PRO+ (5 / 25 / 125 active alerts, per-chain quota).
| Name | Type | Default | Description |
|---|---|---|---|
| token_address | string | required | RHC token 0x address (lowercased on write) |
| drop_pct | number | required | MC drop threshold from baseline, 0.01–99.99 |
| recovery_pct | number | — | Optional follow-up threshold when MC recovers from the dip low, 0.01–1000 |
| name | string | — | Optional label (1–64 chars) |
| delivery_mode | string | webhook | "webhook", "websocket" or "both" — websocket delivers on rhc:price_alert:events |
| webhook_url | string | — | HTTPS URL — required unless delivery_mode is "websocket" |
{
"chain": "robinhood",
"alert": {
"id": 31,
"name": "CASHCAT dip watch",
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"baseline_mc_usd": 3300000,
"drop_pct": 20,
"recovery_pct": 15,
"status": "armed",
"delivery_mode": "webhook",
"webhook_url": "https://your-app.com/rhc/dip",
"is_active": true,
"created_at": "2026-07-28T09:00:00Z"
},
"webhook_secret": "<32-byte hex — shown once>",
"evaluation": {
"mode": "event_driven",
"trigger": "rhc:dex_trade",
"interval_seconds": 5,
"fallback_poll_seconds": {
"fast": 5,
"slow": 60
},
"note": "RHC alerts are evaluated as trades land on the rhc:dex_trade feed, with table polls and a tape replay as safety nets. Latency is a few seconds, not sub-second like the Solana alerts."
},
"note": "Save the webhook_secret — it will not be shown again."
}Fetch one alert by integer id, including its live dip state (status, dip_low_mc_usd, dip_fired_at). 404 if it isn't yours. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Alert ID |
{
"chain": "robinhood",
"alert": {
"id": 31,
"token_address": "0x8c3b…9f04",
"token_symbol": "CASHCAT",
"baseline_mc_usd": 3300000,
"drop_pct": 20,
"recovery_pct": 15,
"status": "dipped",
"dip_low_mc_usd": 2410000,
"dip_fired_at": "2026-07-28T12:42:11Z",
"is_active": true
}
}Only four fields are mutable: name, delivery_mode, webhook_url, is_active. token_address, drop_pct and recovery_pct are IMMUTABLE by design — moving a threshold on an already-dipped alert would make its recorded events uninterpretable, so delete and recreate instead. Sending an immutable field is a 400. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Alert ID |
| name | string | null | — | New label, or null to clear |
| delivery_mode | string | — | webhook / websocket / both |
| webhook_url | string | null | — | New HTTPS URL, or null to clear |
| is_active | boolean | — | Pause (false) or resume (true) |
{
"chain": "robinhood",
"alert": {
"id": 31,
"name": "CASHCAT dip watch v2",
"is_active": false,
"updated_at": "2026-07-28T15:00:00Z"
}
}Permanently delete an alert and its event history. 404 if it isn't yours. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Alert ID |
{
"chain": "robinhood",
"deleted": true
}Dip and recovery events fired by your RHC alerts, newest first — the audit / catch-up path for a missed delivery. Retained 30 days. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| alert_id | integer | — | Filter to one alert (404 if not yours) |
| event_type | string | — | "dip" or "recovery" |
| since | ISO 8601 | — | Events fired at or after this timestamp |
| limit | integer | 50 | Max events (1–500) |
{
"chain": "robinhood",
"events": [
{
"id": 902,
"alert_id": 31,
"event_type": "dip",
"fired_at": "2026-07-28T12:42:11Z",
"token_address": "0x8c3b…9f04",
"baseline_mc_usd": 3300000,
"current_mc_usd": 2410000,
"drop_pct_actual": 26.97,
"dip_low_mc_usd": 2410000,
"recovery_pct_actual": null,
"delivered": true,
"delivered_at": "2026-07-28T12:42:11.812Z"
}
],
"count": 1
}Your Robinhood Chain KOL-convergence rules — fire when N+ tracked KOLs buy the same token inside a rolling window. Per-chain quota: PRO 5, ULTRA 20, BUSINESS 100 rules, separate from the Solana coordination budget. PRO+.
{
"chain": "robinhood",
"rules": [
{
"id": "b41c…",
"name": "3 KOLs in 15m",
"min_kols": 3,
"window_minutes": 15,
"min_score": 0,
"cooldown_min": 30,
"score_jump_break": 20,
"min_mc_usd": null,
"max_mc_usd": null,
"delivery_mode": "websocket",
"webhook_url": null,
"is_active": true,
"created_at": "2026-07-28T09:00:00Z",
"updated_at": "2026-07-28T09:00:00Z"
}
]
}Fire when min_kols distinct tracked KOLs converge on one RHC token inside window_minutes. Unlike copy-trade, coordination rules DO take an MC band (min_mc_usd / max_mc_usd). Scoring is v1: the quality leg is real (7d winrate from the RHC KOL score matview) while earliness is defaulted on RHC — treat min_score accordingly. Returns webhook_secret once. PRO+ (5 / 20 / 100 rules, per-chain quota).
| Name | Type | Default | Description |
|---|---|---|---|
| min_kols | integer | 3 | Distinct KOL buyers needed to fire (2–50) |
| window_minutes | integer | 15 | Rolling convergence window (1–60) |
| min_score | integer | 0 | Minimum coordination score, 0–100 |
| cooldown_min | integer | 30 | Per-token cooldown between firings (1–1440) |
| score_jump_break | integer | 20 | Re-fire inside cooldown if the score jumps by this much (0–100) |
| min_mc_usd | number | null | — | Lower market-cap bound (0–1e12) |
| max_mc_usd | number | null | — | Upper market-cap bound (0–1e12); must be ≥ min_mc_usd |
| name | string | — | Optional label (1–64 chars) |
| delivery_mode | string | websocket | "websocket", "webhook" or "both" — websocket delivers on rhc:kol:coordination |
| webhook_url | string | — | HTTPS URL — required unless delivery_mode is "websocket" |
{
"chain": "robinhood",
"rule": {
"id": "b41c…",
"name": "3 KOLs in 15m",
"min_kols": 3,
"window_minutes": 15,
"min_score": 0,
"cooldown_min": 30,
"score_jump_break": 20,
"min_mc_usd": null,
"max_mc_usd": null,
"delivery_mode": "websocket",
"webhook_url": null,
"is_active": true,
"created_at": "2026-07-28T09:00:00Z"
},
"webhook_secret": null,
"scoring": {
"score_version": "v1",
"quality": "7d winrate from the RHC KOL score matview",
"earliness": "defaulted on RHC — no scout-score equivalent yet"
}
}Fetch one rule by UUID. Scoped to your account — 404 for anything you don't own. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Rule ID |
{
"chain": "robinhood",
"rule": {
"id": "b41c…",
"name": "3 KOLs in 15m",
"min_kols": 3,
"window_minutes": 15,
"min_score": 0,
"cooldown_min": 30,
"delivery_mode": "websocket",
"is_active": true
}
}Partial update of any rule field; an empty body is a 400. The min_mc_usd ≤ max_mc_usd check only runs when you send BOTH bounds in the same request — the DB constraint is the backstop for the one-sided case. No webhook_secret is re-issued. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Rule ID |
| min_kols | integer | — | 2–50 |
| window_minutes | integer | — | 1–60 |
| min_score | integer | — | 0–100 |
| cooldown_min | integer | — | 1–1440 |
| score_jump_break | integer | — | 0–100 |
| min_mc_usd | number | null | — | Lower MC bound, or null to clear |
| max_mc_usd | number | null | — | Upper MC bound, or null to clear |
| name | string | null | — | New label, or null to clear |
| delivery_mode | string | — | websocket / webhook / both |
| webhook_url | string | null | — | New HTTPS URL, or null to clear |
| is_active | boolean | — | Pause (false) or resume (true) |
{
"chain": "robinhood",
"rule": {
"id": "b41c…",
"min_kols": 4,
"is_active": true,
"updated_at": "2026-07-28T15:00:00Z"
}
}Permanently delete a coordination rule. 404 if it isn't yours. PRO+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Rule ID |
{
"chain": "robinhood",
"deleted": true
}Your subscriptions to the moment a tracked KOL touches an RHC token for the FIRST time — the earliest signal we can emit for a KOL/token pair. ULTRA+ only (PRO is 0): ULTRA 10, BUSINESS 50 subscriptions, a per-chain quota separate from the Solana first-touch budget.
{
"chain": "robinhood",
"subscriptions": [
{
"id": "9d02…",
"name": "winners only",
"filters": {
"min_kol_winrate": 0.55,
"strategy": "scalper",
"min_first_buy_eth": 0.1
},
"delivery_mode": "websocket",
"webhook_url": null,
"is_active": true,
"created_at": "2026-07-28T09:00:00Z",
"updated_at": "2026-07-28T09:00:00Z"
}
]
}Subscribe to first-touch events with an optional filters object. RHC deliberately omits the Solana min_scout_tier / min_n_touches filters (there is no scout-score matview on RHC) — min_kol_winrate and strategy are the quality gates instead, and both DROP unscored KOLs rather than treating them as zero. Unknown filter keys are rejected, not ignored. Returns webhook_secret once. ULTRA+ (10 / 50, per-chain quota).
| Name | Type | Default | Description |
|---|---|---|---|
| filters.kol | string | — | Restrict to one KOL EVM address (lowercased on write) |
| filters.min_first_buy_eth | number | — | Minimum size of the first buy, 0–100000 ETH |
| filters.min_kol_winrate | number | — | Minimum 7d winrate as a FRACTION, 0–1 (not 0–100). Unscored KOLs are dropped. |
| filters.strategy | string | — | "scalper", "day_trader", "swing", "inactive" or "unscored" — the RHC hold-time buckets, which differ from the Solana strategy values |
| filters.min_mc_usd | number | — | Lower market-cap bound (0–1e12) |
| filters.max_mc_usd | number | — | Upper market-cap bound (0–1e12); must be ≥ min_mc_usd |
| name | string | — | Optional label (1–64 chars) |
| delivery_mode | string | websocket | "websocket", "webhook" or "both" — websocket delivers on rhc:kol:first_touches |
| webhook_url | string | — | HTTPS URL — required unless delivery_mode is "websocket" |
{
"chain": "robinhood",
"subscription": {
"id": "9d02…",
"name": "winners only",
"filters": {
"min_kol_winrate": 0.55,
"strategy": "scalper",
"min_first_buy_eth": 0.1
},
"delivery_mode": "websocket",
"webhook_url": null,
"is_active": true,
"created_at": "2026-07-28T09:00:00Z"
},
"webhook_secret": null,
"note": "delivery_mode is websocket — subscribe to the rhc:kol:first_touches channel."
}Fetch one subscription by UUID. 404 for anything you don't own. ULTRA+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Subscription ID |
{
"chain": "robinhood",
"subscription": {
"id": "9d02…",
"name": "winners only",
"filters": {
"min_kol_winrate": 0.55,
"strategy": "scalper"
},
"delivery_mode": "websocket",
"is_active": true
}
}Partial update; an empty body is a 400. NOTE: `filters` is a whole-object REPLACE, not a merge — send the complete filter set you want, because merging would make removing a filter inexpressible. Send `filters: {}` to clear them all. ULTRA+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Subscription ID |
| filters | object | — | Replaces the whole filter object (same six keys and bounds as POST) |
| name | string | null | — | New label, or null to clear |
| delivery_mode | string | — | websocket / webhook / both |
| webhook_url | string | null | — | New HTTPS URL, or null to clear |
| is_active | boolean | — | Pause (false) or resume (true) |
{
"chain": "robinhood",
"subscription": {
"id": "9d02…",
"filters": {
"min_kol_winrate": 0.6
},
"is_active": true,
"updated_at": "2026-07-28T15:00:00Z"
}
}Permanently delete a first-touch subscription. 404 if it isn't yours. ULTRA+.
| Name | Type | Default | Description |
|---|---|---|---|
| id | uuid (path) | required | Subscription ID |
{
"chain": "robinhood",
"deleted": true
}Real-time
Events pushed as they happen via webhooks and direct WebSocket, no polling. Measured detection latency is on /benchmarks. The all-DEX firehose is its own stream on Ultra and Business.
Webhooks
PRO 3 webhooks · ULTRA 10 · BUSINESS 30. Per-rule secret, automatic retry, auto-disable on repeated failure. Nine registerable events, including rhc:kol_trade for Robinhood Chain (PRO+) and token:surge / token:revival momentum fires — note min_sol and kol_name are Solana-only filters.
Register a webhook URL to receive real-time push notifications for KOL trades and deployer alerts. HTTPS URLs only. Server generates an HMAC-SHA256 secret for payload verification. Pro+: Pro 3 webhooks max, Ultra 10, Business 30. Custom `filters.conditions` need Ultra+.
| Name | Type | Default | Description |
|---|---|---|---|
| url | string | required | HTTPS webhook URL to receive events |
| events | string[] | required | The complete registerable set (9): "kol:trade", "kol:coordination", "deployer:alert", "deployer:bond", "wallet_tracker:event", "sniper:deploy" (Pro+; Pro curated to elite/good deployers), "rhc:kol_trade" (Pro+, Robinhood Chain), "token:surge", "token:revival" (Pro+, token momentum fires). Anything else is rejected with 400. Price-alert, copy-trade, coordination and first-touch delivery is configured on the rule itself via its own `webhook_url` + per-rule HMAC secret, not through the /webhooks registry. |
| filters | object | {} | Optional filters: min_sol, action (buy/sell), kol_name, deployer_tier[], min_kols, and for token:surge / token:revival kinds[], tiers[], launchpads[], exclude_flags[], min_mc_usd/max_mc_usd. min_sol and kol_name are Solana-only — see the payload-format section before setting them on "rhc:kol_trade". ULTRA+: `conditions` — up to 10 { field, op, value } triples ANDed against the event payload (ops eq/neq/gt/gte/lt/lte/in/contains; `field` must be a documented payload field of a subscribed event type, else 400; on PRO a 403 tier_required). |
{
"webhook": {
"id": 1,
"url": "https://your-app.com/webhooks/madeonsol",
"secret": "a1b2c3d4...your-hmac-secret",
"events": [
"kol:trade",
"deployer:alert"
],
"filters": {
"min_sol": 1
},
"is_active": true,
"created_at": "2026-04-03T12:00:00Z"
},
"note": "Save the secret — it will not be shown again."
}List all your registered webhooks with delivery status and failure counts.
{
"webhooks": [
{
"id": 1,
"url": "https://your-app.com/webhooks/madeonsol",
"events": [
"kol:trade",
"deployer:alert"
],
"filters": {
"min_sol": 1
},
"is_active": true,
"last_delivered_at": "2026-04-03T14:30:00Z",
"consecutive_failures": 0
}
]
}Get webhook details and the last 20 delivery attempts with status codes, response times, and errors.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Webhook ID |
{
"webhook": {
"id": 1,
"url": "https://your-app.com/webhooks/madeonsol",
"is_active": true,
"consecutive_failures": 0
},
"recent_deliveries": [
{
"event_type": "kol:trade",
"status_code": 200,
"response_time_ms": 142,
"delivered_at": "2026-04-03T14:30:00Z",
"error": null
}
]
}Update webhook URL, events, filters, or re-enable a disabled webhook. Re-enabling resets the failure counter.
| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Webhook ID |
| url | string | — | New HTTPS URL |
| events | string[] | — | Updated event types |
| filters | object | — | Updated filters |
| is_active | boolean | — | Re-enable (true) or disable (false) |
{
"webhook": {
"id": 1,
"url": "https://your-app.com/webhooks/madeonsol",
"events": [
"kol:trade"
],
"is_active": true,
"updated_at": "2026-04-03T15:00:00Z"
}
}| Name | Type | Default | Description |
|---|---|---|---|
| id | integer (path) | required | Webhook ID |
{
"deleted": true
}Send a sample event payload to your webhook URL to verify it's reachable and correctly processing events. Returns the HTTP status code and response time.
| Name | Type | Default | Description |
|---|---|---|---|
| webhook_id | integer | required | ID of the webhook to test |
{
"success": true,
"status_code": 200,
"response_time_ms": 89
}Payload format
Every delivery carries a timestamp + HMAC-SHA256 signature over the raw body.
Every delivery carries X-MadeOnSol-Timestamp (unix ms) and X-MadeOnSol-Signature (HMAC-SHA256 of <timestamp>.<body> keyed with your webhook secret exactly as the API returned it). The signature is bare lowercase hex with no prefix. Compute it over the raw request bytes, never re-serialized JSON. The server does not enforce a replay window; the verifiers below reject deliveries older than 5 minutes, which is safe because every retry is signed again with a fresh timestamp.
X-MadeOnSol-Timestamp: <unix milliseconds> X-MadeOnSol-Signature: <hmac-sha256-hex of "<ts>.<body>"> X-MadeOnSol-Event: kol:trade Content-Type: application/json
import crypto from "node:crypto";
import express from "express";
// rawBody: the exact request bytes (never re-serialized JSON)
// secret: the webhook secret exactly as the API returned it (do not hex-decode)
function verifyMadeOnSol(rawBody, timestamp, signature, secret) {
// X-MadeOnSol-Timestamp: unix milliseconds, fresh on every retry
if (typeof timestamp !== "string" || !/^\d{1,16}$/.test(timestamp)) return false;
// Replay window: your choice, the server does not enforce one
if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60_000) return false;
// X-MadeOnSol-Signature: 64 hex characters, bare (no algorithm prefix)
if (typeof signature !== "string" || !/^[0-9a-f]{64}$/i.test(signature)) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest();
// Both sides are exactly 32 bytes here, so this compares in constant time and cannot throw
return crypto.timingSafeEqual(expected, Buffer.from(signature, "hex"));
}
const app = express();
// express.raw keeps the body as bytes. Do not mount express.json() in front of it.
app.post("/madeonsol", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyMadeOnSol(
req.body,
req.get("X-MadeOnSol-Timestamp"),
req.get("X-MadeOnSol-Signature"),
process.env.MADEONSOL_WEBHOOK_SECRET,
);
if (!ok) return res.sendStatus(401);
res.sendStatus(200); // answer inside the 10 s delivery timeout, then work
const { event, data } = JSON.parse(req.body.toString("utf8"));
// ...
});import hashlib
import hmac
import re
import time
_HEX64 = re.compile(r"[0-9a-fA-F]{64}")
_DIGITS = re.compile(r"[0-9]{1,16}")
# raw_body: the exact request bytes (never re-serialized JSON)
# secret: the webhook secret exactly as the API returned it (do not hex-decode)
def verify_madeonsol(raw_body: bytes, timestamp: str | None, signature: str | None, secret: str) -> bool:
# X-MadeOnSol-Timestamp: unix milliseconds, fresh on every retry
if not timestamp or not _DIGITS.fullmatch(timestamp):
return False
# Replay window: your choice, the server does not enforce one
if abs(time.time() * 1000 - int(timestamp)) > 5 * 60 * 1000:
return False
# X-MadeOnSol-Signature: 64 hex characters, bare (no algorithm prefix)
if not signature or not _HEX64.fullmatch(signature):
return False
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
# Both strings are 64 ASCII hex characters here, so compare_digest cannot raise
return hmac.compare_digest(expected, signature.lower())PATCH /webhooks/:id and {"is_active": true}.| kol:trade | KOL wallet buys or sells a token |
| kol:coordination | Your own coordination alert rule fired: multiple KOLs converged on the same token (delivered only to the rule owner) |
| deployer:alert | Tracked deployer launches a new token |
| deployer:bond | Tracked deployer's token graduates bonding curve |
| wallet_tracker:event | Swap or transfer on a wallet in your watchlist (subscriber-scoped) |
| sniper:deploy | Pre-confirm pump.fun deploy detected (PRO+; PRO is curated to elite/good deployers, ULTRA sees every tier) |
| rhc:kol_trade | Tracked KOL buys or sells on Robinhood Chain (PRO+). EVM-native payload — 0x addresses, eth_amount, tx_hash. |
| token:surge | A token < 30 min old running vs its launch MC — tier early / strong / breakout, each once per mint (PRO+). Carries the tape, KOL + early-buyer cohort, deployer reputation and risk_flags[]. |
| token:revival | A token with no trade candle for ≥24 h confirmed trading again by real buys + buy volume — never by the price mark alone (PRO+). Same payload shape as token:surge with dormant_hours / prev_mc_usd. |
| copytrade:signal | Matched copy-trade rule — configured via POST /copytrade/subscriptions, NOT /webhooks. Uses a per-rule HMAC secret. |
| min_sol | number | Minimum SOL amount (for kol:trade). Solana events only — a silent no-op on rhc:kol_trade, whose payload carries eth_amount, not sol_amount. |
| action | string | "buy" or "sell" (for kol:trade) |
| kol_name | string | Filter by specific KOL name. Solana events only — RHC payloads do not carry this field, so setting it drops every rhc:kol_trade event. |
| deployer_tier | string[] | Filter by deployer tiers: ["elite", "good"] |
| min_kols | number | Minimum KOL count (for kol:coordination) |
| kinds | string[] | ["surge"] or ["revival"] — which momentum fires to receive (token:surge / token:revival) |
| tiers | string[] | ["strong", "breakout"] — surge tiers to receive (token:surge only) |
| launchpads | string[] | ["pumpfun", "launchlab"] — momentum fires by launchpad / primary DEX |
| exclude_flags | string[] | ["bundled_launch", "thin_liquidity"] — drop momentum fires carrying ANY listed risk flag |
| min_mc_usd / max_mc_usd | number | Market-cap band on momentum fires (market_cap_usd at the fire) |
WebSocket streams
30 channels on one socket: 16 Solana — kol:trades, kol:coordination (user-scoped), kol:first_touches, deployer:alerts, wallet_tracker:events (ULTRA), copytrade:signals, price_alert:events, sniper:deploys, token:graduations, token:prices (mint-scoped, PRO+), token:locks, token:fee_claims, token:surges (surge + revival fires with risk flags), token:candles, token:risk (both mint-scoped, PRO+) and wallet:scores (wallet-scoped, PRO+) — plus 14 Robinhood Chain channels — rhc:kol_trades, rhc:copytrade:signals, rhc:price_alert:events, rhc:kol:coordination, rhc:kol:first_touches, rhc:token_locks, rhc:token_prices, rhc:token_candles, rhc:token_risk and rhc:wallet_scores (PRO+ — ULTRA gates only the first-touch subscription endpoint, not the channel; rhc:token_prices, rhc:token_candles and rhc:token_risk are address-scoped, rhc:wallet_scores wallet-scoped, each with its own per-connection cap), plus rhc:dex_trades, rhc:dex_trades_unattributed, rhc:new_tokens and rhc:lp_events (ULTRA+). Event frames carry id + seq (seq gaps are normal, not loss; token:price ticks carry neither). Reconnect with resume:{instance, seq, ts} to recover up to 60 minutes from memory or storage, even across a server restart; replay_end reports per channel what was and was not recovered. The all-DEX firehose runs on its own ULTRA endpoint (2 connections, Business 5).
/ws/v1/dex-stream) is documented below. For the products teams build on it and how it recovers from a short disconnect, see what you can build on the DEX trade firehose. For an interface that charts token:prices and token:candles next to a live tape, see building a trading terminal on these channels.Get your authentication token for the WebSocket streaming server. Since 2026-08-27 the token does NOT expire: every call returns the same value (safe for multi-process clients — nothing to refresh, nothing to schedule). It stops working only if your subscription lapses, or if you send {"rotate":true} to replace it (the old value then keeps working for 60 s). Own rate bucket (10/min) — REST traffic never locks you out of minting. Present it as `Authorization: Bearer <token>` on the handshake, or `?token=` if your client cannot set headers (masked in our access logs either way).
{
"token": "dGhpcyBpcyBhIHNhbXBsZSB0b2tlbg...",
"expires_at": null,
"next_refresh_at": null,
"ws_url": "wss://madeonsol.com/ws/v1/stream",
"dex_ws_url": "wss://madeonsol.com/ws/v1/dex-stream (Ultra and Business)",
"usage": "Connect: wscat -c \"wss://madeonsol.com/ws/v1/stream?token=YOUR_TOKEN\" — then send {\"type\":\"subscribe\",\"channels\":[\"kol:trades\"]}. You receive NO events until you subscribe.",
"subscribe_example": {
"type": "subscribe",
"channels": [
"kol:trades"
]
}
}wss://madeonsol.com/ws/v1/streamReal-time event streaming via direct WebSocket connection to madeonsol.com. After connecting, you MUST send a subscribe message — no events are delivered until the server acks it with {type:'subscribed'}. If no channels are active after 10s the server sends a {type:'hint'} frame. The stream token does not expire; if you rotate it, live sockets receive the new value in-band as {type:'token_refresh'}, so store it for your next reconnect. Event frames carry `seq` and a stable `id`; token:price ticks carry neither. seq is one ordinal per server process shared by all channels and all customers, assigned before your filters run, so gaps in the seq you receive are normal and are NOT evidence of loss. The id is the same whether an event is delivered live, re-sent from memory or rebuilt from storage: delivery is at-least-once, dedupe by id. RESUME: keep a cursor {instance, seq, ts} of the last frame your handler finished processing (it returned, or its promise settled) and reconnect with subscribe {..., resume:{instance, seq, ts}}. The {type:'subscribed'} ack echoes resume with accepted:true (accepted:false + a warning code 'replay_in_progress' if a replay is already running on that connection; its channels are still subscribed). Same server instance and cursor inside the in-memory buffer (last 20,000 frames, about 6-8 minutes): frames are re-sent from memory (mode:'ring', with their seq). Otherwise (server restart, or a longer absence) each channel is rebuilt from storage from the cursor's ts (minus 60 s slack, deduped by id), bounded to 60 minutes back, 2,000 rows per channel and 20,000 rows in total (rhc:dex_trades and rhc:dex_trades_unattributed: 10 minutes back and up to 10,000 rows, which can include trades that were never pushed live). Rebuilt frames carry seq:null, mode:'durable', replayed:true, and partial:true + missing:[fields] where a field is not stored. token:graduations and rhc:new_tokens have no stored source: whatever the memory buffer still holds is re-sent, then the channel reports gap 'not_reconstructable'. token:prices gets the current price row per watched mint once (snapshot:true, partial:true). Every resume or legacy replay is bracketed by {type:'replay_start'} and {type:'replay_end'}, even when there is nothing to replay; replay_end carries sent, matched, complete, reason, retryable, retry_after_ms?, resume_ts_hint?, incomplete_channels?, last_seq, last_ts (maxima over frames sent; last_seq never above live_from_seq - 1), live_from_seq, count (legacy, = matched) and channels:{<channel>:{mode, sent, matched, complete, reason?, gap?, time_basis?, max_rows?, retryable?, retry_after_ms?, late_ingest_possible?, partial?, missing?, since_ts?, truncated_at_ts?, gap_before_ts?}}; reasons are backpressure, instance_changed, ring_truncated, window_exceeded, row_cap, source_busy, source_error, late_ingest_possible, not_reconstructable. COMMIT RULE: replayed frames do not move your saved cursor, because a storage replay is sent channel by channel, not in one time order. On replay_end complete:true, commit {instance, seq: last_seq, ts: last_ts} (keep your previous value where one is null) and let live frames advance it again. On complete:false with retryable:true, keep the cursor you resumed from and resume again after retry_after_ms (from ts: resume_ts_hint ONLY when the reply carries one, which the server emits when every incomplete RETRYABLE channel was cut by row_cap (a channel with a FINAL gap, such as not_reconstructable on token:graduations or rhc:new_tokens, does not block the hint, because retrying could not fetch anything behind it); with a mixed retryable set, for example one row_cap and one source_error channel, there is no hint and you resume from your pre-resume cursor, because the hint would step past the failed channel's unread range. Per-channel truncated_at_ts is always there for a client that tracks channels separately); what arrives twice is dropped by id. On complete:false with retryable:false the reported gaps are final (not_reconstructable, window_exceeded, ring_truncated, instance_changed): backfill them from REST, then commit as for complete. SDK behaviour: on a retryable incomplete recovery our SDKs resume again on the same socket after retry_after_ms (up to maxResumeRetries, default 5), using resume_ts_hint only when every incomplete retryable channel is row_cap. The budget is per connection and its counter resets on every (re)connect. When that budget is spent, the gap event says exhausted:true: the client stops asking on that connection, the committed cursor stays where it is, and the next reconnect resumes again. On a FINAL gap they emit a gap event first (channels, reason, permanent, the server's bounds and the RANGE that may be incomplete as skipped.from -> skipped.to) with advancedPastGap:true and source:"auto", then move the cursor past it. A count of missing events is never given because it cannot be known. That advance is the client library's own decision, not your approval: set onUnrecoverableGap:"stop" to keep the cursor instead, which stops the stream and emits fatal with the same gap, and acceptGap() then re-reports it with source:"manual" and continues. WATERMARK AND HOLDBACK: a row's id or timestamp is assigned when it is written, not when it commits, so a row with a lower id can still appear after a read passed it. The server therefore advances the committable watermark only across rows whose writing transaction had already finished when the read took its snapshot (xmin_blocked:true when it stops early), holds it 5 s behind now (holdback_ms) and re-scans the last 60 s (rescan_ms) on the next resume; a stuck long transaction cannot freeze it forever (capped at maxHoldbackMs, reported as holdback_capped_ms). While the cap is in force the channel reports holdback_ms = 300000 (the nominal 5000 stays as nominal_holdback_ms) and its guarantee states the real bound and says CAPPED: about 6 minutes (5 min + the 60 s re-scan), not the usual 65 s.. Frames are still delivered as they are read; the holdback only limits what you may commit, and last_ts is held to the tightest safe_ts (per channel in replay_end.channels.<ch>.watermark {scheme, safe_ts, holdback_ms, nominal_holdback_ms?, holdback_capped_ms?, rescan_ms, xmin_blocked, gate_ms?, guarantee}, overall in replay_end.watermark_safe_ts). RECOVERY IS BOUNDED, NOT A COMPLETENESS GUARANTEE: with the id and ingest-time schemes a write whose transaction commits more than about 65 s after its id or timestamp was taken can be missed (about 6 minutes instead while holdback_capped_ms is set), and for rhc:dex_trades / rhc:dex_trades_unattributed (scheme producer_gate) recovery is bounded by the producer's 600 s freshness rule, so a trade already older than 600 s when it was ingested is not recoverable through this path at all. Each channel states its own bound in watermark.guarantee. LATE WRITES: every storage read pages by an ingest-time column, a storage id or the producer's own freshness window, so a row written late is not skipped; a channel that could not rule that out would report complete:false, reason late_ingest_possible, never complete:true. LOAD: recovery is limited server-wide; a resume waits up to 20 s for a slot, otherwise its storage-backed channels report reason source_busy with retry_after_ms (memory frames are still sent). A second replay request on a socket whose replay is still running gets only a replay_in_progress warning, no second replay_start / replay_end, so do not wait for one. Live frames that arrive during a replay are held (up to 5,000 frames or 4 MB; price ticks coalesced per mint) and delivered right after replay_end, skipping ids already sent. A socket that cannot drain during a replay gets complete:false, reason 'backpressure' and a 4008 close: reconnect and resume. Frames can only be lost across a disconnect or a 4008 close, and a resume tells you what was and was not recovered. LEGACY: replay_since_seq / replay_since_ts still work, from memory only; replay_start now says replay_truncated:true with reason 'instance_changed' or 'ring_truncated' instead of replaying nothing silently. If our own event bus connection drops, the server re-reads the missed events from storage and delivers them with replayed:true, recovered:'bus'. ENTITLEMENTS: every frame is checked against your current plan; after a downgrade, channels above it are dropped with a warning code 'channels_revoked' (also a token:prices mint set above the new cap). Revoking or rotating your API key revokes the stream token: open sockets close with 4001 within about a minute; mint a new token with the new key. NAMED SUBSCRIPTIONS: a subscribe without sub_id is the connection's default subscription (one channel set, one filter object for the socket, e.g. action:'buy' on it also drops deployer alerts); a subscribe with sub_id (1-64 chars of A-Z a-z 0-9 _ . -) opens an independent subscription with its own channels and filters, update {sub_id, filters} replaces its filters, unsubscribe {sub_id} removes it, list answers {type:'subscriptions'}. Frames under a named subscription carry sub_id; an event matching several subscriptions is delivered once per subscription (dedupe by sub_id + id); filters of one never affect another; refusals are warning frames with the sub_id (invalid_sub_id, too_many_subscriptions, unknown_sub_id, invalid_filters, channels_rejected, replay_in_progress). Cap per connection, default included: Pro 5, Ultra 10, Business 20. Resume is per subscription (each subscribe carries the same cursor; replays run one after another and live frames wait for the last replay_end; commit at the smallest last_seq across them). Pro: 2 concurrent connections. Ultra: 3. Business: 10. 15-second heartbeat.
| Name | Type | Default | Description |
|---|---|---|---|
| token | query param | required | Authentication token from POST /stream/token |
{
"_subscribe": {
"type": "subscribe",
"channels": [
"kol:trades",
"deployer:alerts"
],
"filters": {
"min_sol": 1
}
},
"_subscribe_with_resume": {
"type": "subscribe",
"channels": [
"kol:trades",
"deployer:alerts"
],
"resume": {
"instance": "6f1a2b3c4d5e",
"seq": 184203,
"ts": 1712159990000
}
},
"_subscribed_resume": {
"type": "subscribed",
"channels": [
"kol:trades",
"deployer:alerts"
],
"seq": 190112,
"instance": "9c8d7e6f5a4b",
"resume": {
"instance": "6f1a2b3c4d5e",
"seq": 184203,
"ts": 1712159990000,
"accepted": true
}
},
"_replay_start_durable": {
"type": "replay_start",
"mode": "durable",
"count": null,
"resume": true,
"reason": "instance_changed",
"since_ts": 1712159990000,
"limits": {
"max_age_ms": 3600000,
"max_rows_per_channel": 2000,
"max_rows_total": 20000,
"slack_ms": 60000
}
},
"_event_durable": {
"channel": "kol:trades",
"event": "kol:trade",
"id": "kol:trade:5K7j...",
"seq": null,
"mode": "durable",
"replayed": true,
"partial": true,
"missing": [
"slot",
"fdv_usd_at_trade",
"liquidity_usd_at_trade",
"primary_dex",
"primary_pool_address"
],
"data": {
"kol_name": "Cented",
"action": "buy",
"sol_amount": 5.27,
"token_symbol": "PEPE"
},
"ts": 1712160000000
},
"_replay_end": {
"type": "replay_end",
"count": 212,
"sent": 212,
"matched": 212,
"complete": true,
"reason": null,
"retryable": false,
"last_seq": null,
"last_ts": 1712160295000,
"live_from_seq": 190113,
"watermark_safe_ts": 1712160295000,
"mode": "durable",
"resume_reason": "instance_changed",
"channels": {
"kol:trades": {
"mode": "durable",
"sent": 180,
"matched": 180,
"complete": true,
"time_basis": "ingest_time",
"max_rows": 2000,
"watermark": {
"scheme": "ingest_time_holdback",
"safe_ts": 1712160295000,
"holdback_ms": 5000,
"rescan_ms": 60000,
"xmin_blocked": false,
"guarantee": "bounded recovery, not completeness: a write committing >65s after its stamp may be missed"
},
"partial": true,
"missing": [
"slot",
"fdv_usd_at_trade",
"liquidity_usd_at_trade",
"primary_dex",
"primary_pool_address"
],
"since_ts": 1712159930000
},
"deployer:alerts": {
"mode": "durable",
"sent": 32,
"matched": 32,
"complete": true,
"partial": true,
"since_ts": 1712159930000
}
}
},
"_replay_end_retry": {
"type": "replay_end",
"count": 2000,
"sent": 2000,
"matched": 2000,
"complete": false,
"reason": "row_cap",
"retryable": true,
"resume_ts_hint": 1712160120000,
"incomplete_channels": [
"rhc:dex_trades"
],
"last_seq": null,
"last_ts": 1712160120000,
"live_from_seq": 190113,
"mode": "durable",
"channels": {
"rhc:dex_trades": {
"mode": "durable",
"sent": 2000,
"matched": 2000,
"complete": false,
"reason": "row_cap",
"retryable": true,
"max_rows": 10000,
"truncated_at_ts": 1712160120000
}
}
},
"_subscribe_with_replay_legacy": {
"type": "subscribe",
"channels": [
"kol:trades"
],
"replay_since_seq": 184203
},
"_subscribe_named": {
"type": "subscribe",
"sub_id": "kol-buys",
"channels": [
"kol:trades"
],
"filters": {
"action": "buy",
"min_sol": 1
}
},
"_subscribed_named": {
"type": "subscribed",
"sub_id": "kol-buys",
"channels": [
"kol:trades"
],
"filters": {
"action": "buy",
"min_sol": 1
},
"seq": 190112,
"instance": "9c8d7e6f5a4b"
},
"_event_named": {
"channel": "kol:trades",
"sub_id": "kol-buys",
"event": "kol:trade",
"id": "kol:trade:5K7j...",
"seq": 190113,
"data": {
"kol_name": "Cented",
"action": "buy",
"sol_amount": 5.27,
"token_symbol": "PEPE"
},
"ts": 1712160000000
},
"_update_named": {
"type": "update",
"sub_id": "kol-buys",
"filters": {
"action": "buy",
"min_sol": 5
}
},
"_updated_named": {
"type": "updated",
"sub_id": "kol-buys",
"filters": {
"action": "buy",
"min_sol": 5
}
},
"_list": {
"type": "list"
},
"_subscriptions": {
"type": "subscriptions",
"list": [
{
"sub_id": "default",
"channels": [
"deployer:alerts"
],
"filters": {}
},
{
"sub_id": "kol-buys",
"channels": [
"kol:trades"
],
"filters": {
"action": "buy",
"min_sol": 5
}
}
],
"count": 2,
"max": 5
},
"_unsubscribe_named": {
"type": "unsubscribe",
"sub_id": "kol-buys"
},
"_warning_named": {
"type": "warning",
"code": "too_many_subscriptions",
"sub_id": "sixth",
"max": 5,
"tier": "PRO"
},
"_named_subscriptions_note": "Named subscriptions (Phase 2): one socket holds several independent subscriptions, each with its own channels and filters; the default subscription (no sub_id) is unchanged and its frames carry no sub_id. An event matching several subscriptions is delivered once per subscription, each frame stamped sub_id: dedupe by (sub_id, id). Cap per connection, default included: PRO 5 / ULTRA 10 / BUSINESS 20; token:prices mints are capped per connection across subscriptions. Resume per subscription: send every subscribe with the same resume cursor on reconnect; the server serves one replay per sub_id in turn (resume.queued:true on the acks that wait) and holds live frames until the last replay_end; commit at the smallest last_seq / last_ts across them.",
"_subscribe_token_prices": {
"type": "subscribe",
"channels": [
"token:prices"
],
"filters": {
"mints": [
"8vdc...pump",
"9aK2...pump"
]
}
},
"_subscribe_candles": {
"type": "subscribe",
"sub_id": "candles",
"channels": [
"token:candles"
],
"filters": {
"mints": [
"8vdc...pump"
],
"updates": true
}
},
"_event_candle_closed": {
"channel": "token:candles",
"sub_id": "candles",
"event": "candle:closed",
"id": "candle:solana:8vdc...pump:1790157600",
"seq": 184220,
"data": {
"chain": "solana",
"mint": "8vdc...pump",
"bucket_start": "2026-09-23T10:00:00.000Z",
"bucket_end": "2026-09-23T10:01:00.000Z",
"closed_at": "2026-09-23T10:01:13.402Z",
"open_price_usd": 0.0000412,
"high_price_usd": 0.0000431,
"low_price_usd": 0.0000409,
"close_price_usd": 0.0000428,
"close_mc_usd": 42800,
"volume_usd": 1843.2,
"trades": 37,
"final": true,
"source": "token_ohlc_1m"
},
"ts": 1790157673402
},
"_subscribe_risk": {
"type": "subscribe",
"sub_id": "risk",
"channels": [
"token:risk"
],
"filters": {
"mints": [
"8vdc...pump"
]
}
},
"_event_risk_authority": {
"channel": "token:risk",
"sub_id": "risk",
"event": "risk:authority_changed",
"id": "risk:authority_changed:8vdc...pump:freeze_authority:revoked",
"seq": 184221,
"data": {
"chain": "solana",
"mint": "8vdc...pump",
"field": "freeze_authority",
"before": {
"revoked": false
},
"after": {
"revoked": true
},
"observed_at": "2026-09-23T10:02:11.000Z",
"previous_observed_at": "2026-09-23T09:58:40.000Z",
"slot": null,
"source": "token_prices"
},
"ts": 1790157731000
},
"_subscribe_wallet_scores": {
"type": "subscribe",
"sub_id": "scores",
"channels": [
"wallet:scores",
"rhc:wallet_scores"
],
"filters": {
"wallets": [
"7xKX...deployer",
"0x9287...c0de"
]
}
},
"_event_tier_changed": {
"channel": "wallet:scores",
"sub_id": "scores",
"event": "deployer:tier_changed",
"id": "deployer:tier_changed:7xKX...deployer:unranked>rising:81234567",
"seq": 184222,
"data": {
"chain": "solana",
"wallet": "7xKX...deployer",
"tier_before": "unranked",
"tier_after": "rising",
"entered_ranking": true,
"computed_at": "2026-09-23T10:03:02.118Z",
"source": "live_write"
},
"ts": 1790157782118
},
"_connected": {
"type": "connected",
"seq": 184210,
"instance": "6f1a2b3c4d5e",
"replay": {
"oldest_seq": 164210,
"newest_seq": 184210,
"size": 20000
}
},
"_event_kol_trade": {
"id": "kol:trade:5K7j...",
"seq": 184211,
"channel": "kol:trades",
"event": "kol:trade",
"data": {
"kol_name": "Cented",
"action": "buy",
"sol_amount": 5.27,
"token_symbol": "PEPE",
"market_cap_usd_at_trade": 28430,
"price_usd_at_trade": 0.0000284,
"mc_change_pct": {
"5m": 8.2,
"1h": 62.1,
"4h": 87
},
"volume_usd": {
"5m": 42000,
"1h": 340000,
"4h": 920000
},
"mev_volume_pct": {
"1h": 18.4
}
},
"ts": 1712160000000
},
"_event_deployer": {
"id": "deployer:alert:918273",
"seq": 184212,
"channel": "deployer:alerts",
"event": "deployer:alert",
"data": {
"alert_id": 918273,
"tx_signature": "3xQm...",
"deployer_tier": "elite",
"token_symbol": "NEW",
"alert_type": "new_deploy",
"deployer_sol_balance": 42.5301,
"mc_change_pct": {
"5m": 250,
"15m": 480
},
"volume_usd": {
"5m": 12000
}
},
"ts": 1712160000000
},
"_event_graduation": {
"id": "token:graduation:8vdc...pump",
"seq": 184213,
"channel": "token:graduations",
"event": "token:graduation",
"data": {
"token_mint": "8vdc...pump",
"token_symbol": "JUNIE",
"time_to_bond_minutes": 38,
"deployer_wallet": "7vfC...rXs2",
"deployer_tier": "rising",
"market_cap_usd": 24180,
"bonded_at": "2026-06-07T09:00:00Z"
},
"ts": 1712160000000
},
"_channels": [
"kol:trades",
"kol:coordination",
"kol:first_touches",
"deployer:alerts",
"wallet_tracker:events",
"copytrade:signals",
"price_alert:events",
"sniper:deploys",
"token:graduations",
"token:prices",
"token:locks",
"token:fee_claims",
"token:surges",
"rhc:kol_trades",
"rhc:dex_trades",
"rhc:dex_trades_unattributed",
"rhc:new_tokens",
"rhc:copytrade:signals",
"rhc:price_alert:events",
"rhc:kol:coordination",
"rhc:kol:first_touches",
"rhc:token_locks",
"rhc:token_prices",
"rhc:lp_events",
"token:candles",
"rhc:token_candles",
"token:risk",
"rhc:token_risk",
"wallet:scores",
"rhc:wallet_scores"
],
"_event_surge": {
"channel": "token:surges",
"event": "token:surge",
"id": "token:surge:4812",
"seq": 184214,
"data": {
"id": 4812,
"kind": "surge",
"tier": "strong",
"mint": "9kQu...pump",
"symbol": "RUNNER",
"launchpad": "pumpfun",
"fired_at": "2026-08-28T10:00:00Z",
"age_seconds": 450,
"market_cap_usd": 38400,
"liquidity_usd": 9100,
"baseline_mc_usd": 5600,
"baseline_source": "launch",
"mc_multiple": 6.857,
"mc_change_3m_pct": 112.4,
"tape": {
"buys": 96,
"sells": 31,
"buy_volume_usd": 21300,
"unique_buyers": 58,
"wallet_data_available": true
},
"kol": {
"buyers": 2,
"names": [
"alpha_scout",
"degen_dan"
]
},
"early_buyers": {
"count": 20,
"bundled": 0,
"sold": 3,
"sniper_wallets": 1
},
"deployer_tier": "good",
"risk_flags": []
},
"ts": 1788256800000
},
"_event_revival": {
"channel": "token:surges",
"event": "token:revival",
"id": "token:revival:4813",
"seq": 184215,
"data": {
"id": 4813,
"kind": "revival",
"tier": null,
"mint": "8vdc...pump",
"symbol": "GHOST",
"primary_dex": "pumpswap",
"fired_at": "2026-08-28T10:05:00Z",
"dormant_hours": 412.6,
"prev_mc_usd": 22100,
"market_cap_usd": 61000,
"mc_vs_prev_multiple": 2.76,
"peak_mc_usd": 340000,
"pct_of_peak": 17.94,
"tape": {
"buys": 27,
"sells": 4,
"buy_volume_usd": 8900,
"unique_buyers": 19,
"wallet_data_available": true
},
"kol": {
"buyers": 1,
"names": [
"cto_hunter"
]
},
"early_buyers": {
"count": 20,
"bundled": 2,
"sold": 17,
"sniper_wallets": 0
},
"deployer_tier": "moderate",
"risk_flags": [
"early_buyers_exiting"
]
},
"ts": 1788257100000
},
"_surges_filters_note": "token:surges subscribe filters: kinds[] (surge|revival), tiers[] (surges only), launchpads[], exclude_flags[] (drop a frame carrying ANY listed risk flag), min_mc_usd/max_mc_usd, deployer_tier[] (fail-closed: a fire with no known deployer carries deployer_tier null and is dropped by that filter). Identical semantics on the webhook registry events token:surge / token:revival.",
"_channel_gates_note": "30 channels. Solana channel gates: kol:coordination (user-scoped to your own /kol/coordination/alerts rules), kol:first_touches, copytrade:signals, price_alert:events and token:prices are PRO+; sniper:deploys PRO+ (PRO receives the curated elite/good deployers, ULTRA every tier); wallet_tracker:events ULTRA+; kol:trades, deployer:alerts, token:graduations, token:locks, token:fee_claims and token:surges have no channel-level gate beyond the PRO+ stream token. token:prices is MINT-SCOPED — filters.mints is required (max 25 mints on PRO, 100 on ULTRA, 250 on BUSINESS; a subscribe above the cap is rejected, not truncated) and delivers event token:price (~250 ms coalesced per mint). token:locks pushes every new Streamflow / Jupiter Lock / Bonfida lock or vesting contract (event token:lock); token:fee_claims pushes pump.fun fee distributions, social-handle claims and config changes. Lock lifecycle (2026-09-23, opt-in): subscribe token:locks with filters.lifecycle:true to also receive token:lock_claimed, token:lock_cancelled, token:lock_closed, token:lock_updated (change topup | extended | schedule_changed | recipient_changed), token:unlock_upcoming (the lock's next unlock within 24 h) and token:unlock_available (the unlock passed within 30 min: claimable per the schedule, NOT claimed); optional filters events[], unlock_kinds[], mints[] and include_automatic_claims (default false, hides Streamflow keeper-cranked withdrawals, about 90 % of claims). Without lifecycle the channel is unchanged. Events are deduplicated server-side and never emitted for history. Phase 4 (2026-09-23), all PRO+ and SCOPED with their OWN per-connection budget (25 PRO / 100 ULTRA / 250 BUSINESS each, reject never truncate): token:candles (filters.mints; event candle:closed = the stored 1-minute row, id candle:solana:<mint>:<bucket_start s>; filters.updates:true adds candle:update, the minute in progress, ≤ 1 per mint per second, never replayed; fully flat no-trade minutes are not sent), token:risk (filters.mints; risk:authority_changed = mint / freeze authority revoked or Token-2022 fee changed, risk:supply_inflated = first 0.5 % / 5 % supply-drift crossing; a risk:inputs snapshot per mint on subscribe unless risk_snapshot:false; optional risk_events[]; input changes, NOT a risk-band stream) and wallet:scores (filters.wallets, base58; deployer:tier_changed incl. an unranked wallet entering a ranked tier, kol:score_state_changed for is_cold / is_heating_up / auto_strategy_tag; every frame carries source + computed_at = \"recomputed at T\", not \"changed at T\"; optional score_events[]).",
"_rhc_channels_note": "The 14 rhc:* channels are Robinhood Chain (chain id 4663) and ride the SAME socket as the Solana channels — one token, one connection. Payloads are EVM-native (evm_address, token_address, eth_amount, tx_hash), never the Solana field names. rhc:kol_trades (PRO+), rhc:dex_trades (ULTRA+, every attributed swap on the chain), rhc:dex_trades_unattributed (ULTRA+, swaps on pools where neither side is \"the token\" — quote/quote pairs such as WETH/USDG — raw leg data only, no buy/sell action; subscribe to both dex channels for full chain coverage), rhc:new_tokens (ULTRA+, fired once per token the moment its symbol/name/decimals first resolve on-chain, before its first trade lands), rhc:copytrade:signals (PRO+, user-scoped to your own /rhc/copytrade rules), rhc:price_alert:events (PRO+, user-scoped to your own /rhc/price-alerts), rhc:kol:coordination (PRO+, user-scoped to your own /rhc/kol/coordination/alerts), rhc:kol:first_touches (PRO+ broadcast — ULTRA gates only the filtered first-touch SUBSCRIPTION endpoint, not the channel), rhc:token_locks (PRO+ broadcast, one event per token lock or vesting contract created on chain; withdrawals are not tracked; with filters.lifecycle:true also rhc:token_unlock_upcoming / rhc:token_unlock_available, the unlock schedule only, since claims, extensions and cancels are not observable on RHC), rhc:token_prices (PRO+, ADDRESS-SCOPED: filters.addresses is required and capped per connection at 25 PRO / 100 ULTRA / 250 BUSINESS — over the cap is rejected, never truncated; event rhc:token_price: one snapshot per address on subscribe, then ticks derived from the RHC trade feed at most once per address per 250 ms, each with quality fresh | stale | unreliable and quality_reason, price_source, source_pool, liquidity_usd, observed_at and price_age_ms; state stream, no seq/id, a resume gets a fresh snapshot), rhc:lp_events (ULTRA+, RHC liquidity events: add / remove / pool_created on tracked Uniswap v2/v3/v4 pools with in_range + share + material; filters addresses, pools, dexes, actions, material_only, min_share), rhc:token_candles (PRO+, filters.addresses, own cap: rhc:candle_closed, rhc:candle_revised when a stored candle is rewritten — id …:r<n>, latest revision only on replay — and with filters.updates:true rhc:candle_update ≤ 1 per address per second), rhc:token_risk (PRO+, filters.addresses, own cap: rhc:risk_verdict_changed when a recheck stores a different verdict, before/after + checked_at + previous_checked_at — the change happened in that window, days to weeks today; score is higher = safer; rhc:risk_verdict snapshot on subscribe) and rhc:wallet_scores (PRO+, filters.wallets with 0x deployers, own cap: rhc:deployer_tier_changed from the 5-minute mv_rhc_deployers refresh, source matview_refresh). rhc:dex_trade / rhc:dex_trade_unattributed payloads carry additive enrichment since 2026-09-22: amount_in_raw / amount_out_raw as exact decimal strings, token + quote {address, symbol, decimals} (token_in_meta / token_out_meta on the unattributed channel), metadata_status, price_status / price_source / price_observed_at, mc_status, side + side_reason; a KOL trade appears on both rhc:kol_trades and rhc:dex_trades with the same tx_hash + log_index and different ids. Rule-backed RHC channels deliver nothing until you create the matching rule.",
"_event_rhc_token_price": {
"channel": "rhc:token_prices",
"event": "rhc:token_price",
"data": {
"chain": "robinhood",
"address": "0x4a0e...5eea",
"symbol": "SPCX",
"price_usd": 12.41,
"price_native": null,
"market_cap_usd": 1240000,
"liquidity_usd": 88000,
"source_pool": "0x9f1c...02ab",
"source_dex": "uniswap-v4",
"observed_at": "2026-09-22T19:30:14.000Z",
"price_age_ms": 1320,
"quality": "fresh",
"quality_reason": null,
"price_source": "swap_quote",
"tx_hash": "0xab12...",
"log_index": 7,
"block_number": 62110455,
"source": "rhc-dex-stream:trade"
},
"ts": 1758569415320
},
"_event_rhc_token_price_snapshot": {
"channel": "rhc:token_prices",
"event": "rhc:token_price",
"snapshot": true,
"data": {
"chain": "robinhood",
"address": "0x4a0e...5eea",
"symbol": "SPCX",
"price_usd": 12.38,
"price_native": 0.0029,
"market_cap_usd": 1238000,
"liquidity_usd": 88000,
"source_pool": "0x9f1c...02ab",
"source_dex": "uniswap-v4",
"observed_at": "2026-09-22T19:12:03.000Z",
"price_age_ms": 1092000,
"quality": "stale",
"quality_reason": "price_age",
"price_source": "swap_quote",
"tx_hash": null,
"log_index": null,
"block_number": null,
"source": "rhc_token_prices"
},
"ts": 1758568323000
},
"_velocity_note": "Every event carrying a token_mint is enriched with velocity context: mc_change_pct, volume_usd (organic, MEV-stripped), and mev_volume_pct keyed by 5m/15m/1h/2h/4h windows. Window keys are only present when the token has been tracked long enough — branch on history_age_seconds rather than probing for missing keys. Reorg-safe (sourced from CONFIRMED-commitment swap stream).",
"_copytrade_note": "copytrade:signals is user-scoped — only signals from your own rules are delivered. Configure rules via POST /copytrade/subscriptions.",
"_graduation_note": "token:graduations fires for EVERY pump.fun graduation (bonding curve complete → PumpSwap migration), tracked deployer or not — unlike deployer:bond on deployer:alerts, which covers tracked deployers only. deployer_tier is 'unranked' for unknown deployers; the deployer_tier array filter applies. Real-time detection paths only — reconciler-recovered bonds (rare, minutes-stale) are not emitted."
}Normalized real-time trade stream across 14 Solana DEX programs (Pump.fun, Pump AMM, Raydium AMM/CPMM/CLMM, Jupiter v6, Orca Whirlpool, Meteora DBC/DAMM/DLMM, LaunchLab/Bonk.fun, Moonshot) via direct WebSocket. Ultra-only (Business included). 2 concurrent connections per user on Ultra, 5 on Business, up to 10 named subscriptions per connection with independent filters, dynamic updates without disconnect, and optional historical replay (up to 500 trades).
| Name | Type | Default | Description |
|---|---|---|---|
| token | query param | required | Authentication token from POST /stream/token (Ultra tier) |
{
"_subscribe": {
"type": "subscribe",
"sub_id": "fresh-pumpfun",
"replay": 50,
"replay_order": "asc",
"filters": {
"dex": "pumpfun",
"token_age_max_seconds": 300,
"min_sol": 0.5,
"action": "buy"
}
},
"_update": {
"type": "update",
"sub_id": "fresh-pumpfun",
"filters": {
"dex": [
"pumpfun",
"pumpswap"
],
"min_sol": 1
}
},
"_unsubscribe": {
"type": "unsubscribe",
"sub_id": "fresh-pumpfun"
},
"_list": {
"type": "list"
},
"_event": {
"channel": "dex:trades",
"sub_id": "fresh-pumpfun",
"data": {
"wallet": "7vfC...rXs2",
"mint": "EPjF...Dt1v",
"action": "buy",
"sol_amount": 2.5,
"token_amount": 50000,
"tx_signature": "5K7j...",
"block_time": 1712160000,
"program": "6EF8...wF6P",
"dex": "pumpfun",
"dust": false
},
"replay": false,
"ts": 1712160000000
},
"_filters": "At least one targeting filter required. Targeting: token_mint(s) (max 50), wallet(s) (max 50), dex (pumpfun|pumpswap|raydium|jupiter|orca|meteora|launchlab|moonshot — single or array), program (raw program ID), deployer_tier (elite|good|moderate|rising|cold|unranked — single or array), token_age_max_seconds, market_cap_max_sol, market_cap_min_sol. Refinement: min_sol, max_sol, action (buy|sell). min_sol defaults to 0.05 when you don't set it (see _completeness) — the subscribed/updated ack echoes the effective value.",
"_completeness": "2026-09-08: trades below 0.05 SOL (\"dust\" — roughly half of parsed volume, mostly bots sweeping thin/dead pools) used to never reach this stream — not filtered, dropped before broadcast. They now flow through like any other trade, each frame carrying dust:true. filters.min_sol defaults to 0.05 for any subscription that doesn't set it, so existing integrations see zero volume change; set min_sol explicitly (0 for literally everything, or your own floor) to receive dust.",
"_notes": "token_age uses a persisted first-seen lookup (survives restarts). market_cap is computed from the in-memory price × cached supply (1h TTL); the first trade for an unseen mint won't match a market_cap filter while the supply fetch is in flight, but every following trade for that mint will. deployer_tier is null/'unranked' for any mint without a tracked deployer. Replay is served from a server-side buffer (not connection-scoped) holding roughly the last 5-6 minutes of firehose history at typical volume (120k trades, doubled 2026-09-08 to hold the same window now that dust is included) — it backfills trades from before your connection existed. Replayed trades are flagged replay:true and delivered newest-first by default (send replay_order:'asc' with the subscribe for oldest-first); you receive the N most recent trades matching your filters. A replay_done message ALWAYS follows (also when nothing matched): { type:'replay_done', sub_id, count, sent, requested, matched, complete, reason?, note?, order }. The server waits for your socket to drain during the replay, so live trades for the same subscription can arrive interleaved with the replayed ones (which stay marked replay:true) before replay_done; if the socket cannot drain, replay_done says complete:false, reason:'backpressure' and the socket is closed 4008. Firehose frames carry no seq; dedupe by tx_signature.",
"_graduations": "Subscriptions that scope mints via token_mint(s) also receive in-band graduation frames: { channel: 'dex:graduations', sub_id, data: { token_mint, token_symbol, time_to_bond_minutes, deployer_wallet, deployer_tier, market_cap_usd, bonded_at }, ts } — the pump.fun bond lands on the same connection as the trade flow, no separate poll. Subs without mint scoping don't receive them (use the token:graduations channel on /ws/v1/stream for the global feed).",
"_legs_subscribe": {
"type": "subscribe",
"sub_id": "legs",
"legs": true,
"filters": {
"dex": [
"pumpswap",
"raydium"
],
"min_sol": 1
}
},
"_legs_route": {
"channel": "dex:route",
"sub_id": "legs",
"data": {
"signature": "2i8v...ZmQm",
"slot": 449476673,
"block_time": null,
"signer": "7vfC...rXs2",
"hop_count": 2,
"leg_ids": [
"2i8v...ZmQm:7.0",
"2i8v...ZmQm:7.3"
],
"shape": "chain",
"decoded_complete": true,
"undecoded_program_ids": [],
"liquidity_actions": [],
"entry_program": "JUP6...TaV4",
"router_program": "JUP6...TaV4",
"signer_net": [
{
"mint": "So11...1112",
"delta_raw": "-995000000",
"decimals": 9,
"delta_ui": -0.995
},
{
"mint": "Cgxx...pump",
"delta_raw": "1234567890",
"decimals": 6,
"delta_ui": 1234.56789
}
],
"trade_mint": "Cgxx...pump",
"legs_version": 1
},
"ts": 1712160000000
},
"_legs_leg": {
"channel": "dex:leg",
"sub_id": "legs",
"data": {
"leg_id": "2i8v...ZmQm:7.0",
"signature": "2i8v...ZmQm",
"slot": 449476673,
"signer": "7vfC...rXs2",
"leg_index": 0,
"instruction_index": 7,
"inner_index": 0,
"program": "LBUZ...wxo",
"venue": "meteora",
"pool_model": "concentrated",
"pool": "49fU...u6Jd",
"pool_kind": "authority",
"pool_authority": "49fU...u6Jd",
"vault_in": "8k3p...4sx7",
"vault_out": "GySU...VgAg",
"token_in": {
"mint": "So11...1112",
"amount_raw": "995000000",
"decimals": 9,
"amount_ui": 0.995,
"native": false
},
"token_out": {
"mint": "C1MH...",
"amount_raw": "741269499015",
"decimals": 9,
"amount_ui": 741.269499015,
"native": false
},
"side": "buy",
"amount_basis": "transfers",
"extra_mints": 0,
"is_route_member": true,
"route_shape": "chain"
},
"ts": 1712160000000
},
"_legs": "Per-pool swap legs (2026-09-22, opt-in, legs_version 1). Add legs:true to a subscribe (or legs:'only' to drop the legacy frame) and every transaction whose dex:trades frame matches your filters is delivered as one dex:route (the whole path: hop_count, leg_ids, shape none|single|chain|split|parallel|mixed, decoded_complete, undecoded_program_ids, liquidity_actions, signer_net = the wallet-swap) plus one dex:leg per decoded pool swap (one DEX instruction hitting one pool: program, venue, pool + pool_kind state|authority|unresolved, pool_authority, vault_in/vault_out, token_in = what the pool received, token_out = what it paid out, amount_raw as an integer string, amount_basis transfers|vault_delta|ambiguous, side buy|sell|null, is_route_member), then the unchanged dex:trades frame exactly once. leg_id is <signature>:<instruction_index>[.<inner_index>], identical live and on replay; dedupe legs by leg_id. Liquidity deposits and withdrawals are never a leg. Coverage is honest: a route that touched a DEX we do not decode says decoded_complete:false and names the program in undecoded_program_ids; the subscribed ack lists coverage.decodable_programs (pump.fun, PumpSwap, Raydium v4/CPMM/CLMM, Orca Whirlpool, Meteora DBC/DAMM v1/DAMM v2/DLMM, LaunchLab, Moonshot; Jupiter v6 and the Raydium route program are routers whose inner legs are decoded). Measured at build time on 429 real trades: 97.4% of routes complete, every incomplete one an unlisted DEX. Replay emits the same frames with the same ids (replay:true); replay_done is unchanged. Legs never change what mc-tracker, candles, token_trades or any REST aggregate count.",
"_liquidity_subscribe": {
"type": "subscribe",
"sub_id": "lp",
"liquidity": "only",
"filters": {
"dex": [
"raydium",
"pumpswap",
"meteora"
],
"actions": [
"remove"
],
"material_only": true
}
},
"_liquidity_event": {
"channel": "dex:liquidity",
"sub_id": "lp",
"id": "4Rk2...9sQa:3",
"data": {
"id": "4Rk2...9sQa:3",
"instruction_index": 3,
"inner_index": null,
"signature": "4Rk2...9sQa",
"slot": 449480112,
"block_time": 1712160000,
"signer": "9xQe...Vf2L",
"program": "pAMM...EXA",
"venue": "pumpswap",
"action": "remove",
"instruction": "withdraw",
"instruction_kind": "remove",
"event": true,
"event_reason": null,
"pool": "Gf7h...2kLm",
"pool_kind": "authority",
"pool_authority": "Gf7h...2kLm",
"pool_model": "constant_product",
"mints": [
{
"mint": "Cgxx...pump",
"amount_raw": "412000000000",
"decimals": 6,
"side": "out",
"native": false,
"vault": "7Tq1...aa3F"
},
{
"mint": "So11...1112",
"amount_raw": "38100000000",
"decimals": 9,
"side": "out",
"native": false,
"vault": "Bn4d...Qz8e"
}
],
"lp_mint": "Lp9k...4rTs",
"lp_amount_raw": "125000000000",
"lp_kind": "lp_token",
"owner": "9xQe...Vf2L",
"owner_source": "counterparty",
"amount_basis": "transfers",
"single_sided": false,
"reserves_before": [
{
"mint": "Cgxx...pump",
"vault": "7Tq1...aa3F",
"amount_raw": "1210000000000",
"decimals": 6,
"native": false
},
{
"mint": "So11...1112",
"vault": "Bn4d...Qz8e",
"amount_raw": "112000000000",
"decimals": 9,
"native": false
}
],
"reserves_after": [
{
"mint": "Cgxx...pump",
"vault": "7Tq1...aa3F",
"amount_raw": "798000000000",
"decimals": 6,
"native": false
},
{
"mint": "So11...1112",
"vault": "Bn4d...Qz8e",
"amount_raw": "73900000000",
"decimals": 9,
"native": false
}
],
"share_of_reserves": 0.340495,
"depth_effect": {
"basis": "vault_reserves"
},
"material": true,
"lp": "burn",
"liquidity_version": 1
},
"ts": 1712160000000
},
"_liquidity": "Liquidity events (2026-09-23, opt-in, liquidity_version 1). Add liquidity:true to a subscribe (dex:liquidity frames in addition to the trade view) or liquidity:\"only\" (liquidity frames only; cannot be combined with legs). One dex:liquidity frame per liquidity instruction: action pool_created | add | remove, id <signature>:<ix>[.<inner>] (stable live and on replay; dedupe by (sub_id, id)), program, venue, instruction, pool + pool_kind, pool_model, mints[] with raw amount_raw and side in|out relative to the pool, lp_mint / lp_amount_raw / lp_kind, owner (the wallet or router that funded an add or received a remove) + owner_source, amount_basis transfers | vault_delta | ambiguous (ambiguous = amounts null, never guessed), reserves_before[] / reserves_after[] (raw vault balances), share_of_reserves (constant-product pools only), depth_effect, material (a removal of at least 25 % of reserves). Filters: token_mint(s), wallet(s), pool(s), dex, program (one is required with \"only\"), actions, min_share_of_reserves, material_only; trade-only filters such as min_sol never gate a liquidity event. Only instructions proven by a real transaction fixture are emitted (the subscribed ack lists liquidity_coverage.instructions with emits_events); a recognised but unverified instruction, and every fee or reward claim, stays on dex:route.liquidity_actions with event:false. Limits: concentrated pools (Raydium CLMM, Orca Whirlpool, Meteora DLMM) report share_of_reserves null and depth_effect unknown because tick and bin ranges are not decoded; Meteora DAMM v1, Raydium LaunchLab and Moonshot are not emitted; block_time is the receive clock, as on dex:trades; there is no USD field and min_usd is rejected. Recovery: replay from the in-memory buffer only (replay_done adds liquidity.captured_since); there is no durable resume for this view. Never an input to prices, candles or volume."
}List your live WebSocket sessions across both stream servers (event stream + DEX firehose). Reflects in-memory connection state, not historical logs, so every id returned can be passed to DELETE /stream/sessions/{id} to free that slot. Use this when a redeploy or process restart on your side leaves a half-open socket holding a connection slot and new connections are rejected with code 4002 (connection limit reached). Pro/Ultra.
{
"sessions": [
{
"id": "48213",
"service": "ws-streaming",
"tier": "ULTRA",
"channels": [
"kol:trades"
],
"connected_at": "2026-06-29T19:42:11Z",
"remote_ip": "203.0.113.7",
"messages_sent": 142
},
{
"id": "48217",
"service": "dex-stream",
"tier": "ULTRA",
"channels": [
"dex:trades"
],
"connected_at": "2026-06-29T19:43:02Z",
"remote_ip": "203.0.113.7",
"messages_sent": 5310
}
],
"count": 2
}Force-close one of your own live WebSocket sessions and release its connection slot immediately — the self-serve fix for a 4002 lockout after a deploy overlap (e.g. an old + new instance briefly running at once). The {id} is from GET /stream/sessions; you can only terminate your own sessions. The socket is closed with code 4002. Returns 404 if no live session with that id belongs to your key. Pro/Ultra.
| Name | Type | Default | Description |
|---|---|---|---|
| id | path param | required | Session id from GET /stream/sessions |
{
"evicted": true,
"id": "48213"
}Reference
The boring but useful.
Deployer tiers
Bond rate is the headline outcome: percent of launched tokens that graduate the bonding curve.
| Tier | Criteria | Alert bond rate |
|---|---|---|
| Elite | 5+ deploys, 5+ bonds, 40%+ lifetime, 50%+ recent | ~56% |
| Good | 3+ deploys, 3+ bonds, 25%+ lifetime, 30%+ recent | ~17% |
| Rising | 1–3 deploys with 100% bond rate | Varies |
| Moderate | Lower rates, not tracked for alerts | — |
| Cold | Last 10 outcomes all failed | — |
Errors
All errors return JSON with an error string. Rate-limit headers ship on every response.
401 UnauthorizedMissing or invalid API key.
{"error": "Unauthorized"}403 ForbiddenValid key but your tier doesn't have access to this endpoint.
{"error": "This endpoint requires PRO or ULTRA tier"}422 Unprocessable EntityInvalid request parameter (bad wallet address, out-of-range limit, etc.).
{"error": "Invalid wallet address"}429 Too Many RequestsRate limit hit. Check X-RateLimit-Remaining and retry after X-RateLimit-Reset.
{"error": "Rate limit exceeded. Resets at 2026-04-12T15:00:00Z"}500 Internal Server ErrorSomething went wrong on our side. Check /api/v1/status for service health.
{"error": "Internal server error"}Rate-limit headers on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp).
Versioning
Every endpoint lives under /v1. Here is exactly what that promise means.
New endpoints and new optional response fields ship within v1 without prior notice. Write parsers that tolerate unknown fields — that's the only forward-compatibility requirement we place on you.
Within v1, existing response fields never change type or meaning, and documented fields don't disappear. A change that would break a working integration ships as a new endpoint or a new version prefix — never as a mutation of a route you already depend on.
If a v1 endpoint or field must be retired, you get at least 30 days' notice via the changelog and an email to every API key that called it in the preceding 90 days. The endpoint keeps working through the entire window.
Every API change — additive or deprecating — is recorded in the changelog. The machine-readable GET /api/v1/openapi.json spec and GET /api/v1/postman.json collection are regenerated with each release, so codegen pipelines can diff them.
SDKs
TypeScript, Python and Rust for the REST API (the Python client ships in madeonsol-x402 and auto-detects an msk_ key). TypeScript and Python for keyless x402. MCP, ElizaOS and Solana Agent Kit packages for agents. A parallel robinhood-chain-* family covers /rhc/*.
$ npm install madeonsol
$ pip install madeonsol-x402
$ cargo add madeonsol
Authorization: Bearer msk_… on every request. The key is generated at /developer — sign in with email or Google, no payment to start. AI agents that don't have an account can pay per call via /x402.X-RateLimit-Reset so your client can back off precisely. Daily counters reset at midnight UTC.X-MadeOnSol-Signature (HMAC-SHA256 of <timestamp>.<raw-body>). Hash the raw body — NOT the parsed JSON — with your webhook secret and compare with timingSafeEqual. See the payload format section for a Node verifier.delay and as_of.npm i madeonsol) or Rust (cargo add madeonsol). x402 agents: TypeScript or Python (pip install madeonsol-x402) — both ship LangChain and CrewAI toolkits, and the Python package doubles as the keyed REST client (pass api_key="msk_…" and it calls /api/v1 directly). Agents: MCP server, ElizaOS plugin and Solana Agent Kit plugin. Robinhood Chain has its own robinhood-chain-* family of seven packages for /rhc/*.POST /stream/token again returns it unchanged. A 4001 close means the token really is invalid (subscription lapsed, or you rotated it, or your API key was revoked or rotated): mint again and reconnect. Subscriptions are not restored across reconnects: re-send the subscribe message with resume: {instance, seq, ts} taken from the last frame your handler finished processing. The server re-sends what you missed from memory, or, after a server restart, rebuilds it from storage (up to 60 minutes, 2,000 rows per channel), stamped replayed:true and bracketed by replay_start / replay_end, before live delivery resumes; replay_end says per channel what could not be recovered. Delivery is at-least-once, so dedupe by id. Gaps in seq are normal (one counter across all channels and customers) and are not a sign of loss. The older replay_since_seq / replay_since_ts fields still work, from memory only. The DEX firehose uses its own replay: N parameter instead.before cursors on the time-series endpoints to page back through full history. Leaderboard and PnL rankings aggregate selectable windows up to 180 days. For bulk historical backfills email [email protected].Ready when you are
200 calls/day, no payment to start. Pay by card, USDC, or SOL when you outgrow it.