madeonsol
v2.31.0
Published
Official SDK for the MadeOnSol Solana API — KOL wallet tracking, Pump.fun deployer intelligence, and tool directory
Maintainers
Readme
madeonsol
⭐ Star on GitHub if you find this useful · 📂 Examples · 📚 API docs
Official TypeScript/JavaScript SDK for the MadeOnSol Solana API — zero dependencies, fully typed, works in Node.js ≥ 18 and edge runtimes.
Real-time Solana trading intelligence: track 1,069 KOL wallets with <3s latency on paid keys and x402 pay-per-call (free-tier live feeds are 5-min delayed), score 23,000+ Pump.fun deployers, surface deshred deploy signals ~500ms before on-chain confirmation, detect multi-KOL coordination, score token rug-risk 0–100 with a transparent factor breakdown, expose the bundle cohort that bought a token together and how much of supply it still holds, verify any wallet's current on-chain holdings with airdrop/insider
transfer_deltadetection, push every pump.fun graduation the second it bonds, and stream every DEX trade across 9+ programs. Free tier: 200 requests/day across 40+ endpoints (live feeds 5-min delayed) — no signup payment. Get a key at madeonsol.com/pricing.
New in 2.29.0 — named subscriptions: several independent subscriptions per socket.
subscribe({ subId, channels, filters }),updateSubscription(subId, filters),unsubscribe(subId),getSubscriptions()/listSubscriptions(). Each named subscription has its own channels and filters (the server caps the total per connection, default included: PRO 5, ULTRA 10, BUSINESS 20); frames carryevt.sub_id; an event matching several subscriptions is delivered once per subscription (dedupe per(sub_id, id)). Resume is per subscription with one commit for the connection. The plainsubscribe(channels, filters)API is unchanged. See "Named subscriptions" in the stream section.
New in 2.28.0 — stream recovery: resume cursor, de-duplication, honest gaps. The managed stream now tracks the cursor
{ instance, seq, ts }of the last frame your handlers finished and resumes after it on every reconnect (the v1resumerequest, with an automatic fallback toreplay_since_seq/replay_since_tson older servers). Delivery is at-least-once, de-duplicated by eventid; new lifecycle eventscursor,replay,gap(what could not be recovered — aseqgap is never loss) andfatal. Close codes are handled: 4001 re-fetches the token (bounded), 4002 waits ≥ 60 s instead of looping every second, 4003 stops, 4008 resumes; the backoff resets only after asubscribedack. Every serverwarningframe is emitted (incl.channels_rejected/channels_revoked).STREAM_CHANNELSlists every Solana channel andtoken:pricesjoins theStreamChanneltype. See the stream section's "Recovery" notes.
New in 2.27.0 — deployer reputation as-of a date, and creator-fee rewards.
client.deployerHunter.deployerAsOf(wallet, { date? })(typedDeployerAsOfResponse) bindsGET /deployer-hunter/{wallet}/as-of: the deployer's reputation exactly as it stood ondate(default today, UTC) — the latest write-on-change snapshot at or before it, so a backtest sees only what was knowable then.snapshot.snapshot_datecan predaterequested_date(snapshots are write-on-change);snapshot.carried: truemarks that. No snapshot at or before the date →as_of: false, snapshot: null— nothing is ever synthesized.datemust be ≥ 2026-04-07 and not in the future.client.deployerHunter.deployerRewards(wallet)(typedDeployerRewardsResponse) bindsGET /deployer-hunter/{wallet}/rewards: pump.fun creator-fee rewards, answered two ways that are never merged —collected(what actually reached the wallet: direct vault claims, kept 90 days; social-handle claims; shareholder payouts on any token) andattributed(every payout on the tokens it deployed, splitto_self/to_others+redirected_pct— a deployer redirecting most of its fees elsewhere is visible in the gap between the two). Every money field is{ sol, usdc, usd };usdisnull(never a silent 0) when a SOL amount exists and no SOL price was available.top_tokens/top_recipients(≤10, USD-sorted) show where attributed fees went, recipients flaggedis_self/is_social_pda. Works for non-deployers too (is_deployer: false,attributedempty). PRO+ (BASIC receives HTTP 403) on the keyedmsk_API. New types:DeployerAsOfParams,DeployerAsOfResponse,DeployerAsOfSnapshot,DeployerRewardsResponse,DeployerRewardsMoney,DeployerRewardsRail,DeployerRewardsTopToken,DeployerRewardsTopRecipient,DeployerRewardsSocial.
New in 2.26.0 — token surges & revivals: momentum fires with the honest half attached — one endpoint + one live channel.
client.token.surges(params?)(typedTokenSurgesResponse) bindsGET /tokens/surges:surge= a token < 30 min old whose MC runs hard vs its launch MC (tierearly≤ 10 min / ≥ $12k / ≥ 3×,strong≤ 30 min / ≥ $30k / ≥ 6× and still climbing,breakout≤ 2 min / ≥ $45k / ≥ 8× — each fires once per mint, and only when SUSTAINED across ≥ 10 s, never on a one-tick mark);revival= a token with no trade candle for ≥ 24 h that started trading again, confirmed by real buys + buy volume on the tape, never by the price move alone. Hard gates on both: liquidity ≥ $1.5k and ≥ 2 % of MC, and the MC gained must be paid for (buy volume ≥ 3 % of the move — a spoof-pool mark moves MC on ~$0). Every row carriestape(buys / sells / volume,unique_buyersonly where wallet data exists —wallet_data_available: falseotherwise, never inferred),kol,early_buyers(bundled / sold / sniper wallets),deployerreputation andrisk_flags[](bundled_launch,few_buyers,wash_pattern,thin_liquidity,cold_deployer,sniper_heavy,early_buyers_exiting,sell_pressure,no_tape_trades,no_prior_price,mint_authority_active,transfer_fee); rows ≥ 65 min old carry the +1 houtcome(mc_1h_multiple,peak_1h_multiple,priced_after_1h) andstats: truereturns per-(kind, tier) hit-rates — out-of-sample by construction. Filterskind,tier,mint,launchpad,deployer_tier,min_mc_usd/max_mc_usd,min_buys,exclude_flags,only_clean; cursorssince/before. Pushed live on the newtoken:surgesWS channel (eventstoken:surge/token:revival, typedTokenSurgeStreamEvent, server-side filtersTokenSurgesSubscribeFilters:kinds[],tiers[],launchpads[],exclude_flags[],min_mc_usd/max_mc_usd,deployer_tier[]). Nearly every scalar is| null— null means unknown, never zero. PRO+ (BASIC receives HTTP 403) on the keyedmsk_API.
New in 2.25.1 — stream tokens never expire.
client.stream.getToken()(POST /stream/token) now returns the same token on every call, forever. It stops working only if your subscription lapses or you callclient.stream.getToken({ rotate: true })to replace it (the previous value keeps working for 60 s).StreamToken.expires_atandnext_refresh_atare alwaysnull(kept for wire compatibility — do not schedule refreshes on them); the response gainsrotated: booleanandlifetime: string. The server never rotates on its own and never sendstoken_refreshunless you rotated; a4001close means "mint again" (lapsed or rotated), never a timer. PreferAuthorization: Bearer <token>on the WebSocket handshake —?token=still works and is masked in access logs.client.stream.connect()already does the right thing (it callsgetToken()on every (re)connect); no code change needed on your side.
New in 2.25.0 — token locks & vesting, upcoming unlocks, and pump.fun creator-fee sharing / fee claims — five endpoints + two live channels.
client.token.locks(mint, params?)(typedTokenLocksResponse) bindsGET /tokens/{mint}/locks: every on-chain Streamflow / Jupiter Lock / Bonfida lock or vesting contract on a mint, decoded from the locker programs' account state, with a LIVE-derived view (locked_rawstill locked,unlocked,withdrawn,claimable,status,next_unlock) and asummary(locked / deposited totals, the 7d / 30d forward unlock schedule,active_cancelable_by_sender— a lock the sender can cancel is a weaker promise).client.token.locksFeed(params?)(GET /tokens/locks) is the cross-token feed of NEW contracts, cursor-paginated (pagination.next_since/next_before) and pushed live on the newtoken:locksWS channel (eventtoken:lock, typedTokenLockStreamEvent).client.token.unlocks(params?)(GET /tokens/unlocks) lists upcoming unlock EVENTS (cliff / period / final / tranche) insidewithin=1h…90dwithwindow_amount_*per contract.client.token.feeShares(mint)(GET /tokens/{mint}/fee-shares) decodes a pump.fun coin's on-chainSharingConfig— who its creator fees are redirected to (share_bps,is_admin,is_social_pdafor fees earmarked for an X account etc.,redirected_bps,social_bps,is_default= 100% to the creator) plus the distribution rollup per recipient and the config change log;client.token.feeClaims(params?)(GET /tokens/fee-claims) is the fee-event feed (distributionwith per-addresspayouts[],social_claim,shares_created/updated/reset,creator_transferred,creator_claimon request), pushed live on the newtoken:fee_claimschannel (eventtoken:fee_claim, typedTokenFeeClaimStreamEvent). Honest limits: base-unit amounts are strings and ui / usd / pct arenullwhen decimals or price are unknown; LP locks are NOT included (token / vesting locks only); fee-event history starts 2026-08-17; all five are PRO+ (BASIC receives HTTP 403) on the keyedmsk_API.
New in 2.24.0 — live holder census: exact holder count, labelled holders, and pools that are named, not just excluded.
client.alpha.holders(mint)(typedTokenHoldersResponse) bindsGET /tokens/{mint}/holders(PRO+): every token account of the mint read from the ledger atconfirmedand merged per owner, soconcentration.holder_countis EXACT (distinct non-zero owners minus pools / bonding curves / burns) — never a trade-derived estimate; it isnullonly when the provider refuses the census for a mega-cap, in which case you get the top-20 view andsource.census_fallback_reasonsays so. Each disclosed owner carries our labels (deployer/kol/early_buyer/bundle/bot/dump_cluster— empty means unknown to us, not clean), andexcluded[]NAMES what was taken out of the circulating denominator:reason=pool(withdex+pool_address),bonding_curve(pump.fun / LaunchLab),burn, orprogram_accountonly when we genuinely cannot attribute the PDA;pool_pct/burned_pct/program_pctsplit the exclusion. Amounts are raw u64 strings. Disclosure: PRO ranks 1–10, ULTRA 1–50, BUSINESS 1–100 — the maths is tier-independent. Big tokens take 5–30 s upstream: you get503 holder_scan_in_progresswithretry_after_seconds: 20while the scan finishes into the cache, and the retry is instant.
New in 2.23.0 — two prices on the trade tape, and the right one is now the default. The trade tape now tells you what a trade actually cost.
price_sol/price_usdon each trade are THIS trade's executed price —sol_amount / token_amount, reconciling exactly with the amounts on the same row and with the PnL endpoints. Becausesol_amountis the wallet's net SOL movement, that is the trader's all-in effective rate: swap fee and any account rent included, not the pool mid. The market-cap tracker's canonical pool price moved to the newmarket_price_sol/market_price_usdfields — sampled once per token per pool update, so every trade in the same slot shares it. Until nowprice_solcarried that canonical value and disagreed with the row's own amounts by a 7.9% median (p90 ~74%): a stale market price reads low in a pump and high in a dump, so anything you averaged out of the tape inherited the bias instead of cancelling it. Useprice_solfor cost basis, fills and PnL;market_price_solfor a per-token series independent of trade size and direction. Bothclient.alpha.tokenTrades(mint)andclient.wallet.trades(address)carry all four fields onTokenTrade/WalletTrade— the wallet tape returned amounts and no price at all before.
New in 2.22.0 — Clean stream shutdown.
client.stream.connect().close()now fully tears down the underlying WebSocket so short-lived scripts exit promptly instead of hanging on a lingering socket. In Node the client now prefers thewspackage (which exposesterminate()) and hard-terminates on close; the browser still uses the native WebSocket. No API changes — purely a lifecycle fix. (If you don't already depend onwsand want the fast exit on Node ≥22,npm i ws.)New in 2.21.0 — Pool depth / price-impact + dev self-activity on the risk score.
client.alpha.tokenDepth(mint, { sizes? })(GET /tokens/{mint}/depth, PRO+) answers "how much SOL does it take to move the price N%", per pool. Each depth-computable pool returnsspot_price_sol,fee_pct,source("stream"for constant-product AMMs served zero-RPC from stream reserves,"live_rpc"for pump.fun/bonk curves priced from a live read of the curve's VIRTUAL reserves),reserves_age_ms, per-sizequotes(size_sol/tokens_out/avg_price_sol/price_impact_pct), andto_move_price(SOL to move price1pct/5pct/10pct).sizesaccepts a CSV string ornumber[](max 8, each >0 and ≤10000; default0.5,1,5,10); the response carriessol_usd,sizes_sol,primary_pool, and honesty-firstunsupported_pools— concentrated pools (CLMM/Orca/DLMM), Meteora-DBC curves, and unclassified pools come back with areasoninstead of a wrong number. Impact is per-pool, not router-optimal. Plusclient.alpha.risk(mint)responses gain a top-leveldevblock (TokenRiskDev | null) — deployer self-activity for the mint:wallet,launchpad,deployed_at, create-txbuy_sol/buy_tokens/buy_supply_pct, post-createbought_tokens_after/sold_tokens/sold_solwithfirst_sell_at/last_sell_at, live on-chainholdings_tokens/holdings_supply_pct,wallet_empty(boolean | null), andtransferred_out(boolean | null— chain balance well below the trade-derived expectation, i.e. tokens moved without a swap).devisnullwhen the mint has no tracked deploy row (single-mint/riskonly; absent on batch items). New types:TokenDepthParams,TokenDepthResponse,TokenDepthPool,TokenDepthUnsupportedPool,TokenDepthPoolBase,TokenDepthQuote,TokenDepthToMovePrice,TokenDepthSource,TokenRiskDev.New in 2.20.0 — Wallet batch classify, token trade tape, sniper footprint.
client.wallet.batchClassify(wallets)(POST /wallet/batch/classify, 1–100 addresses, PRO+) returns bulk reputation flags per wallet:is_sniper,is_bundler(lifetime),is_dumper(rolling 42d),is_kol+kol_name,bot_confidence, anddump_clustercohort stats — flags are pump.fun-pipeline scoped (false= not observed, NOT verified clean).client.token.trades(mint, params?)(GET /tokens/{mint}/trades, PRO+) is the mint-scoped trade tape — cursor-paginated raw trades withprice_sol/price_usd/early_buyer_rank/slot, filterable byaction/wallet/since/until, defaulting to the FULL history (starts 2026-04-12; thecoverageblock carrieshistory_start+scope). The wallet profileflagsblock (client.wallet.stats()) gains the sameis_sniper/is_bundler/is_dumper+dump_clusterfields, andbot_confidenceis a type fix: previously typednumber | nullbut the API always returnednulldue to a bug — it now returns the real value as a string enum"none" | "low" | "medium" | "high" | null.TokenRiskInputsgainssniper_footprint(slot-window snipe rollup:buys/buyers/sol/supply_pct/sniper_wallet_buys/data_available/as_of, or null) andclient.sniper.recent()deploys each carry the samefootprintblock. New types:WalletClassification,WalletBatchClassifyResponse,TokenTradesParams,TokenTrade,TokenTradesResponse,TokenTradesCoverage,SniperFootprint,BotConfidence,DumpClusterStats.New in 2.19.0 — Verified wallet holdings.
client.wallet.holdings(address, { limit?, min_value_usd? })(GET /wallet/{address}/holdings) reads the wallet's actual current SPL + Token-2022 token accounts and SOL balance straight from chain, enriches each with our price/MC/name/symbol, and computes atransfer_delta(on-chain amount − trade-derived net position) — exposing tokens that arrived or left without a swap (airdrops, insider funding, wallet-hopping). Distinct fromclient.wallet.positions()(trade-derived FIFO): holdings is "what they actually hold right now". ReturnsWalletHoldingsResponsewith asummary(token_accounts/non_zero/returned/priced/total_value_usd/truncated),sol_balance, andverified_at.limit1–500 (default 200),min_value_usd≥0 (default 0). ULTRA only. New types:WalletHoldingsParams,WalletHoldingsResponse,Holding.New in 2.18.0 — Bundle cohort intelligence.
client.alpha.bundle(mint)(GET /tokens/{mint}/bundle) surfaces the wallets that bought a token together — in one atomic transaction (bundle_kind: "atomic_tx") or the same slot ("same_slot") — and, headline first, how much of supply they still hold. Thebundlesummary block (returned on every tier) carriesheld_pct_of_supply(0–1 of total supply, HEADLINE),bundle_kind,wallet_count,held_ratio,fully_exited,buy_volume, andtokens_held. Tier-gated: BASIC/TRADER get thebundleblock only (wallets: []); PRO adds the top-10 cohort wallets with flags (held_ratio,has_sold,atomic,is_kol); ULTRA returns the full cohort plus identity (kol_name,win_rate,bot_confidence) and per-wallettokens_held. New types:TokenBundleResponse,BundleSummary,BundleWallet,BundleKind.New in 2.17.0 — Batch risk scoring + live stream-session control.
client.token.batchRisk(mints)(POST /tokens/batch/risk, up to 50 mints, counts as 1 request) returns the same transparent 0–100 rug-risk result asclient.alpha.risk(mint)for each mint (withas_of); untracked mints come back as{ mint, error: "not_tracked" }without failing the batch.client.stream.sessions()lists your live WebSocket sessions (ws-streaming+dex-stream) andclient.stream.deleteSession(id)force-closes one to free a slot a ghost socket is holding. PRO/ULTRA only. New types:TokenRiskBatchResponse,TokenRiskBatchItem,TokenRiskBatchError,StreamSession,StreamSessionsResponse,StreamSessionEvictResponse.New in 2.16.0 — Almost-bonded discovery + trending sorts.
client.token.almostBonded({ min_progress, min_velocity_pct_per_min, deployer_tier, sort, limit })— pre-bond pump.fun tokens near graduation, ranked by velocity (Δprogress/min): "95% and accelerating" beats "92% stalled". Each token carriesprogress_pct,velocity_pct_per_min,eta_minutes,stalled,real_sol_reserves,market_cap_usd,liquidity_usd,authorities_revoked,deployer_tier, andage_minutes. PRO/ULTRA only. New types:AlmostBondedParams,AlmostBondedToken,AlmostBondedResponse,AlmostBondedSort. Plusclient.token.list({ sort })gains four momentum sorts —mc_change_5m_desc,mc_change_1h_desc,volume_1h_desc, andtrending(composite recent-volume × positive-momentum rank).New in 2.15.0 — Token flow + deployer SOL balance.
client.alpha.tokenFlow(mint, { window })(GET /tokens/{mint}/flow,window1hdefault or24h, PRO+) returns aggregated buy/sell flow for a token:unique_wallets/unique_buyers/unique_sellers,buy_count/sell_count/total_trades,buy_sol/sell_sol/net_sol(buy − sell), andtrades_per_wallet, plus the windowfromtimestamp. New types:TokenFlowResponse,TokenFlowParams,TokenFlowWindow. Deployer-alert objects (DeployerAlert) now also carrydeployer_sol_balance(the deployer wallet's SOL balance at alert time,number | null).New in 2.14.0 — OHLCV candles + net flow.
client.alpha.candles(mint, { tf, limit, from, to })returns the persisted price/MC trajectory as candlesticks (1m/5m/15m/1h/4h/1d, rolled up on read). PRO: OHLCV (open/high/low/close/volume_usd/trades/market_cap_usd), last 30 days. ULTRA: adds per-bar net flow (buy_volume_usd/sell_volume_usd/net_volume_usd,buy_count/sell_count,volume_mev_usd), liquidity delta (open_liquidity_usd/close_liquidity_usd) and full history —net_flow_includedflags which set you got. New types:Candle,CandlesResponse,CandlesParams,CandleTimeframe.New in 2.13.0 — Token risk score.
client.alpha.risk(mint)returns a transparent 0–100 rug-risk/safety score (higher = riskier) for any token: aband(safe/caution/danger), an explainablefactors[]array (each withkey,label,status,points,detail) that sums into the score, and the rawinputsit was computed from — mint/freeze authority revocation, liquidity USD + liquidity-to-MC ratio, transfer fee bps, Token-2022 flag, burn detection, launch cohort (SOL + size), deployer bond rate + total deployed, KOL signal, and blacklist status. Plusscore_versionandas_of. PRO/ULTRA only. New types:TokenRiskResponse,TokenRiskFactor,TokenRiskInputs,TokenRiskBand,TokenRiskStatus.New in 2.12.0 — Launch cohort, liquidity/MC ratio, deployer tier filter, KOL hold stats, and signal performance.
TokenResponseBody(single + batch) gainsliquidity_to_mc_ratio,launch_cohort_sol, andlaunch_cohort_size.client.token.list()addsmin_liq_mc_ratio,max_liq_mc_ratio, anddeployer_tierfilter params; list items gainliquidity_to_mc_ratioanddeployer_tier.KolLeaderboardEntrygainsmedian_hold_minutes_30dandpercentile_early_entry_30d. New top-level methodclient.getSignalPerformance(name)callsGET /signals/{name}/performance.New in 2.11.1 — Deployer runner-rate fields. Sniper deploys, deployer alerts/profiles, and leaderboard rows now carry
runner_rate(fraction of the deployer's labeled tokens that ran — peak ≥60min after deploy — vs dumped) andlabeled_tokens(confidence denominator; gate on ≥3).New in 2.11 — Graduation events + dump-cluster detection. Subscribe
token:graduationsfor every pump.fun bond in real time — tracked deployer or not — with typedGraduationEventpayloads (mint, deployer tier, time-to-bond, MC at bond). Buyer-qualitybreakdownaddsdump_cluster_count(out-of-sample validated: 3+ such wallets in the first-20 → 94% dump vs 61% base) andrecycled_early_buyer_count(high count with zero cluster leans runner). DEX firehose: replay buffer deepened to ~5 minutes; mint-scoped subs now receive in-banddex:graduationsframes — the bond lands on the same connection as your position's trade flow.
New in 2.9 — Deshred Sniper Alerts.
client.sniper.recent()surfaces new pump.fun deploys reconstructed from shred-level data ~500ms before the chain confirms them — a measured head start over any confirmed-stream feed. PRO sees elite/good deployers; ULTRA sees every tier and maintains a custom deployer watchlist (client.sniper.addToWatchlist()). Use thesniper:deploysWebSocket channel orsniper:deploywebhook for live push instead of polling.New in 2.8 — Price alerts, scout leaderboard, wallet derived stats.
client.priceAlerts.*— CRUD for token MC dip/recovery alerts delivered via webhook or WebSocket (PRO=5, ULTRA=25).client.kol.scoutLeaderboard()— top scouts ranked by first-touch follow-on rate.client.kol.coordinationHistory()andclient.token.peakHistory()expose the historical record.client.wallet.stats()now returns aderivedblock:win_rate,roi,verdict, andbiggest_miss.New in 2.7 — Universal Wallet API.
client.wallet.stats(),client.wallet.pnl(),client.wallet.positions(),client.wallet.trades()— FIFO cost-basis PnL, open positions hydrated with live prices, and cursor-paginated raw trades for any Solana wallet (not just curated KOLs). PRO+. Server-side cache (5min/1h/24h based on activity) — cache hits don't count against your quota.New in 2.6.1 (2026-05-13) — Velocity types fixed. Velocity fields are now correctly typed as
mc_change_pct,volume_usd,mev_volume_pct— each its own object keyed by5m/15m/1h/2h/4h— to match the actual API response. The 2.6.0 shape (velocity[window].mc_change_pct) was wrong; clients reading it would getundefined. Patch is type-only — no runtime breaking changes.New in 2.6.0 (2026-05-12) — Token directory + self-inspection.
client.token.list({ min_liq, min_volume_1h_usd, max_mev_share_pct, mc_change_1h_min_pct, sort, ... })— browse and filter every active mint, with defaultmin_liq=2000to skip phantom-MC dust.client.me()— read your tier, daily/burst quota state, and per-feature usage in one call (no header parsing). Velocity / MEV-share fields added to everyTokenResponseBody:mc_change_pct,volume_usd,mev_volume_pct(each keyed by5m/15m/1h/2h/4h) plushistory_age_secondson the parent./token/{mint}400s now shipcode,reason,received_length,example, anddocsURL — stop guessing why a mint failed. Deprecatedavg_entry_mc_usd/entry_mc_samplesremoved from leaderboard types. All other 2.5.x APIs unchanged.
Build Solana trading bots, analytics dashboards, KOL copy-trading tools, deshred sniper bots, and ecosystem browsers.
Quick start (10 seconds)
npm install madeonsolimport { MadeOnSol } from "madeonsol";
const client = new MadeOnSol({ apiKey: "msk_..." }); // free tier at madeonsol.com/pricing
const { trades } = await client.kol.feed({ limit: 5, action: "buy" });| Feature | Description |
|---|---|
| KOL Tracker | Real-time trade feed, PnL leaderboard with five time windows (today, 7d, 30d, 90d, 180d), coordination detection, per-wallet profiles, and deep PnL analytics for 1,069 tracked KOL wallets. 180 days of trade history retained. |
| Deshred Sniper | Deploy feed reconstructed from shred-level data — surfaces new pump.fun launches ~500ms before on-chain confirmation. PRO: elite/good deployers. ULTRA: all tiers + custom watchlist. Use WebSocket/webhook for live push. |
| Alpha Wallet Intel | Leaderboard of 1M+ scored early-buyer wallets, full wallet profiles, linked-wallet clustering, token cap-table enrichment, and 0–100 buyer quality scores with dump-cluster wallet detection. |
| Token Risk Score | Transparent 0–100 rug-risk/safety score per token with a safe/caution/danger band, explainable factor breakdown, and the raw inputs (authorities, liquidity, transfer fee, launch cohort, deployer bond rate, KOL signal, blacklist). PRO/ULTRA. |
| Bundle Cohort | The wallets that bought a token together (one atomic tx or the same slot) and — headline first — held_pct_of_supply still held, plus held_ratio, fully_exited, and buy volume. Every tier gets the summary; PRO adds top-10 wallet flags; ULTRA adds KOL identity, win rate, bot confidence, and per-wallet balances. |
| Universal Wallet | FIFO cost-basis PnL, open positions (hydrated with live prices), and raw trade history for any Solana wallet — not just curated KOLs. 90-day window, server-side cache. PRO+. |
| Verified Holdings | Current on-chain SPL + Token-2022 balances + SOL, read straight from chain and enriched with price/MC/name, plus a transfer_delta that exposes airdrops / insider funding / wallet-hopping (tokens that moved without a swap). ULTRA. |
| Price Alerts | Token MC dip/recovery alerts delivered via WebSocket or HMAC-signed webhook. PRO: 5 rules, ULTRA: 25. |
| Wallet Tracker | Monitor any Solana wallet for swaps and transfers. Track up to 10/50/100 wallets (Free/Pro/Ultra). Full wallets, counterparties, and tx_signatures on every tier. 120-day event retention. WS events on ULTRA. |
| Deployer Hunter | 23,000+ pump.fun deployers scored by bonding rate — tier leaderboard, deploy alerts, deployer profiles, and best-tokens feed. |
| DEX Trade Stream | Real-time WebSocket stream of ALL Solana DEX trades across 9+ programs — filter by token, wallet, DEX, deployer tier, or trade size. ~5 min replay + in-band graduation frames on mint-scoped subs. ULTRA. |
| Webhooks | Push notifications for KOL trades, coordination signals, deployer alerts, and wallet tracker events (Pro/Ultra) |
| Tool Directory | Search 1,070+ Solana tools and dApps indexed on MadeOnSol |
Links: Full docs · Website · API docs
Authentication
Get a free API key at madeonsol.com/pricing. Keys start with msk_.
Install
npm install madeonsol
# or
yarn add madeonsol
# or
pnpm add madeonsolRequires Node.js ≥ 18 (uses native fetch). Works out of the box in Cloudflare Workers, Vercel Edge, and Bun.
Quick start
import { MadeOnSol } from "madeonsol";
const client = new MadeOnSol({ apiKey: "msk_your_api_key_here" });
// Latest KOL buy trades
const { trades } = await client.kol.feed({ limit: 10, action: "buy" });
console.log(trades[0].kol_name, "bought", trades[0].token_symbol);
// Deshred sniper — ~500ms before on-chain confirmation (PRO/ULTRA)
const { deploys } = await client.sniper.recent({ limit: 20, min_bond_rate: 0.5 });
console.log(deploys[0].token_name, "deployed by", deploys[0].deployer_tier, "tier deployer");
// Multi-KOL coordination signal
const { coordination } = await client.kol.coordination({ min_kols: 3, min_score: 70 });
// FIFO PnL for any wallet (PRO+)
const pnl = await client.wallet.pnl("ASVz...ybJk");
console.log(`Realized: ${pnl.summary.realized_sol} SOL · Win rate: ${(pnl.summary.win_rate! * 100).toFixed(1)}%`);
// Search Solana tools
const { tools } = await client.tools.search({ q: "trading", limit: 10 });Use cases
- Copy-trading bot — stream KOL buys via
client.kol.feed()and mirror trades - Deshred sniper —
client.sniper.recent()or subscribe tosniper:deploysWebSocket for ~500ms pre-confirm deploy signals - DEX trade sniping — subscribe to the all-DEX stream filtered by token, wallet, or deployer tier
- Graduation sniper / position manager — subscribe
token:graduationsfor every pump.fun bond in real time, or hold a mint-scoped firehose sub and get the bond in-band with your position's trade flow - Coordination detector — flag tokens with
client.kol.coordination({ min_kols: 3, min_score: 70 }) - Scout signal — track first-KOL-touch events filtered to S/A-tier scouts via
client.kol.firstTouches({ preset: "scout" }) - Rug-risk gate — score a token with
client.alpha.risk(mint)and skip anything in thedangerband before buying - Bundle-cohort check — call
client.alpha.bundle(mint)and bail whenheld_pct_of_supplyis high (a bundle still sitting on supply can dump) or when the cohort hasn'tfully_exited - Wallet analyser —
client.wallet.pnl()for FIFO cost-basis PnL on any Solana wallet - Holdings verifier / airdrop detector —
client.wallet.holdings()for verified current on-chain balances, and flag tokens with a nonzerotransfer_delta(arrived without a swap — airdrops, insider funding, wallet-hopping) - Price alert bot —
client.priceAlerts.create()for MC dip/recovery alerts delivered via webhook - Analytics dashboard — combine leaderboard, PnL, token velocity, and tool data
- Telegram/Discord bot — pipe alerts via webhooks into chat
- Portfolio tracker — use
client.kol.wallet()to follow specific KOL positions
API Reference
KOL Tracker — client.kol
client.kol.feed(params?)
Live feed of trades made by tracked KOL wallets.
const { trades, count } = await client.kol.feed({
limit: 50, // 1–100, default 50
action: "buy", // "buy" | "sell"
kol: "7xKX...", // filter by specific wallet
});Returns: KolFeedResponse — { trades: KolTrade[], count: number }
Each KolTrade includes market_cap_usd_at_trade and price_usd_at_trade — the token's MC and price at the exact moment the swap fired, sourced from our in-memory price tracker (real-time, faster than Dexscreener spot). Use these to surface "KOL bought $X SOL of token at $Y MC" without a second lookup.
client.kol.leaderboard(params?)
KOL PnL leaderboard ranked by realized profit.
const { leaderboard, period } = await client.kol.leaderboard({
period: "7d", // "today" | "7d" | "30d" | "90d" | "180d", default "7d"
});180-day retention — KOL trade data is retained for 180 days (extended from 31 on 2026-04-07). The 90d and 180d windows fill up over time as the trade table accumulates.
Each KolLeaderboardEntry includes median_hold_minutes_30d (median position hold duration in minutes over the last 30 days) and percentile_early_entry_30d (early-entry percentile rank 0–100 over the last 30 days).
Returns: KolLeaderboardResponse
client.kol.wallet(wallet, params?)
Full profile for a single KOL wallet, including trade history and optional per-token PnL breakdown.
const profile = await client.kol.wallet("7xKX...", {
include: "pnl_by_token",
});Returns: KolWalletProfile
client.kol.coordination(params?)
Detect tokens where multiple KOLs are buying simultaneously — a strong signal of coordinated pumps. v1.1 adds peak-density windows, exit tracking, and a composite 0–100 coordination score.
const { coordination, score_version, window_minutes } = await client.kol.coordination({
period: "24h", // "1h" | "6h" | "24h" | "7d", default "24h"
min_kols: 3, // 2–50, default 3
limit: 20, // 1–50, default 20
window_minutes: 15, // v1.1 — peak-density window in minutes (1–60)
min_score: 60, // v1.1 — filter by composite score (0–100)
include_majors: false, // v1.1 — include WIF/BONK/POPCAT
});
for (const c of coordination) {
console.log(c.token_symbol, "score", c.coordination_score, "peak", c.peak_kols, "exited", c.exited_count);
// c.kols[]: { name, wallet, buy_sol, sell_sol, exited }
}Returns: KolCoordinationResponse — { coordination: CoordinatedToken[], score_version, window_minutes }
client.coordinationAlerts.* (v1.1)
Create real-time push alerts that fire the moment a new coordination cluster forms. Alerts are evaluated per-trade by the signal-evaluator service (sub-second latency), delivered via WebSocket channel kol:coordination and/or HMAC-signed webhook. PRO: 5 rules, ULTRA: 20 rules.
// Create a rule: ≥5 KOLs, 10-min window, score ≥70, webhook delivery
const { rule, webhook_secret } = await client.coordinationAlerts.create({
name: "strong-clusters",
min_kols: 5,
window_minutes: 10,
min_score: 70,
include_majors: false,
cooldown_min: 30, // don't re-fire same token within 30 min
score_jump_break: 15, // UNLESS score jumps by 15+ (catches conviction surges)
delivery_mode: "webhook", // "websocket" | "webhook" | "both"
webhook_url: "https://example.com/coord-hook",
});
// SAVE webhook_secret — used for HMAC-SHA256 signature verification.
await client.coordinationAlerts.list();
await client.coordinationAlerts.get(rule.id);
await client.coordinationAlerts.update(rule.id, { min_score: 80, is_active: false });
await client.coordinationAlerts.delete(rule.id);Webhook signatures: header X-MadeOnSol-Signature = sha256(timestamp + "." + body) with webhook_secret as the HMAC key. Reject deliveries older than ~5 min.
WebSocket delivery: subscribe to channel kol:coordination on wss://madeonsol.com/ws/v1/stream — events are user-scoped (you only receive your own rule fires).
client.priceAlerts.* (new in 2.8)
Sub-second token MC dip/recovery alerts. Set a drop threshold on any token — when MC drops below baseline, a price_alert:dip event fires. Optionally track recovery. PRO: 5 alerts, ULTRA: 25 alerts.
// Create: alert when token drops 20%, then notify when it recovers 15% from the dip low
const { alert, webhook_secret } = await client.priceAlerts.create({
token_mint: "So11111111111111111111111111111111111111112",
drop_pct: 20,
recovery_pct: 15,
name: "SOL dip tracker",
delivery_mode: "webhook",
webhook_url: "https://example.com/dip-hook",
});
await client.priceAlerts.list();
await client.priceAlerts.get(alert.id);
await client.priceAlerts.update(alert.id, { name: "Renamed", is_active: false });
await client.priceAlerts.delete(alert.id);
// Event history (30-day retention)
const { events } = await client.priceAlerts.events({ event_type: "dip", limit: 50 });Alert lifecycle: watching -> dipped -> recovered (terminal). One-shot per alert. Baseline MC captured at creation time. 30-day auto-expiry. Thresholds immutable — delete and recreate to change.
WebSocket: subscribe to channel price_alert:events — user-scoped. Webhook: per-alert HMAC-SHA256 signed (same scheme as coordination alerts).
client.sniper.* — Deshred Sniper Alerts (new in 2.9)
The fastest path to a new pump.fun launch. Deploys are reconstructed from shred-level (deshred) data and surface in the feed ~500ms before the chain confirms them — a measured head start versus any confirmed-stream feed. PRO sees elite + good deployers; ULTRA sees every tier and can keep a custom deployer watchlist.
// Newest-first deshred deploy feed (PRO: elite/good · ULTRA: all tiers)
const { deploys } = await client.sniper.recent({ limit: 50, min_bond_rate: 0.5 });
// Audit one deployer's recent launches (ULTRA)
await client.sniper.byDeployer("7dEx...4pQ8");
// Custom watchlist — get deploys from only the deployers you track, any tier (ULTRA, max 50)
await client.sniper.addToWatchlist({ wallets: ["7dEx...4pQ8", "9aBc...2zZ1"], label: "alpha devs" });
await client.sniper.watchlist();
const { deploys: tracked } = await client.sniper.recent({ watchlist: true });
await client.sniper.removeFromWatchlist("7dEx...4pQ8");Detection is pre-execution, so payloads carry no MC/logs/balances — confirmed_on_chain is "deshred". For live push (not polling), use the sniper:deploy webhook event or the sniper:deploys WebSocket channel. ~1–3% of detected deploys may abandon before settlement.
v2.20 — each deploy also carries a footprint block (SniperFootprint | null): the slot-window snipe rollup for slots [-1..+3] around the deploy — buys, buyers, sol, supply_pct, sniper_wallet_buys, data_available, as_of. null until the ~10-min settle window has passed (or when the mint is outside the pump.fun-pipeline write-gate) — absent, not zero.
client.kol.scoutLeaderboard(params?) (new in 2.8)
Scout leaderboard: top KOLs ranked by scout score, first-touch frequency, and swarm attraction rate. ULTRA only.
const data = await client.kol.scoutLeaderboard({ limit: 20, scout_tier: "S", sort: "scout_score" });client.kol.coordinationHistory(params?) (new in 2.8)
Historical coordination alert fires — past events with token, score, KOL count. ULTRA only.
const data = await client.kol.coordinationHistory({ limit: 50, min_score: 70 });client.token.kolConsensus(mint) (new in 2.8)
KOL consensus on a token: how many bought/sold, exit rate, net flow, median entry MC. ULTRA gets individual wallet arrays.
const consensus = await client.token.kolConsensus("4sVahM4U8js62mQV58ABSkNRhf6Ztc7Xs2LXUznNpump");client.token.peakHistory(mint) (new in 2.8)
Peak MC history: ATH, decline from peak, MC at bond and at 1h/6h/24h/7d after bond.
const peak = await client.token.peakHistory("4sVahM4U8js62mQV58ABSkNRhf6Ztc7Xs2LXUznNpump");client.kol.firstTouches(params?) (new in 2.2)
Recent first-KOL-touch events on tokens — every time a tracked KOL was the first to buy a given mint. Filterable by scout tier (S/A/B/C from the per-KOL mv_kol_scout_score view), KOL winrate, token age, mint suffix, etc.
Backtested signal: top scouts attract ≥3 follow-on KOLs within 4h ~50% of the time vs ~14% baseline (38d / 491k buys / 72,549 events). The full leaderboard is at madeonsol.com/kol/scouts.
// S-tier scouts on tokens younger than 1h
const { events } = await client.kol.firstTouches({
preset: "scout",
min_scout_tier: "S",
limit: 20,
});
for (const e of events) {
console.log(e.first_kol.name, "scouted", e.token_symbol, `(scout_score=${e.first_kol.scout_score}%)`);
}Filter knobs: since, before, limit, kol, min_kol_winrate_7d, min_scout_tier, min_n_touches, strategy, token_age_max_min, min_first_buy_sol, mint_suffix, preset ("scout" or "fresh_launch"), include (e.g. "followers_4h").
Don't poll — push. Median lead time before the second KOL is 12 seconds, so REST polling will lose the swarm. Subscribe to the
kol:first_touchesWebSocket channel (PRO+) or, on Ultra, create an HMAC-signed webhook subscription viaclient.firstTouchSubscriptions.create({...}).
Returns: FirstTouchesResponse
client.firstTouchSubscriptions.* (Ultra)
Create push-delivery rules for first-touch events. Up to 10 active subscriptions per Ultra user.
const { subscription, webhook_secret } = await client.firstTouchSubscriptions.create({
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",
});
// store webhook_secret — shown once
await client.firstTouchSubscriptions.list();
await client.firstTouchSubscriptions.update(subscription.id, { is_active: false });
await client.firstTouchSubscriptions.delete(subscription.id);Same HMAC scheme as coordination alerts. WebSocket channel: kol:first_touches.
client.kol.token(mint)
KOL buy/sell activity for a specific token mint.
const activity = await client.kol.token("EPjFW...");Returns: KolTokenActivity
client.kol.pnl(wallet, params?)
Deep per-wallet PnL breakdown with equity curve, risk metrics, and position history.
const pnl = await client.kol.pnl("7xKX...", {
period: "30d", // "7d" | "30d" | "90d" | "180d", default "30d"
});
// All tiers: summary + equity curve + closed positions
// ULTRA: + open positions (tokens bought but not yet sold)Returns: KolPnlResponse
client.kol.trendingTokens(params?)
Tokens ranked by KOL buy volume across multiple time windows.
const { tokens } = await client.kol.trendingTokens({
period: "1h", // "5m" | "15m" | "30m" | "1h" | "4h" | "8h" | "12h", default "1h"
min_kols: 2, // minimum distinct KOL buyers
limit: 20, // 1–50, default 20
});
// Available on all tiers; ULTRA unlocks full KOL wallet addresses per tokenReturns: KolTrendingTokensResponse
Alpha Wallet Intelligence — client.alpha
client.alpha.leaderboard(params?)
Leaderboard of 1M+ scored early-buyer wallets ranked by win rate, PnL, or ROI.
const { wallets } = await client.alpha.leaderboard({
period: "30d", // "7d" | "30d" | "90d", default "30d"
sort: "win_rate", // "win_rate" | "pnl" | "roi"
min_tokens: 5,
exclude_bots: true,
});
// Up to 100 results on Free/Pro; ULTRA unlocks 500 + bot signalsReturns: AlphaLeaderboardResponse
client.alpha.wallet(wallet)
Full profile for an alpha wallet including per-token history and bot signals. ULTRA only.
const profile = await client.alpha.wallet("7xKX...");Returns: AlphaWalletResponse
client.alpha.linked(wallet)
Linked-wallet clustering — wallets that co-bought with this address within 2 seconds. ULTRA only.
const { linked } = await client.alpha.linked("7xKX...");Returns: AlphaLinkedResponse
client.alpha.capTable(mint)
First buyers for a token enriched with historical win rates, PnL, and KOL identity. PRO/ULTRA.
const { buyers } = await client.alpha.capTable("EPjFW...");Returns: AlphaCapTableResponse
client.alpha.buyerQuality(mint)
0–100 cohort quality score based on the profile of a token's first buyers. All tiers. 5-minute cache.
const { score } = await client.alpha.buyerQuality("EPjFW...");Returns: AlphaBuyerQualityResponse
client.alpha.risk(mint)
Transparent 0–100 token rug-risk/safety score (higher = riskier). Returns a band (safe/caution/danger), an explainable factors[] array that sums into risk_score, and the raw inputs (mint/freeze authority revocation, liquidity USD + liquidity-to-MC ratio, transfer fee bps, Token-2022 flag, burn detection, launch cohort SOL + size, deployer bond rate + total deployed, KOL signal, blacklist status). v2.20: inputs also carries sniper_footprint (SniperFootprint | null) — the slot-window snipe rollup (buys/buyers/sol/supply_pct/sniper_wallet_buys/data_available/as_of). Informational: it does not move the score; null when not yet computed. PRO/ULTRA — BASIC receives HTTP 403.
const { risk_score, band, factors } = await client.alpha.risk("EPjFW...");
if (band === "danger") return; // skip risky tokensReturns: TokenRiskResponse
client.alpha.bundle(mint)
Bundle-cohort holdings — the wallets that bought a token together (one atomic transaction, bundle_kind: "atomic_tx", or the same slot, "same_slot") and, headline first, how much of supply they still hold. The bundle summary block (held_pct_of_supply, bundle_kind, wallet_count, held_ratio, fully_exited, buy_volume, tokens_held) is returned on every tier. BASIC/TRADER get wallets: []; PRO adds the top-10 cohort wallets with flags (held_ratio, has_sold, atomic, is_kol); ULTRA returns the full cohort plus identity (kol_name, win_rate, bot_confidence) and per-wallet tokens_held.
const { bundle, wallets } = await client.alpha.bundle("EPjFW...");
if ((bundle.held_pct_of_supply ?? 0) > 0.2 && !bundle.fully_exited) return; // bundle still holds supplyReturns: TokenBundleResponse
client.alpha.candles(mint, params?)
OHLCV candlestick time-series — the persisted price/MC trajectory, rolled up to any timeframe on read. PRO: OHLCV (last 30 days). ULTRA: + per-bar net flow (buy/sell volume, net_volume_usd, counts, MEV volume), liquidity delta, and full retained history. Params: tf (1m|5m|15m|1h|4h|1d, default 1h), limit (1–1000, default 200), from/to (ISO8601). net_flow_included flags whether the ULTRA fields are populated.
const { candles, net_flow_included } = await client.alpha.candles("EPjFW...", { tf: "5m", limit: 100 });
const last = candles.at(-1);
console.log(last.close, net_flow_included ? `net flow $${last.net_volume_usd}` : "(ULTRA for net flow)");Returns: CandlesResponse
client.alpha.tokenFlow(mint, params?)
Aggregated buy/sell flow for a token over a rolling window. PRO+ (keyed). Params: window (1h default, or 24h). Returns unique wallet/buyer/seller counts, buy/sell counts and SOL volumes, net_sol (buy_sol − sell_sol), and trades_per_wallet, plus the window from timestamp.
const flow = await client.alpha.tokenFlow("EPjFW...", { window: "24h" });
console.log(`${flow.unique_wallets} wallets · net ${flow.net_sol} SOL`);Returns: TokenFlowResponse
client.alpha.tokenPools(mint)
Per-venue liquidity map — every DEX pool a token trades in, each flagged live (is_active) or parked, with liquidity_usd, last_price_sol, last_swap_at, dex, quote_mint, and amm_id. The summary block rolls up pool_count/active_pool_count/dex_count, dexes[], total_liquidity_usd, the primary_pool/primary_dex, and top_pool_share_pct (largest-pool concentration) — a fragmentation read on a token's liquidity. PRO/ULTRA only — BASIC receives HTTP 403.
const { pools, summary } = await client.alpha.tokenPools("EPjFW...");
console.log(`${summary.active_pool_count}/${summary.pool_count} live across ${summary.dex_count} DEXs · top pool ${summary.top_pool_share_pct}%`);Returns: TokenPoolsResponse
client.alpha.holders(mint)
Live holders, holder count + concentration (GET /tokens/{mint}/holders) — a full holder census read from the ledger at confirmed: every token account of the mint (owner + balance), merged per owner. This is who holds now; capTable is who bought first. PRO+ — BASIC receives HTTP 403.
concentration.holder_countis exact (distinct non-zero owners minus excluded pools/curves/burns, atslot) andnullonly when the provider refused the census for a mega-cap mint — thensource.methodis"getTokenLargestAccounts"(top-20 view) andsource.census_fallback_reasonis set. It is never estimated from trades.amount_rawon every holder and excluded row is a raw u64 string — never a float; useBigInt().amountis the UI-scaled convenience number.- Pools, bonding curves, burns and unattributed program accounts are excluded from the circulating denominator and listed in
excluded[], each named where possible:reasonpool(+dex,pool_address),bonding_curve(pump.fun / LaunchLab),burn, elseprogram_account. The #1 raw account of a fresh memecoin is its own bonding curve.concentration.pool_pct/burned_pct/program_pctsplit them (over total supply). - Disclosure is tier-gated: PRO ranks 1–10, ULTRA 1–50, BUSINESS 1–100 (
disclosedtells you your cap);top1/top10/top20/top50/top100_share, the cohort*_pctvalues andholder_countare computed over the full set and are identical on every tier. All shares are 0–100. - Each holder carries
labels[]from MadeOnSol wallet intelligence (deployer/kol/early_buyer/buyer/bundle/bot/dump_cluster) pluskol_name,early_buyer_rank,bot_confidence,historical_win_rate. Empty labels = unknown to us, not verified clean. - Latency: fresh pump.fun mints <1 s; 200k–550k-account tokens 6–11 s. While the upstream scan is still running the API answers 503
error_kind: "holder_scan_in_progress"withretry_after_seconds: 20— the scan keeps going and is cached, so the retry is instant.holder_rpc_unavailable(503,retry_after_seconds: 15) is a fail-closed RPC outage. Both throwMadeOnSolErrorwithstatus === 503; inspecterror.body. Unknown mint: 404error_kind: "not_a_mint".
import { MadeOnSolError } from "madeonsol";
async function holders(mint: string) {
for (;;) {
try {
return await client.alpha.holders(mint);
} catch (e) {
const body = e instanceof MadeOnSolError ? (e.body as { error_kind?: string; retry_after_seconds?: number }) : null;
if (e instanceof MadeOnSolError && e.status === 503 && body?.error_kind === "holder_scan_in_progress") {
await new Promise((r) => setTimeout(r, (body.retry_after_seconds ?? 20) * 1000)); // scan is cached — retry is instant
continue;
}
throw e;
}
}
}
const { holders: top, concentration, excluded } = await holders("EPjFW...");
console.log(`${concentration.holder_count} holders · top10 ${concentration.top10_share}% of circulating`);
console.log(`bonding curve / pools hold ${concentration.pool_pct}% of supply (${excluded.length} excluded owners)`);
console.log(top[0].owner, BigInt(top[0].amount_raw), top[0].labels);Returns: TokenHoldersResponse (TokenHolder, TokenHoldersExcluded, TokenHoldersConcentration, TokenHoldersDeployer, TokenHoldersSource, TokenHolderLabel, TokenHolderExcludedReason, TokenHoldersMethod)
Wallet Tracker — client.walletTracker
client.walletTracker.watchlist()
List your tracked wallets and remaining capacity.
const { wallets, capacity } = await client.walletTracker.watchlist();
// capacity: { used, limit } — Free: 10, Pro: 50, Ultra: 100Returns: WatchlistResponse
client.walletTracker.addToWatchlist(wallet, params?)
Add a wallet to your watchlist. Tracking begins immediately.
await client.walletTracker.addToWatchlist("7xKX...", { label: "whale" });client.walletTracker.removeFromWatchlist(wallet)
Remove a wallet from your watchlist.
await client.walletTracker.removeFromWatchlist("7xKX...");client.walletTracker.updateLabel(wallet, label)
Update the label for a tracked wallet.
await client.walletTracker.updateLabel("7xKX...", "smart money");client.walletTracker.trades(params?)
Historical swap and transfer events for your watched wallets. 120-day retention.
const { events } = await client.walletTracker.trades({
wallet: "7xKX...", // filter by specific wallet
action: "buy", // "buy" | "sell"
event_type: "swap", // "swap" | "transfer"
limit: 50,
before: "2026-04-01T00:00:00Z", // ISO 8601 cursor
});Returns: WalletTrackerTradesResponse
client.walletTracker.summary(params?)
Per-wallet stats across your watchlist: swap counts, SOL bought/sold, last event time.
const { wallets } = await client.walletTracker.summary({
period: "7d", // "24h" | "7d" | "30d", default "7d"
wallet: "7xKX...", // optional: single wallet
});Returns: WalletTrackerSummaryResponse
Universal Wallet — client.wallet (new in 2.7)
Per-wallet endpoints that work on any Solana wallet, not just curated KOLs. FIFO cost-basis PnL over the last 90 days. PRO+ on every method. Results are cached server-side in wallet_analyses with dynamic TTL (5min / 1h / 24h based on last activity); cache hits don't count against your daily quota.
Cost-basis honesty: observable only inside the 90-day data window. Wallets that sold tokens bought before that window have the overflow silently discarded rather than fabricated. notes.cost_basis_observable_from makes the cutoff visible per call.
client.wallet.stats(address)
Aggregate stats over 90d plus cross-product flags (KOL / alpha / deployer). Includes enrichments: top traded tokens with realized PnL, trading style, deployer tier mix, recent trades. v2.8 adds derived block: win rate, ROI, best/worst trade, biggest miss (token sold that later mooned), and AI-classified verdict. v2.20 adds reputation flags to flags: is_sniper, is_bundler (lifetime), is_dumper (rolling 42d), and dump_cluster cohort stats — pump.fun-pipeline scoped, so false means "not observed", NOT verified clean. v2.20 type fix: flags.bot_confidence is a string enum ("none" | "low" | "medium" | "high" | null), not a number — the old number | null typing never matched a real value (the API returned null unconditionally due to a bug, now fixed).
const { stats, flags, derived } = await client.wallet.stats("ASVz...ybJk");
console.log(`${flags.kol_name ?? address}: ${stats?.total_trades} trades`);
if (derived?.verdict) {
console.log(`${derived.verdict.label}: ${derived.verdict.description}`);
}
if (derived?.biggest_miss) {
console.log(`Biggest miss: ${derived.biggest_miss.token_symbol} — missed +${derived.biggest_miss.missed_sol.toFixed(1)} SOL`);
}Returns: WalletStatsResponse (404 if the wallet has no trades and no flag-table presence).
client.wallet.pnl(address)
Full FIFO cost-basis 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 time, win/loss), and open positions hydrated with live current prices from the market-cap tracker.
const pnl = await client.wallet.pnl("ASVz...ybJk");
console.log(`Realized: ${pnl.summary.realized_sol} SOL · Unrealized: ${pnl.summary.unrealized_sol} SOL`);
console.log(`Win rate: ${(pnl.summary.win_rate! * 100).toFixed(1)}% · PF: ${pnl.summary.profit_factor}`);
for (const c of pnl.closed_positions.slice(0, 5)) {
const sign = c.pnl_sol > 0 ? "+" : "";
console.log(` ${c.token_mint.slice(0,8)}… ${sign}${c.pnl_sol} SOL (${c.roi_pct}% ROI, ${c.hold_minutes}m hold)`);
}Returns: WalletPnlResponse. Cache hits include cache_hit: true + computed_at; misses include ttl_seconds.
client.wallet.positions(address)
Open positions only — lighter slice of pnl(). Shares the same cache, so calling this right after pnl() is an immediate hit.
const { positions } = await client.wallet.positions("ASVz...ybJk");
for (const p of positions) {
const u = p.unrealized_sol;
console.log(` ${p.token_mint.slice(0,8)}… cost ${p.cost_basis_sol} SOL unrealized ${u ?? "—"} SOL (${p.unrealized_pct ?? "—"}%)`);
}Returns: WalletPositionsResponse. Mints without a current price return unrealized_sol: null rather than fabricated zero.
client.wallet.holdings(address, params?)
Verified current on-chain holdings — reads the wallet's actual SPL + Token-2022 token accounts and SOL balance straight from chain, enriches each with our price/MC/name/symbol, and computes a transfer_delta (on-chain amount − trade-derived net position). A nonzero transfer_delta exposes tokens that arrived or left without a swap — airdrops, insider funding, wallet-hopping. Distinct from positions() (trade-derived FIFO): holdings is "what they actually hold right now". ULTRA only.
const h = await client.wallet.holdings("ASVz...ybJk", { min_value_usd: 10 });
console.log(`${h.summary.non_zero} tokens · $${h.summary.total_value_usd} · ${h.sol_balance} SOL`);
for (const t of h.holdings) {
const d = t.transfer_delta;
const flag = d && d > 0 ? " ⬅ arrived without a swap" : "";
console.log(` ${t.symbol ?? t.mint.slice(0,8)}… ${t.amount} ($${t.value_usd ?? "—"})${flag}`);
}Params:
limit— 1-500, default 200min_value_usd— number ≥0, default 0 (minimum USD value per holding to include)
Returns: WalletHoldingsResponse — holdings[] (typed Holding), sol_balance, summary (token_accounts / non_zero / returned / priced / total_value_usd / truncated), verified_at, trade_window_days, cache_hit, ttl_seconds.
client.wallet.trades(address, params?)
Cursor-paginated raw trades. Default window is the last 90 days; override via since / until (Unix epoch seconds). Default limit 100, max 500.
let cursor: string | undefined;
while (true) {
const page = await client.wallet.trades("ASVz...ybJk", { limit: 200, cursor, action: "buy" });
for (const t of page.trades) processBuy(t);
if (!page.has_more) break;
cursor = page.next_cursor!;
}Params:
limit— 1-500, default 100cursor— fromnext_cursorof previous responseaction—"buy"or"sell"token_mint— filter to one tokensince/until— Unix epoch seconds (default last 90d)
Returns: WalletTradesResponse with trades[] + next_cursor + has_more + filters echo.
client.wallet.batchClassify(wallets) (new in 2.20 — PRO+)
Bulk wallet reputation flags — 1–100 addresses in one request (POST /wallet/batch/classify). Each entry carries the same flag values as the flags block of stats(): is_sniper, is_bundler, is_dumper, is_kol + kol_name, bot_confidence ("none"/"low"/"medium"/"high" or null), and dump_cluster ({ dump_cohorts, runner_cohorts, total_cohorts, as_of } or null).
Semantics — flags are pump.fun-pipeline scoped: false means the behavior was not observed by our pipeline, NOT that the wallet is verified clean. is_bundler is a lifetime flag; is_dumper is a rolling 42-day window.
const { wallets, as_of } = await client.wallet.batchClassify([buyerA, buyerB, buyerC]);
for (const w of wallets) {
const tags = [w.is_sniper && "sniper", w.is_bundler && "bundler", w.is_dumper && "dumper", w.is_kol && `KOL ${w.kol_name}`].filter(Boolean);
console.log(`${w.address.slice(0, 8)}… ${tags.join(" · ") || "clean-ish (not observed)"} bot=${w.bot_confidence ?? "?"}`);
}Returns: WalletBatchClassifyResponse — { wallets: WalletClassification[], count, as_of }.
Deployer Hunter — client.deployer
client.deployer.stats()
Global statistics across all tracked deployer wallets.
const stats = await client.deployer.stats();
console.log(stats.overall_bonding_rate); // e.g. 0.043Returns: DeployerStats
client.deployer.leaderboard(params?)
Deployers ranked by bonding rate or recent performance.
const { deployers } = await client.deployer.leaderboard({
tier: "elite", // "elite" | "good" | "moderate" | "rising" | "cold"
sort: "bonding_rate", // "bonding_rate" | "recent_bond_rate" | "total_bonded" | "last_deploy_at"
limit: 20, // 1–50, default 20
offset: 0,
});Returns: DeployerLeaderboardResponse
client.deployer.profile(wallet)
Full profile for a single deployer wallet.
const deployer = await client.deployer.profile("3xAB...");
console.log(deployer.tier, deployer.bonding_rate);Returns: DeployerProfile
client.deployer.tokens(wallet, params?)
All tokens deployed by a specific wallet.
const { tokens } = await client.deployer.tokens("3xAB...", {
limit: 20,
offset: 0,
});Returns: DeployerTokensResponse
client.deployer.alerts(params?)
Real-time deploy alerts — fired when a tracked deployer launches a new token.
const { alerts } = await client.deployer.alerts({
since: "2025-01-01T00:00:00Z", // ISO 8601
limit: 20,
tier: "elite", // "elite" | "good" | "moderate" | "rising" | "cold"
offset: 0,
});Each DeployerAlert carries the deploy details plus deployer_sol_balance — the deployer wallet's SOL balance at alert time (number | null when unknown).
Returns: DeployerAlertsResponse
client.deployer.alertStats(params?)
Aggregated alert statistics by tier.
const stats = await client.deployer.alertStats({ period: "7d" });
// "7d" | "30d" | "all", default "all"Returns: DeployerAlertStats
client.deployer.bestTokens(params?)
Top-performing tokens from tracked deployers by peak market cap.
const { tokens } = await client.deployer.bestTokens({
period: "7d", // "7d" | "30d" | "all", default "7d"
limit: 5, // 1–20, default 5
});Returns: BestTokensResponse
client.deployer.recentBonds(params?)
Most recently bonded tokens from tracked deployers.
const { bonds } = await client.deployer.recentBonds({ limit: 20 });Returns: RecentBondsResponse
client.deployer.deployerHistory(wallet, opts?)
Daily reputation time-series for a deployer — one snapshot per date capturing the tier, is_tracked flag, total_deployed/total_bonded, bonding_rate, recent_bond_rate, avg_peak_mc, and best_token_peak_mc that were true on that day. Backtest "was this deployer elite when it launched token X?" without look-ahead bias. opts.limit is 1
