Step 3: Pick which KOLs to follow
The feed covers every tracked wallet. To follow a subset, start from the leaderboard and then filter the feed with kol=:
curl -s "https://madeonsol.com/api/v1/kol/leaderboard?period=7d&limit=3" \
-H "Authorization: Bearer msk_your_key"
{
"leaderboard": [
{
"name": "Fox 🦊",
"wallet": "DAEdBmTPEKM6xkwfzC3d411QUe6coKpkND6UURa4CvHC",
"strategy_tag": "mixed",
"auto_strategy_tag": "scalper",
"pnl": 2815.184207,
"buy_count": 42,
"sell_count": 110,
"volume": 3576.302046,
"win_rate": 60,
"avg_roi": 209.83,
"profit_factor": 67.8617,
"early_entry_pct_30d": 41.53,
"consistency_7d": 62.5,
"median_hold_minutes_30d": 24.7,
"is_heating_up": false,
"is_cold": false,
"percentile_pnl_7d": 100,
"percentile_winrate_7d": 80.09
}
],
"pagination": { "limit": 3, "offset": 0, "returned": 3, "total": 50, "has_more": true },
"period": "7d",
"sort": "pnl"
}
pnl is SOL over the period, profit_factor is gross wins over gross losses, median_hold_minutes_30d tells you whether copying this wallet is even feasible at your latency (24.7 minutes here; scalpers under 3 minutes are not copyable through a 15-second poller). For a single wallet, GET /api/v1/kol/{wallet} returns the same scores plus peer_ranks and the 50 most recent trades. The profit-factor percentile guide goes deeper on selection.
Then narrow the feed:
curl -s "https://madeonsol.com/api/v1/kol/feed?kol=9St6ETbe3CFitw6UNSd8kg7kZ6STXy71wEGiERqQj89U&limit=2" \
-H "Authorization: Bearer msk_your_key"
That returned the wallet's latest two trades, the newest a 1.52 SOL buy of a 3-minute-old token at a $7,642 market cap.
Step 4: Switch to the WebSocket instead of polling
Every feed response carries a stream object pointing at the socket that pushes the same rows. On PRO and above:
import WebSocket from "ws";
const KEY = process.env.MADEONSOL_KEY;
// 1. Mint a stream token (does not expire; the same value is returned on every call)
const { token, ws_url } = await (await fetch("https://madeonsol.com/api/v1/stream/token", {
method: "POST", headers: { Authorization: `Bearer ${KEY}` },
})).json();
// 2. Connect and subscribe — nothing is delivered until you subscribe
const ws = new WebSocket(`${ws_url}?token=${token}`);
ws.on("open", () => ws.send(JSON.stringify({ type: "subscribe", channels: ["kol:trades"] })));
ws.on("message", (raw) => {
const m = JSON.parse(raw);
if (m.channel === "kol:trades") handle(m.data); // m.seq and m.id are your dedupe keys
});
A frame received during the test session:
{
"channel": "kol:trades",
"event": "kol:trade",
"id": "kol:trade:5qKGdtkz1LXnj6GSDQg63fU88b6qhB4Ct5YUbscFAvM2ozFWCk4k7zbRe…",
"seq": 61650562,
"data": {
"kol_name": "",
"wallet_address": "3oApZug9zFHshrer3kJnHPtjSCXrQh2FAib2CzT8Q4KT",
"token_mint": "AfX2Su3aLfYu7dmrKXEu6KyK748YWjSr74e6F3TTWWYe",
"token_name": null,
"token_symbol": null,
"action": "buy",
"sol_amount": 0.31248314099999996,
"token_amount": 5079483.157030722,
"tx_signature": "5qKGdtkz1LXnj6GSDQg63fU88b6qhB4Ct5YUbscFAvM2ozFWCk4k7zbReqTo4TpQscc…",
"slot": 444569826,
"traded_at": "2026-09-05T16:53:49.000Z",
"price_usd_at_trade": 0.000006356110873885258,
"market_cap_usd_at_trade": null,
"fdv_usd_at_trade": null,
"liquidity_usd_at_trade": null,
"primary_dex": null,
"primary_pool_address": null
},
"ts": 1788627229154
}
Notice what the pushed row does not have: the kol_winrate_* and kol_percentile_* scores that ride on the REST rows, and the token fields (token_name, market_cap_usd_at_trade) can be null for a token so new the market-cap tracker has not sampled it yet. The socket optimises for latency, the REST feed for context. The usual pattern is: act on the frame, then enrich with one GET /api/v1/token/{mint} or by reading the KOL's scores once and caching them per wallet. Frames carry seq; on reconnect, subscribe with replay_since_seq set to the last one you processed and the server re-delivers the gap (the last ~20,000 frames, about 10 minutes). Heartbeats arrive every 15 seconds. In a four-minute test window the channel delivered 31 KOL trades.
Pushed events do not count against the daily quota, so a socket replaces a polling loop entirely. The reconnect-loop guide has the full client.
Step 5: Know the tier boundaries before you build
- BASIC (free): the feed, leaderboard and profiles work, 200 calls/day, 60/min, and the live feeds are delayed 5 minutes (
data_age_seconds will show it). Enough to prototype, not enough to trade on.
- PRO (€43/mo): real-time feed, 10,000 calls/day, 300/min, the
kol:trades, kol:first_touches and kol:coordination sockets, webhooks, copy-trade signals.
- ULTRA (€131/mo): 100,000 calls/day, 3 concurrent sockets, the full DEX firehose.
GET /api/v1/me returns your tier and remaining quota, so a client can self-throttle instead of collecting 429s.
FAQ
How do I get Solana KOL trades via API?
Call GET /api/v1/kol/feed with a MadeOnSol msk_ key. Each row is one buy or sell by a tracked KOL wallet with the KOL's name, X handle, win rates and percentile ranks, plus the token and the market cap at trade time. Free keys read it on a 5-minute delay; PRO is real-time.
What is the right way to poll the KOL feed without wasting quota?
Pass the next_since value from each response back as since. You then receive only rows strictly newer than your last one. If count equals your limit, page down with before=next_before before resuming.
Can I get KOL trades pushed instead of polling?
Yes. On PRO and above, mint a token with POST /api/v1/stream/token, connect to wss://madeonsol.com/ws/v1/stream?token=… and send {"type":"subscribe","channels":["kol:trades"]}. Frames arrive within about a second of confirmation and do not count against the daily quota.
Does the WebSocket frame contain the same fields as the REST row?
No. The pushed row has the trade, wallet, token mint and price at trade, but not the KOL's win-rate and percentile scores, and token name / market cap can be null for a brand-new token. Enrich from GET /api/v1/token/{mint} or cache the KOL scores per wallet.
How do I follow only specific KOL wallets?
Pick wallets from GET /api/v1/kol/leaderboard (7d/30d PnL, win rate, profit factor, median hold time) and add kol=<wallet> to the feed call. For your own arbitrary wallet list, use the Wallet Tracker endpoints instead.
How many KOL wallets are tracked?
Over a thousand Solana wallets; the methodology post explains how wallets are sourced and verified. Robinhood Chain KOL trades are a separate feed at GET /api/v1/rhc/kol/feed and the rhc:kol_trades channel.