Solutions · Copy trading
The MadeOnSol copy-trade API checks each trade by the tracked Solana KOL wallets against your rules and emits one copytrade:signal per matching trade, with a suggested SOL size, over a signed webhook, the copytrade:signals WebSocket channel and REST, while the trade itself stays with your code.
Pick the traders from their record, write the conditions once, and receive signals your bot can check, size and execute on its own terms.
Rules and their signals from Pro, over REST, a per-rule webhook or the copytrade:signals channel. A Free key reads the KOL roster and leaderboard, and the KOL feed 5 minutes delayed.
Step one: choose the traders
REST endpoints for deciding which KOL wallets a rule should name.
GET /kol/walletsFreeThe tracked KOL roster. Active wallets here are the only addresses a copy-trade rule can fire on.
wallet_addressis_activestrategy_tag
GET /kol/leaderboardFreeRank KOLs by PnL, win rate, profit factor or early entries over a chosen period.
profit_factor_30dpercentile_pnl_30dwinrate_30d
GET /kol/{wallet}/timingFreeHow long a KOL holds, so you know whether your bot can enter before they exit.
median_hold_minutespct_closed_1h
The problem
Following a wallet sounds like one webhook. A copy-trading product has to decide which traders deserve following, turn each of their transactions into a buy or sell with an amount, drop trades that are too small or entered at the wrong market cap, size the follow, never fire twice on a replayed transaction, and still reach a bot that was offline for a minute.
Show me
The full path for a single trade. Field names are the real request and payload keys; values are illustrative and the frames are abridged.
Name the KOL wallets, a size floor, the side, a market cap band, how to size the follow and where to deliver. The response returns webhook_secret once.
POST /api/v1/copytrade/subscriptions
Authorization: Bearer msk_...
Content-Type: application/json
{
"name": "two kols, early buys",
"source_wallets": ["CyaE...n5Lb", "4BdK...9sQe"],
"min_trade_sol": 1,
"only_action": "buy",
"min_mc_usd": 20000,
"max_mc_usd": 2000000,
"sizing_mode": "proportional",
"sizing_amount": 0.1,
"delivery_mode": "both",
"webhook_url": "https://bot.example.com/hooks/copytrade"
}kol-tracker parses the swap and publishes it on kol:trades with the market cap at the trade. The same event feeds the rule engine.
{
"id": "kol:trade:5K7j...",
"seq": 184219,
"channel": "kol:trades",
"event": "kol:trade",
"data": {
"kol_name": "Cented",
"wallet_address": "CyaE...n5Lb",
"token_mint": "8vdc...pump",
"token_symbol": "NEW",
"action": "buy",
"sol_amount": 5.27,
"token_amount": 18400000,
"tx_signature": "5K7j...",
"slot": 449476688,
"traded_at": "2026-09-23T10:00:35.000Z",
"market_cap_usd_at_trade": 28430,
"liquidity_usd_at_trade": 9120,
"primary_dex": "pumpfun"
},
"ts": 1790157635000
}5.27 SOL clears the floor, the entry sits inside the band, and 0.1 of the KOL's size becomes suggested_sol_amount. A webhook receives the same data inside { event, data, timestamp }.
{
"id": "copytrade:signal:77120",
"seq": 184220,
"channel": "copytrade:signals",
"event": "copytrade:signal",
"data": {
"signal_id": 77120,
"subscription_id": 41,
"subscription_name": "two kols, early buys",
"fired_at": "2026-09-23T10:00:36.120Z",
"source_wallet": "CyaE...n5Lb",
"kol_name": "Cented",
"action": "buy",
"token_mint": "8vdc...pump",
"token_symbol": "NEW",
"source_sol_amount": 5.27,
"suggested_sol_amount": 0.527,
"tx_signature": "5K7j...",
"traded_at": "2026-09-23T10:00:35.000Z",
"market_cap_usd_at_trade": 28430,
"price_usd_at_trade": 0.0000284
},
"ts": 1790157636120
}Handle it on the stream, or verify the webhook signature over timestamp.body with the rule's secret. Everything after this line is your code.
import crypto from "node:crypto";
import { MadeOnSolREST } from "madeonsol-x402";
// Option A: WebSocket (delivery_mode "websocket" or "both")
const rest = new MadeOnSolREST({ apiKey: process.env.MADEONSOL_API_KEY! });
const stream = rest.stream();
stream.on("copytrade:signal", (s) => myBot.consider(s)); // your sizing caps, your signer
stream.subscribe(["copytrade:signals"]);
// Option B: webhook (delivery_mode "webhook" or "both")
function verified(rawBody: string, headers: Record<string, string>): boolean {
const ts = headers["x-madeonsol-timestamp"];
const expected = crypto
.createHmac("sha256", process.env.RULE_WEBHOOK_SECRET!) // returned once at create
.update(`${ts}.${rawBody}`)
.digest("hex");
const got = headers["x-madeonsol-signature"] ?? "";
return got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got));
}
// body: { "event": "copytrade:signal", "data": { ...same fields }, "timestamp": "..." }rest.copyTradeCreate(params)rest.copyTradeSignals({ since })client.copy_trade_create(...)madeonsol_copytrade_createTry it
A free key reads the KOL feed over REST (5 minutes delayed) plus the roster and leaderboard, enough to shortlist wallets and replay what a draft rule would have caught. The KOL Tracker shows the same trades in the browser.
From rule to signal
Each stage reads one part of your rule and one part of the kol:trade event. The first seven run on MadeOnSol; the last one is yours.
01 · MadeOnSol
source_walletsvswallet_addressEvery trade by an active tracked KOL is checked against the rules that list that wallet. A wallet outside the roster produces no kol:trade, so a rule naming it never fires.
02 · MadeOnSol
min_trade_solvssol_amountTrades below your floor are dropped. The default floor is 0.
03 · MadeOnSol
only_actionvsactionbuy, sell or both. A rule created without it follows buys only (buy).
04 · MadeOnSol
min_mc_usdmax_mc_usdvsmarket_cap_usd_at_tradeThe KOL's entry must sit inside your band. When a band is set and the trade's market cap is unknown, no signal fires.
05 · MadeOnSol
sizing_modesizing_amountvssol_amountfixed returns sizing_amount as SOL. proportional and percent_source both multiply the KOL's SOL amount by sizing_amount. The result is suggested_sol_amount.
06 · MadeOnSol
subscription_idvstx_signatureOne signal per rule per transaction. A trade replayed after a reconnect or restart does not fire the rule twice.
07 · MadeOnSol
delivery_modewebhook_urlwebhook posts to your URL signed with the rule's own secret, up to 3 attempts. websocket publishes to copytrade:signals for your connections only. both does both.
08 · Your bot
suggested_sol_amountYour code decides: cap the size, check the token, then build, sign and send the swap with your own wallet and RPC.
Filled chips are rule fields, outlined chips are fields of the KOL trade or the signal. Copy-trade API reference
Compared
A plain address webhook tells you something touched a wallet. A copy-trade signal tells you a tracked trader made a trade your rule accepts. Following addresses outside the KOL roster is what wallet tracking events are for.
| Concern | Generic wallet webhook | MadeOnSol copy-trade signal |
|---|---|---|
| What triggers it | Any transaction that touches the address: transfers, approvals, failed swaps, dust. | A parsed buy or sell by a tracked KOL wallet, with SOL and token amounts.actionsol_amounttoken_amount |
| Who the trader is | An address. Track record is yours to build. | A named KOL with a leaderboard record and hold-time profile you can query before you follow.kol_nameprofit_factor_30dmedian_hold_minutes |
| Qualification | Your service receives everything and filters it. | Size floor, side and market cap band are checked on the server before anything is sent.min_trade_solonly_actionmin_mc_usdmax_mc_usd |
| What you receive | A raw transaction to decode. | A structured signal naming the rule, the source trade, the market cap at entry and a suggested size.subscription_idsuggested_sol_amountmarket_cap_usd_at_trade |
| Duplicates and history | Deduplication and storage are on your side. | One signal per rule per transaction; signals stay readable over REST for 7 days with a delivery flag.tx_signaturedelivereddelivered_at |
Plans
| Step | Where | Plan |
|---|---|---|
| Choose traders | GET /kol/wallets, /kol/leaderboard, /kol/{wallet}/timing | Free |
| KOL trades over REST | GET /kol/feed | Free5 minutes delayed on Free |
| KOL trades live | kol:trades channel | From Pro |
| Copy-trade rules | POST /copytrade/subscriptions | From ProRules and wallets per rule depend on the plan |
| Signals, pushed | Per-rule webhook, copytrade:signals channel | From Pro |
| Signal history | GET /copytrade/signals | From Pro |
| Multi-KOL confirmation | kol:coordination channel | From Pro |
Compare plans for the number of rules and wallets per rule.
Architecture
1 · Source
Chain activity
Transactions and program events
2 · MadeOnSol
Data and intelligence layer
3 · Delivery
4 · Yours
Your caps, checks and signer
Keys and business logic stay server-side
5 · Users
Your copy-trading product
Build outcomes
Showing MadeOnSol data to your own users requires a Business plan. See pricing
Keep going
Solution
Trading Bots
Deployer alerts and token context next to your copy signals.
Solution
Wallet Tracking
Follow addresses that are not on the KOL roster.
Product
KOL Tracker
Live KOL trades and the leaderboard in the browser.
Guide
Choose wallets by profit factor and percentile rank
Selection metrics beyond win rate.
Guide
Backtest a copy-trade strategy on priced KOL trades
Test a rule before it runs live.
Guide
Swap on a KOL buy with Jupiter in TypeScript
The execution layer in your own code.
FAQ
Next step
See how the trades are verified, shortlist KOLs on a free key, then turn the shortlist into a rule.