madeonsol-x402
v1.29.0
Published
Official TypeScript SDK for the MadeOnSol Solana API — KOL wallet tracking, Pump.fun deployer intelligence, x402 micropayments, and the all-DEX trade firehose. Free tier at madeonsol.com/pricing.
Maintainers
Readme
madeonsol-x402
📂 Examples · 📚 API docs · 💰 Get a free API key
TypeScript SDK for the MadeOnSol Solana KOL intelligence API.
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, score 1M+ early-buyer wallets (incl. dump-cluster detection), read bundle-cohort holdings (
held_pct_of_supply— are the bundlers still holding?), verify any wallet's current on-chain holdings (with airdrop/insidertransfer_deltadetection), push every pump.fun graduation, and stream every DEX trade. 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 1.29.0 — deployer reputation as-of a date, and creator-fee rewards.
rest.deployerAsOf(wallet, opts?)(typedDeployerAsOfResponse) bindsGET /deployer-hunter/{wallet}/as-of: the deployer's reputation exactly as it stood onopts.date(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 predate the requested date (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.rest.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). 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+, KEYED (v1) only —msk_API key, no x402 route.
New in 1.28.0 — token surges & revivals: momentum fires with the honest half attached.
rest.tokensSurges(params?)(typedTokenSurgesResponse) bindsGET /tokens/surges(PRO+): every token momentum fire, newest first. Two kinds, one row shape.surge— a token < 30 min old whose market cap runs hard against its launch MC, in three tiers that each fire at most once per mint:early(≤10 min, ≥$12k, ≥3× launch),strong(≤30 min, ≥$30k, ≥6× launch and ≥2× the lowest sample of the last 3 min — it is climbing now),breakout(≤2 min, ≥$45k, ≥8×). A tier must be sustained (current tick and a sample ≥10 s older; nothing fires before 20 s of age) — a same-slot bundle marked to $475k at age 1 s is a spike, not a surge.revival— a token with no 1-minute trade candle for ≥24 h that starts trading again, confirmed only by the tape (≥5 buys, ≥$500 buy volume, MC ≥1.5× the pre-dormancy close), never by the price mark;tierisnull. Hard gates on both: liquidity ≥$1.5k and ≥2 % of MC, and the MC gained must be paid for by buy volume (a price mark in a spoof pool moves MC on ~$0). Every row carries the bursttape(sourcecandles / wallet_trades,unique_buyersonly where the mint is in trade coverage —wallet_data_available:falseotherwise, never an inferred zero),kolbuyers, the first-20early_buyerscohort (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— typedTokenSurgeRiskFlag). Rows ≥65 min old carry the +1 houtcome;stats: trueprints per-(kind, tier) hit-rates (up_1h_pct,median_peak_multiple,doubled_1h_pct) — 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; subscribe filterskinds[],tiers[],launchpads[],exclude_flags[],min_mc_usd/max_mc_usd,deployer_tier[]) and accepted by the webhook registry as eventstoken:surge/token:revivalwith the same filters. The response echoes the live thresholds indefinitions. PRO+, KEYED (v1) only —msk_API key, no x402 route.
New in 1.27.1 — stream tokens never expire.
POST /stream/token(rest.getStreamToken()) now returns the same token on every call, forever. It stops working only if your subscription lapses or you callrest.getStreamToken({ rotate: true })to replace it (the previous value keeps working for 60 s).StreamToken.expires_atand the newnext_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.rest.stream()already does the right thing (it callsgetStreamToken()on every (re)connect); no code change needed on your side.
New in 1.27.0 — token locks & vesting, upcoming unlocks, and pump.fun creator-fee sharing / fee claims — five endpoints + two live channels.
rest.tokenLocks(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).rest.tokenLocksFeed(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).rest.tokenUnlocks(params?)(GET /tokens/unlocks) lists upcoming unlock EVENTS (cliff / period / final / tranche) insidewithin=1h…90dwithwindow_amount_*per contract.rest.tokenFeeShares(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;rest.tokenFeeClaims(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+ and KEYED (v1) only —msk_API key, no x402 route.
New in 1.26.0 — live holder census: exact holder count, labelled holders, and pools that are named, not just excluded.
rest.tokenHolders(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. KEYED (v1) — requires anmsk_API key; the census is not on the x402 rail.
New in 1.25.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 — it is 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. Bothrest.tokenTrades(mint)andrest.walletTrades(address)carry all four fields (typed onTokenTrade/WalletTrade) —walletTradesreturned amounts and no price at all before.
New in 1.24.0 — the Deployer Hunter surface completed. Seven new operations that existed on the API but had no SDK binding:
deployerLeaderboard(),deployerStats(),deployerProfile(),deployerTokens(),deployerAlertStats(),deployerBestTokens()anddeployerRecentBonds()(poll it incrementally withnext_since). Readbonding_rate(lifetime) againstrecent_bond_rate(rolling) — the gap between them is the signal, not either number alone.runner_rateonly means something oncelabeled_tokens >= 3, and an untracked wallet returns a profile with zeroed counters, not a 404, so checktotal_deployedbefore reading a 0% bond rate as a track record. Dependency ranges are now bounded to the versions actually tested (@x402/*^2.x,@solana/kit^5.5.1) instead of open-ended>=0.0.1, and the lazily-imported x402 peers are marked optional — a keyed install no longer pulls the whole Solana stack.
New in 1.23.0 — Clean stream shutdown.
rest.stream().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 1.22.0 — Token depth / price impact + deployer self-activity on risk.
rest.tokenDepth(mint, { sizes? })(GET /tokens/{mint}/depth, PRO+) answers "how much SOL moves this token's price N%" — per pool, not router-optimal. Pass up to 8 SOL buysizes(each >0 and ≤10000; default[0.5, 1, 5, 10], sent as a CSVsizesparam); every computable pool returnsspot_price_sol,fee_pct, aquotes[]entry per size (tokens_out,avg_price_sol,price_impact_pct), andto_move_price— the SOL required to move price 1% / 5% / 10%. Constant-product AMMs are served from stream reserves (source: "stream"withreserves_age_ms); pump.fun/bonk curves from a live read of the curve's virtual reserves (source: "live_rpc"). Pools we can't price honestly — concentrated CLMM/Orca/DLMM, Meteora-DBC curves, unclassified models — come back inunsupported_pools[]with areason(e.g.concentrated_liquidity_depth_not_supported,curve_graduated_use_amm_pool) rather than a wrong number;primary_poolnames the deepest computable pool andfound: falsemeans no pools are tracked at all. TypedTokenDepthResponse(+TokenDepthParams,TokenDepthPool,TokenDepthQuote,TokenDepthToMovePrice,TokenDepthUnsupportedPool). KEYED (v1) — requires anmsk_API key; there is no x402 route. Andrest.tokenRisk(mint)now returns a top-leveldevblock (typedTokenRiskDev | null) — the deployer's self-activity on its own mint: the create-tx self-buy snapshot (buy_sol,buy_tokens,buy_supply_pct), the post-create rollup (bought_tokens_after— catches the same-second-separate-tx dev buy the create snapshot reads as 0 —sold_tokens,sold_sol,first_sell_at/last_sell_at), live on-chain holdings (holdings_tokens,holdings_supply_pct— pump.fun 1B denominator, null elsewhere — andwallet_empty: is the dev wallet empty NOW), andtransferred_out(tokens left without a sell;null= unknown when trade coverage or rollup freshness can't prove it — never a guess).devisnullwhen the mint has no pending_deploys row; the response also carriesas_of.deployer:alertwebhook/WS payloads gaindev_buy_sol+dev_buy_supply_pct— the dev's self-buy visible at alert time.New in 1.21.0 — Wallet batch classify, token trade tape, sniper footprint, and 7 new x402-payable endpoints.
rest.walletClassify(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 (typedWalletBatchClassifyResponse) — flags are pump.fun-pipeline scoped (false= not observed, NOT verified clean).rest.tokenTrades(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).rest.tokenTopTraders(mint, params?)andrest.sniperRecent(params?)are new keyed methods too. The wallet profileflagsblock 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,SniperFootprint | null) and sniper deploys each carry the samefootprintblock. x402 catalog grew 18 → 25:tokenCandles($0.01),almostBonded($0.01),tokenTopTraders($0.02),tokenCapTable($0.02),sniperRecent($0.01),tokenFlow($0.01 — the 1.16 keyed-only guard is gone), anddeployerTrajectory($0.01) are now callable on theMadeOnSolX402client with per-request USDC micropayments. New types:WalletClassification,WalletBatchClassifyResponse,TokenTradesParams,TokenTrade,TokenTradesResponse,TokenTopTradersParams,TokenTopTrader,TokenTopTradersResponse,SniperRecentParams,SniperDeploy,SniperRecentResponse,SniperFootprint,DumpClusterStats.New in 1.20.0 — Verified wallet holdings.
rest.walletHoldings(wallet, { limit?, min_value_usd? })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 fromwalletPositions(trade-derived FIFO): holdings is "what they actually hold right now". Returns typedWalletHoldingsResponsewith asummary(token_accounts / non_zero / returned / priced / total_value_usd / truncated) andverified_at. KEYED (v1) — requires anmsk_API key; there is no x402 route. ULTRA only.New in 1.19.0 — Bundle-cohort holdings.
rest.tokenBundle(mint)returns the bundle wallets' current position for a token — the "are the bundlers still holding, or did they dump on you?" read. Thebundleblock carrieswallet_count,bundle_kind(atomic_tx/same_slot/none),held_ratio(net held / buy volume — churn-sensitive secondary),held_pct_of_supply(net held / circulating supply — the headline signal; null when supply is unknown),fully_exited,buy_volume, andtokens_held(typedTokenBundleResponse). Field-gated by tier: BASIC get thebundleblock only (wallets: []); PRO adds the top-10walletswith flags (has_sold,atomic,is_kol); ULTRA returns the full cohort plus per-wallet identity (kol_name,win_rate,bot_confidence,tokens_held). All tiers reach it.New in 1.18.0 — Batch risk scoring + live stream-session control.
rest.tokensBatchRisk(mints)scores up to 50 mints in one call (counts as 1 request) — each entry intokensis either a full risk result (same shape asrest.tokenRisk(mint), plusas_of) or{ mint, error: "not_tracked" }; untracked mints don't fail the batch, andtokenspreserves de-duplicated input order (typedTokenBatchRiskResponse). PRO/ULTRA only. Plusrest.streamSessions()lists your live WebSocket sessions across ws-streaming + dex-stream (typedStreamSessionsResponse), andrest.streamSessionKill(id)force-releases a slot by id (typedStreamSessionEvictResponse) — the self-serve fix for a 4002 lockout when a deploy overlap leaves a ghost socket holding your slot. PRO/ULTRA only.New in 1.17.0 — Almost-bonded discovery + trending sorts.
rest.almostBonded({ min_progress?, max_progress?, min_velocity_pct_per_min?, max_age_minutes?, deployer_tier?, authority_revoked?, min_liq?, sort?, limit? })returns 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(typedAlmostBondedResponse).sortisvelocity_desc(default) /progress_desc/eta_asc. KEYED (v1) — requires anmsk_API key; there is no x402 route. PRO/ULTRA only. Plusclient.tokensList({ 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 1.16.0 — Token trade flow.
client.tokenFlow(mint, { window? })returns a trade-flow aggregate over a1h/24hwindow —unique_wallets/unique_buyers/unique_sellers,buy_count/sell_count/total_trades,buy_sol/sell_sol/net_sol, and atrades_per_walletwash-trading proxy (typedTokenFlowResponse). It's an organic-vs-fake volume read. KEYED (v1) — requires anmsk_API key; there is no x402 route, so x402-only clients can't reach it. PRO/ULTRA only. Deployer alerts now carrydeployers.deployer_sol_balance— the deployer wallet's SOL balance at alert time (null for historical rows).New in 1.15.0 — Live token snapshot + Signal Scorecard.
rest.token(mint)returns a live snapshot — price (USD/SOL), VWAP, market cap, FDV, liquidity, liquidity-to-MC ratio, primary DEX + pool, Token-2022 / transfer-fee flags, and atop_buyers[]array (typedTokenSnapshotResponse).rest.signalPerformance(name, { history? })returns the Signal Scorecard — out-of-sample reliability buckets (hit_rate, base_rate, lift, sample_n, window_days) fordump_cluster_count,runner_rate,recycled_early_buyer_count, orcoordination_count, with a per-dayserieswhenhistory: true(typedSignalPerformanceResponse).rest.signals()is the free catalog of all scored signals (typedSignalsCatalogResponse).rest.tokenRisk(mint)andrest.tokenBuyerQuality(mint)are now fully live server-side.New in 1.13.0 — Token risk score.
rest.tokenRisk(mint)returns a transparent 0–100 rug-risk/safety score (higher = riskier) with aband(safe/caution/danger), an explainablefactors[]array, and the rawinputs(mint/freeze authority, liquidity, liq-to-MC ratio, transfer fee, launch cohort, deployer bond rate, KOL signal, blacklist). Typed asTokenRiskResponse. PRO/ULTRA only.New in 1.12.0 —
/token/{mint}and/token/batchresponses now includeliquidity_to_mc_ratio,launch_cohort_sol, andlaunch_cohort_size./tokensgains three new filter params:min_liq_mc_ratio,max_liq_mc_ratio, anddeployer_tier./tokenslist items now includeliquidity_to_mc_ratioanddeployer_tier./kol/leaderboardentries now includemedian_hold_minutes_30dandpercentile_early_entry_30d.New in 1.11.1 — Deployer profiles now carry
runner_rate+labeled_tokens(fraction of a deployer's labeled tokens that ran vs dumped, gate onlabeled_tokens≥3) plusavg_time_to_bond_minutes, onDeployerAlert.deployersand the deployer-trajectory profile.New in 1.11 — Graduation events + dump-cluster detection. Subscribe
token:graduationsfor every pump.fun bond in real time (tracked deployer or not, typedGraduationEvent). Buyer-qualitybreakdownaddsdump_cluster_count(out-of-sample: 3+ → 94% dump vs 61% base) +recycled_early_buyer_count. DEX firehose: replay buffer deepened to ~5 min; mint-scoped subs get in-banddex:graduationsframes.
New in 1.10 — Deshred Sniper. Deshred deploy feed ~500ms before on-chain confirmation (SDK method
rest.sniperRecent()shipped in 1.21). PRO: elite/good. ULTRA: all tiers + watchlist. Usesniper:deploysWebSocket for push.New in 1.9 — Price alerts, scout leaderboard, coordination history.
rest.priceAlertsCreate()(PRO=5, ULTRA=25).scoutLeaderboard(),kolConsensus(),peakHistory(),coordinationHistory().walletStats()now returnsderived: win_rate, roi, verdict, biggest_miss.New in 1.8 — Universal Wallet API.
rest.walletStats(),rest.walletPnl(),rest.walletPositions(),rest.walletTrades()— FIFO cost-basis PnL for any Solana wallet. PRO+. Cache hits free.New in 1.7.1 (2026-05-13) — Velocity field shape corrected to match the API:
mc_change_pct,volume_usd,mev_volume_pctare top-level on the token response, each keyed by5m/15m/1h/2h/4h. The 1.7.0 README documented avelocity[window]shape that didn't match the wire format. Runtime is unchanged — fix is to typed shape + docs.New in 1.7.0 (2026-05-12) — Token directory + account inspection.
client.tokensList({ min_liq, min_volume_1h_usd, max_mev_share_pct, mc_change_1h_min_pct, sort, min_liq_mc_ratio, max_liq_mc_ratio, deployer_tier, ... })filters every active mint by MC band, liquidity floor, primary DEX, authority/safety flags, computed 1h volume, MEV-share ceiling, MC-change deltas, liq/MC ratio, and deployer tier. Response items now includeliquidity_to_mc_ratioanddeployer_tier. Defaultmin_liq=2000skips phantom-MC dust; passmin_liq=0to opt out.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 every token response:mc_change_pct,volume_usd,mev_volume_pct(each keyed by5m/15m/1h/2h/4h) plushistory_age_seconds./token/{mint}400s now ship structuredcode,reason,received_length,example, anddocs— stop guessing why a mint failed. Deprecatedavg_entry_mc_usdfully removed.
Quick start (10 seconds)
npm install madeonsol-x402import { createClient } from "madeonsol-x402";
const client = createClient("msk_..."); // free tier at https://madeonsol.com/pricing
const { trades } = await client.kolFeed({ limit: 5 });Authentication
Two options:
| Method | Option | Best for |
|---|---|---|
| MadeOnSol API key (recommended) | apiKey | Developers — get a free key |
| x402 micropayments | privateKey | AI agents with Solana wallets |
v1.0 breaking change: RapidAPI auth has been removed. The MadeOnSol RapidAPI marketplace was retired on 2026-04-19. If you were using
rapidApiKey, get a freemsk_key at madeonsol.com/pricing.
Install
npm install madeonsol-x402x402 peer deps (
@x402/fetch @x402/svm @x402/core @solana/kit @scure/base) are only needed when usingprivateKey.
Quick Start
import { createClient } from "madeonsol-x402";
// Option 1: API key — get one free at madeonsol.com/pricing
const client = createClient("msk_your_api_key_here");
// Option 2: x402 micropayments (auto-detected when no msk_ prefix)
// const client = createClient(process.env.SOLANA_PRIVATE_KEY!);
const { trades } = await client.kolFeed({ limit: 10 });
console.log(trades);Advanced initialization
import { MadeOnSolX402 } from "madeonsol-x402";
const client = new MadeOnSolX402({
apiKey: "msk_...", // OR
privateKey: "base58...", // x402 micropayments
});x402 Endpoints (per-request micropayments)
| Method | Description |
|---|---|
| kolFeed(params?) | Real-time KOL trade feed from 1,000+ tracked wallets |
| kolCoordination(params?) | Tokens being accumulated by multiple KOLs simultaneously |
| kolLeaderboard(params?) | KOL performance rankings by PnL and win rate (180 days of trade history) |
| kolPairs(params?) | KOL affinity matrix — which KOLs frequently co-trade the same tokens |
| kolHotTokens(params?) | KOL momentum tokens — accelerating KOL buy interest |
| kolTokenEntryOrder(mint, params?) | Ranked KOL first-buyer order for a token |
| kolCompareWallets({ wallets }) | Side-by-side comparison of 2–5 KOL wallets |
| kolAlertsRecent(params?) | Live KOL alert feed — clusters, fresh-token buys, heating-up wallets |
| deployerAlerts(params?) | Pump.fun deployer alerts with KOL enrichment. PRO/ULTRA: filter by tier. |
| walletStats(address) | New 1.8 · Wallet stats + cross-product flags (is_kol / is_alpha_tracked + bot_confidence / is_deployer). 90-day window. $0.005 |
| walletPnl(address) | New 1.8 · FIFO cost-basis PnL: realized + unrealized SOL, profit factor, drawdown, hold times, daily curve, closed + open positions. $0.02 |
| walletPositions(address) | New 1.8 · Open positions only, live unrealized from market-cap tracker. Shares /pnl cache. $0.01 |
| walletTrades(address, params?) | New 1.8 · Cursor-paginated raw trades with action / token / since-until filters. $0.005 |
| tokenFlow(mint, params?) | Trade-flow aggregate (organic-vs-fake volume) — unique wallets/buyers/sellers, buy/sell counts + SOL, net SOL, trades_per_wallet wash-trading proxy. window ("1h" | "24h", default "1h"). Now x402-payable (1.21). $0.01 |
| tokenCandles(mint, params?) | New 1.21 · OHLCV candles (1m–1d timeframes, 30d history) with per-candle volume, trade count, and market cap. $0.01 |
| almostBonded(params?) | New 1.21 · Launchpad tokens approaching graduation (pump.fun + LetsBonk LaunchLab) — bonding progress, velocity (Δprogress/min), ETA, deployer tier. $0.01 |
| tokenTopTraders(mint, params?) | New 1.21 · Wallets ranked by realized PnL (or ROI) on a token, enriched with KOL identity + alpha reputation. $0.02 |
| tokenCapTable(mint) | New 1.21 · Early-buyer cap table — first 10 non-deployer buyers with PnL, exit status, bundle/KOL/alpha flags + buyer-quality score. $0.02 |
| sniperRecent(params?) | New 1.21 · Deshred sniper deploy feed (elite/good deployers) with per-deploy snipe footprint. $0.01 |
| deployerTrajectory(wallet, params?) | New 1.21 · Deployer bond-rate trajectory — streaks, rolling bond rates, trend, cadence. include: "daily_snapshots" adds 90 days. $0.01 |
| discovery() | Lists all 25 endpoints, prices, and parameter docs (free) |
REST API client
The MadeOnSolREST class exposes the full v1 API (alpha intelligence, token quality, copy-trade rules, wallet tracker, webhooks, streaming). Most endpoints require a Pro or Ultra subscription.
import { MadeOnSolREST } from "madeonsol-x402";
const rest = new MadeOnSolREST({ apiKey: "msk_your_key" });
const { leaderboard } = await rest.alphaLeaderboard({ period: "30d", sort: "win_rate" });
// Rate-limit headers from the most recent response
console.log(rest.lastRateLimit); // { limit, remaining, reset, requestId }Alpha wallet intelligence
Scored from 1M+ early-buyer records (wallets seen in the first 20 buyers of Pump.fun tokens).
| Method | Tier | Description |
|---|---|---|
| rest.alphaLeaderboard(params?) | All | Top profitable wallets. Up to 100 on Free/Pro; ULTRA unlocks 500 + bot signals |
| rest.alphaWallet(wallet) | ULTRA | Full per-token breakdown + bot_signals array |
| rest.alphaLinked(wallet) | ULTRA | Wallets behaviorally linked (co-bought 3+ tokens within 2s) |
alphaLeaderboard params — period ("7d" | "30d" | "all"), min_tokens (1–20), sort ("win_rate" | "pnl" | "roi"), exclude_bots ("true" | "false")
Token quality
| Method | Tier | Description |
|---|---|---|
| rest.token(mint) | All | New 1.15 · Live token snapshot — price (USD/SOL), VWAP, market cap, FDV, liquidity, liq-to-MC ratio, primary DEX + pool, Token-2022 / transfer-fee flags, and top_buyers[]. Returns { token } |
| rest.tokenCapTable(mint) | PRO+ | First non-deployer early buyers, enriched with PnL/KOL/bot flags. PRO=10, ULTRA=20 |
| rest.tokenBuyerQuality(mint) | All | 0–100 buyer-quality score + full breakdown (5-min cached). Live server-side |
| rest.tokenRisk(mint) | PRO+ | Transparent 0–100 rug-risk/safety score with band, explainable factors[], and raw inputs. 1.22: adds a top-level dev block (TokenRiskDev \| null) — deployer self-buy at create, sells rollup, live on-chain holdings, wallet_empty, transferred_out. Live server-side |
| rest.tokenBundle(mint) | All | New 1.19 · Bundle-cohort holdings — bundle block (wallet_count, bundle_kind, held_ratio, headline held_pct_of_supply, fully_exited, buy_volume, tokens_held). BASIC = block only; PRO = top-10 wallets + flags; ULTRA = full cohort + identity fields |
| rest.tokenPools(mint) | PRO+ | New 1.19.2 · Per-venue liquidity map — every DEX pool a token trades in (pool_address, dex, liquidity_usd, last_price_sol, is_active), plus a summary rollup (pool_count, active_pool_count, dex_count, total_liquidity_usd, primary_pool/primary_dex, top_pool_share_pct) |
| rest.tokenDepth(mint, params?) | PRO+ | New 1.22 · Per-pool price impact / slippage — quotes[] per SOL buy size (tokens_out, avg_price_sol, price_impact_pct), to_move_price (SOL to move price 1%/5%/10%), spot_price_sol, fee_pct. Pools we can't price honestly land in unsupported_pools[] with a reason. sizes max 8, default [0.5, 1, 5, 10] |
| rest.tokenHolders(mint) | PRO+ | New · Live holder census + concentration — who holds NOW (vs tokenCapTable = who bought first). concentration.holder_count is EXACT (mint-scoped getProgramAccounts census, merged per owner; null only when the provider refuses a mega-cap → top-20 getTokenLargestAccounts fallback with source.census_fallback_reason — never trade-estimated). Each disclosed owner labelled deployer / kol / early_buyer / bundle / bot / dump_cluster (empty = unknown, not clean). Pools / bonding curves / burns EXCLUDED from the circulating denominator and NAMED in excluded[] (reason: pool + dex + pool_address, bonding_curve, burn, program_account). amount_raw / supply_raw / circulating_raw are raw u64 STRINGS. Disclosure PRO 10 / ULTRA 50 / BUSINESS 100; maths tier-independent. Big tokens: first call may be HTTP 503 holder_scan_in_progress (retry_after_seconds: 20) — the scan continues and is cached, the retry is instant. Keyed only (no x402 route) |
| rest.tokenLocks(mint, params?) | PRO+ | New 1.27 · Token locks & vesting on a mint — every Streamflow / Jupiter Lock / Bonfida vesting contract with a live-derived view (locked_raw still locked, unlocked, withdrawn, claimable, status, next_unlock, cancelable_by_sender) + summary (locked / deposited raw + ui + usd + % of supply, unlocking_7d / unlocking_30d, nearest next_unlock, active_cancelable_by_sender). Params status, program, limit (≤500). Base-unit amounts are STRINGS; ui/usd/pct null when unknown. LP locks NOT included. Keyed only (no x402 route) |
| rest.tokenLocksFeed(params?) | PRO+ | New 1.27 · Cross-token feed of NEW lock / vesting contracts, newest first (same row + token facts). Cursor since = pagination.next_since, before = next_before; filters mint, sender, recipient, program, kind, status, min_usd, min_pct_of_supply, include_estimated (backfilled Jupiter rows). Pushed live on WS token:locks. Keyed only |
| rest.tokenUnlocks(params?) | PRO+ | New 1.27 · Upcoming unlock EVENTS across all active contracts inside within (1h · 6h · 24h · 3d · 7d · 14d · 30d · 90d) — each contract's NEXT cliff / period / final / tranche with amount_* + window_amount_* (total over the window). sort soonest · largest_usd · largest_pct; filters mint, program, kind, min_usd, min_pct_of_supply. Keyed only |
| rest.tokenFeeShares(mint) | PRO+ | New 1.27 · pump.fun creator-fee sharing on a coin — the on-chain SharingConfig (admin, shareholders[] with share_bps / is_admin / is_social_pda + social identity (platform 2 = X, user_id = numeric id, lifetime claimed), redirected_bps, social_bps, is_default = 100% to creator, source stream/chain) + distributions rollup per recipient, past_recipients, history (config changes / creator transfers), recent_distributions. Quote base units as STRINGS. History starts 2026-08-17. Keyed only |
| rest.tokenFeeClaims(params?) | PRO+ | New 1.27 · pump.fun fee-event feed, newest first — distribution (with pro-rata payouts[]), social_claim, shares_created / shares_updated / shares_reset, creator_transferred, creator_claim (only when asked via type). Filters type (comma list), mint, recipient, actor, social_platform, social_user_id, min_sol; cursor since = pagination.next_since. Pushed live on WS token:fee_claims. History starts 2026-08-17. Keyed only |
| rest.tokensSurges(params?) | PRO+ | New 1.28 · Token momentum fires, newest first — kind surge (token < 30 min old vs its LAUNCH MC; tier early ≤10 min ≥$12k ≥3× · strong ≤30 min ≥$30k ≥6× and ≥2× the 3-min low · breakout ≤2 min ≥$45k ≥8×; each once per mint, sustained ≥10 s) or revival (no trade candle ≥24 h, then ≥5 buys / ≥$500 buy volume / ≥1.5× the pre-dormancy MC on the tape — never a price mark; tier null). Each row: burst tape (unique_buyers null outside trade coverage), kol, early_buyers (bundled / sold / sniper), deployer, risk_flags[], and outcome (+1 h MC / peak / low) once ≥65 min old. stats: true = per-(kind, tier) hit-rates over days. Filters kind, tier, mint, launchpad, deployer_tier, min_mc_usd / max_mc_usd, min_buys, exclude_flags (comma list), only_clean; cursors since / before. Pushed live on WS token:surges. Retention 60 d. Keyed only |
| rest.tokensBatchRisk(mints) | PRO+ | New 1.18 · Bulk risk scoring — up to 50 mints in one call (counts as 1 request). Each tokens[] entry is a full risk result or { mint, error: "not_tracked" }; untracked mints don't fail the batch |
| rest.tokenCandles(mint, params?) | PRO+ | OHLC candles. PRO = OHLCV, last 30 days; ULTRA = + net flow (buy/sell volume, net_volume_usd, counts, MEV vol), liquidity delta, full history |
| rest.tokenTrades(mint, params?) | PRO+ | New 1.21 · Mint-scoped trade tape — cursor-paginated raw trades (price_sol/price_usd, early_buyer_rank, slot), filter by action/wallet/since/until. Default window = full history; coverage block carries history_start (2026-04-12) + scope (pump.fun pipeline) |
| rest.tokenTopTraders(mint, params?) | PRO+ | New 1.21 · Wallets ranked by realized PnL (or ROI) on a token — sort ("pnl" | "roi"), window_days (1–180), min_bought_sol; enriched with KOL identity + alpha reputation (bot_confidence, historical win rate/PnL) |
| rest.sniperRecent(params?) | PRO+ | New 1.21 · Deshred sniper deploy feed — PRO sees elite/good deployers, ULTRA all tiers. Each deploy carries a slot-window snipe footprint (buys/buyers/sol/supply_pct/sniper_wallet_buys; null until the ~10-min settle window) |
tokenCandles params — tf ("1m" | "5m" | "15m" | "1h" | "4h" | "1d", default "1h"), limit (1–1000, default 200), from (ISO 8601), to (ISO 8601)
// Score a basket in one request (counts as 1 against quota)
const { tokens, count } = await rest.tokensBatchRisk([mintA, mintB, mintC]);
for (const t of tokens) {
if ("error" in t) console.log(t.mint, t.error); // e.g. "not_tracked"
else console.log(t.mint, t.risk_score, t.band); // full risk result + as_of
}Signal Scorecard (new in 1.15)
Out-of-sample reliability for the scored early-buyer / coordination signals — every claim is backed by a hit-rate vs base-rate measurement so you can size positions on evidence, not vibes.
| Method | Tier | Description |
|---|---|---|
| rest.signals() | All (free) | Catalog of scored signals — name, methodology, and each signal's performance_endpoint. No payment required |
| rest.signalPerformance(name, params?) | All | Signal Scorecard for one signal — buckets[] (hit_rate, base_rate, lift, sample_n, window_days, test_from/test_to) + metric_type, outcome, methodology, as_of. Pass { history: true } for a per-day series[] |
Valid signal names: dump_cluster_count, runner_rate, recycled_early_buyer_count, coordination_count.
const { signals } = await rest.signals();
const scorecard = await rest.signalPerformance("dump_cluster_count", { history: true });
console.log(scorecard.buckets); // [{ bucket, hit_rate, base_rate, lift, sample_n, ... }]KOL coordination alerts (v1.1 — push signals)
Real-time push alerts when a cluster of KOLs co-buys the same token. Fires within ~1s of the triggering trade (pg_notify push, not polling). Delivered via WebSocket (kol:coordination channel, user-scoped) and/or HMAC-signed webhook. PRO=5 rules, ULTRA=20.
// Create a rule
const { rule, webhook_secret } = await rest.coordinationAlertsCreate({
name: "fresh pump cluster",
min_kols: 4, // minimum distinct KOLs in window
window_minutes: 15, // peak-density window (1-60)
min_score: 70, // 0-100 composite score cutoff
include_majors: false, // filter WIF/BONK/POPCAT
cooldown_min: 60, // one fire per (rule,token) per 60min...
score_jump_break: 10, // ...unless score jumps +10 vs last fire
delivery_mode: "both",
webhook_url: "https://you.com/hooks/coord",
});
// → store webhook_secret — shown ONCEcoordinationAlertsList, coordinationAlertsGet(id), coordinationAlertsUpdate(id, params), coordinationAlertsDelete(id) round out the CRUD.
Webhook signature: X-MadeOnSol-Signature: sha256=<hmac> where hmac = HMAC-SHA256(webhook_secret, timestamp + "." + rawBody), and X-MadeOnSol-Timestamp carries the unix seconds used.
The kolCoordination() response now includes v1.1 fields: peak_window_start/end, peak_kols, peak_buys (the busiest slice within the period), exited_count + per-KOL exited flag (net-flow-negative wallets), and coordination_score (0-100). Pass min_score, window_minutes, include_majors to filter.
KOL first-touch signal (new in 1.3)
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.
Backtest: top scouts attract ≥3 follow-on KOLs within 4h ~50% of the time vs ~14% baseline (38d / 491k buys / 72,549 events). Live leaderboard at madeonsol.com/kol/scouts.
import { MadeOnSolREST } from "madeonsol-x402";
const rest = new MadeOnSolREST({ apiKey: process.env.MADEONSOL_API_KEY! });
// S-tier scouts on tokens younger than 1h
const { events } = await rest.firstTouches({ preset: "scout", min_scout_tier: "S" });
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 ("S"|"A"|"B"|"C"), min_n_touches, strategy, token_age_max_min, min_first_buy_sol, mint_suffix ("pump", "bonk", …), preset ("scout"/"fresh_launch"), include ("followers_4h").
Don't poll — push. Median lead time before the second KOL is 12 seconds. REST polling will miss the swarm. Subscribe to the
kol:first_touchesWebSocket channel (PRO+) or, on Ultra, create an HMAC-signed webhook subscription.
Webhook subscriptions (Ultra) — up to 10 active per user, mirrors coordinationAlerts:
const { subscription, webhook_secret } = await rest.firstTouchSubscriptionsCreate({
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 ONCEfirstTouchSubscriptionsList, firstTouchSubscriptionsGet(id), firstTouchSubscriptionsUpdate(id, params), firstTouchSubscriptionsDelete(id) round out the CRUD.
Price alerts (new in 1.9)
CRUD for token dip/recovery price alerts. Fires via WebSocket (price_alert:events channel) and/or HMAC-signed webhook when a token's market cap crosses your threshold. PRO=5 rules, ULTRA=25.
const { alert, webhook_secret } = await rest.priceAlertsCreate({
name: "SOL dip buy",
token_mint: "So11111111111111111111111111111111111111112",
condition: "below", // "below" | "above"
threshold_mc_usd: 5_000_000_000,
cooldown_min: 120,
delivery_mode: "both",
webhook_url: "https://you.com/hooks/price",
});
// → store webhook_secret — shown ONCEpriceAlertsList, priceAlertsGet(id), priceAlertsUpdate(id, params), priceAlertsDelete(id) round out the CRUD.
Scout leaderboard & KOL consensus (new in 1.9)
| Method | Tier | Description |
|---|---|---|
| rest.scoutLeaderboard(params?) | PRO+ | Top scout-tier KOLs ranked by first-touch follow-on rate, win rate, and ROI |
| rest.kolConsensus(params?) | PRO+ | Tokens with the strongest KOL agreement signal — weighted by scout score and recent PnL |
| rest.peakHistory(mint) | PRO+ | Historical peak-density windows for a token — every coordination spike with KOL breakdown |
| rest.coordinationHistory(params?) | PRO+ | Global coordination event log with token, KOL count, score, and outcome |
const { leaderboard } = await rest.scoutLeaderboard({ period: "30d", limit: 25 });
const { tokens } = await rest.kolConsensus({ min_kols: 5, period: "24h" });Wallet derived stats (new in 1.9)
walletStats(address) now includes a stats object with derived fields computed from the 90-day trade window:
const { stats } = await rest.walletStats("WALLET_ADDRESS");
// stats.win_rate — fraction 0-1, tokens sold above cost basis
// stats.roi — aggregate return on invested SOL
// stats.verdict — "strong" | "profitable" | "neutral" | "losing"
// stats.biggest_miss — token with the highest post-exit gain the wallet missedCopy-trade rules
Server-side rules that fire signals when one of your watched source wallets trades. Delivered via webhook (HMAC-signed) and/or WebSocket. PRO=3 rules × 5 source wallets each; ULTRA=20 × 50.
| Method | Description |
|---|---|
| rest.copyTradeList() | List your rules |
| rest.copyTradeCreate(params) | Create a rule. Returns webhook_secret once — store it |
| rest.copyTradeGet(id) | Get one rule |
| rest.copyTradeUpdate(id, params) | Update fields or toggle is_active |
| rest.copyTradeDelete(id) | Delete permanently |
| rest.copyTradeSignals(params?) | Recent fired signals (up to 7 days). Filter by subscription_id, since, limit (1–500) |
Wallet tracker
Per-account watchlist with historical swap/transfer history.
| Method | Description |
|---|---|
| rest.walletTrackerList() | List tracked wallets + remaining capacity |
| rest.walletTrackerAdd(wallet, label?) | Add a wallet |
| rest.walletTrackerRemove(wallet) | Remove a wallet |
| rest.walletTrackerUpdateLabel(wallet, label) | Update label (pass null to clear) |
| rest.walletTrackerTrades(params?) | Historical events. Params: wallet, action, event_type, limit (1–200), before (cursor) |
| rest.walletTrackerSummary(params?) | Per-wallet stats. Params: period ("24h" | "7d" | "30d"), wallet |
| rest.walletStats(address) | New 1.8 · Universal wallet stats (90d) + cross-product flags. PRO+. |
| rest.walletPnl(address) | New 1.8 · Full FIFO PnL + curve + closed/open positions. PRO+. |
| rest.walletPositions(address) | New 1.8 · Open positions only with live unrealized. PRO+. |
| rest.walletTrades(address, params?) | New 1.8 · Cursor-paginated raw trades. Params: limit (1-500), cursor, action, token_mint, since, until. PRO+. |
| rest.walletClassify(wallets) | New 1.21 · Bulk reputation flags for 1–100 wallets in one request — is_sniper / is_bundler (lifetime) / is_dumper (rolling 42d) / is_kol + kol_name / bot_confidence / dump_cluster. Pump.fun-pipeline scoped: false = not observed, NOT verified clean. PRO+. |
Webhooks
| Method | Description |
|---|---|
| rest.createWebhook(params) | Create webhook. Returns secret once — store it for HMAC verification |
| rest.listWebhooks() | List your webhooks |
| rest.getWebhook(id) | Get one + recent delivery log |
| rest.updateWebhook(id, params) | Update URL, events, filters, or re-enable |
| rest.deleteWebhook(id) | Delete |
| rest.testWebhook(id) | Send test payload |
KOL/deployer detail
| Method | Description |
|---|---|
| rest.kolTiming(wallet, params?) | Entry/exit timing — hold duration, exit speed, hour distribution |
| rest.kolPnl(wallet, params?) | Per-wallet PnL breakdown |
| rest.deployerTrajectory(wallet) | Deployer skill curve — streaks, rolling bond rate, trend |
| rest.deployerHistory(wallet, opts?) | New 1.19.2 · PRO+ · Daily reputation time-series — backtest "was this deployer elite when it launched token X?" without look-ahead. snapshots[] carry per-day tier, is_tracked, total_deployed/total_bonded, bonding_rate, recent_bond_rate, avg_peak_mc, best_token_peak_mc. opts.limit (1–365, default 90) |
Streaming token
const token = await rest.getStreamToken();
// token.ws_url — KOL/deployer streaming (Pro/Ultra)
// token.dex_ws_url — all-DEX trade stream (Ultra only)
// token.expires_at — always null: stream tokens never expire (1.27.1)
const fresh = await rest.getStreamToken({ rotate: true }); // replace it; old value works 60 s moreStream tokens never expire: the same token comes back on every call until your subscription lapses or you rotate it. Send it as Authorization: Bearer <token> on the WebSocket handshake (?token= still works). A 4001 close means "mint again", never a timer.
Managed streaming client (new in 1.10)
rest.stream() handles the token fetch (the token never expires — getStreamToken() is called on every (re)connect), auto-reconnect (backoff + jitter), heartbeat liveness, and typed events — just subscribe and listen.
const stream = rest.stream();
stream.on("kol:trade", (t) => console.log(t.token_symbol, t.action));
stream.on("deployer:alert", (a) => console.log("new deploy", a.token_mint));
stream.subscribe(["kol:trades", "deployer:alerts"]);
// stream.unsubscribe([...]) / stream.close() when doneChannels: kol:trades, kol:coordination, kol:first_touches, deployer:alerts, wallet_tracker:events, copytrade:signals, price_alert:events, sniper:deploys, token:graduations (every pump.fun graduation in real time, tracked deployer or not — typed GraduationEvent), token:locks (new 1.27 — event token:lock for every NEW Streamflow / Jupiter Lock / Bonfida lock or vesting contract, typed TokenLockStreamEvent; PRO+; updates are not pushed — poll rest.tokenLocks()), token:fee_claims (new 1.27 — event token:fee_claim for every pump.fun fee event: distributions, social-handle claims, config changes, typed TokenFeeClaimStreamEvent; PRO+), token:surges (new 1.28 — events token:surge (a token < 30 min old running ≥3× / ≥6× / ≥8× its launch MC — tier early / strong / breakout, each once per mint, sustained) and token:revival (≥24 h with no trade candle, then confirmed buys on the tape; tier null), typed TokenSurgeStreamEvent — the same row as rest.tokensSurges() minus outcome, risk_flags[] included; subscribe filters kinds[], tiers[], launchpads[], exclude_flags[], min_mc_usd / max_mc_usd, deployer_tier[]; PRO+). Lifecycle events: open, close, reconnect, heartbeat, error. Uses the global WebSocket on Node 22+; on Node < 22 also npm i ws.
Live stream sessions (new in 1.18)
List and force-release the connection slots your key currently holds across both stream services (ws-streaming + dex-stream). Reflects in-memory state, so every listed slot is evictable — the self-serve fix when a deploy overlap leaves a ghost socket holding your slot and reconnects hit the 4002 connection limit. PRO/ULTRA only.
| Method | Tier | Description |
|---|---|---|
| rest.streamSessions() | PRO+ | List your live sessions — each with id, service, tier, channels[], connected_at, remote_ip, messages_sent. Typed StreamSessionsResponse |
| rest.streamSessionKill(id) | PRO+ | Terminate one of your sessions by id and free its slot. Throws on a bad id (400) or no matching live session (404). Typed StreamSessionEvictResponse |
const { sessions } = await rest.streamSessions();
for (const s of sessions) console.log(s.id, s.service, s.channels, s.messages_sent);
// Free a stuck slot after a deploy overlap
if (sessions.length) await rest.streamSessionKill(sessions[0].id); // { evicted: true, id }DEX Firehose (Ultra)
Connect to dex_ws_url and use the multi-subscription protocol — up to 10 named subs per connection, each with its own sub_id, server-side filters, and optional replay (up to 500 most recent matching trades) from a server-side buffer holding ~5 minutes of firehose history — it backfills trades from before your connection existed. Replayed trades arrive newest-first flagged "replay": true, then a replay_done frame; sort by block_time client-side.
import WebSocket from "ws";
const { token, dex_ws_url } = await rest.getStreamToken();
const ws = new WebSocket(`${dex_ws_url}?token=${token}`); // token MUST be in the query string
ws.on("open", () => {
ws.send(JSON.stringify({
type: "subscribe",
sub_id: "fresh-pumpfun",
replay: 50, // up to 500 from ring buffer
filters: {
dex: "pumpfun", // pumpfun | pumpamm | pumpswap | raydium | jupiter | orca | meteora | launchlab
token_age_max_seconds: 300,
min_sol: 0.5,
action: "buy",
},
}));
});
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.channel === "dex:trades") {
// { sub_id, data: { wallet, mint, action, sol_amount, dex, ... }, replay, ts }
}
});Operations (all carry sub_id): subscribe, update (replace filters in place), unsubscribe, list, ping. Filters: token_mint(s) (≤50), wallet(s) (≤50), dex, program, deployer_tier, token_age_max_seconds, market_cap_min/max_sol, min_sol, max_sol, action. At least one targeting filter is required. Inbound rate limit: 5 messages/sec.
Full protocol reference: madeonsol.com/api-docs#streaming.
Rate-limit headers
Every successful REST response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and X-Request-Id. The SDK exposes them via rest.lastRateLimit:
await rest.alphaLeaderboard();
const { limit, remaining, reset, requestId } = rest.lastRateLimit;
if (remaining !== null && remaining < 5) {
console.warn(`Throttle warning — ${remaining}/${limit} requests left until ${reset}`);
}Discovery
const info = await client.discovery();
console.log(info.endpoints); // all endpoints with prices and paramsDocs: madeonsol.com/solana-api
Also Available
| Platform | Package |
|---|---|
| TypeScript SDK | madeonsol on npm |
| Rust SDK | madeonsol on crates.io |
| Python (LangChain, CrewAI) | madeonsol-x402 on PyPI |
| MCP Server (Claude, Cursor) | mcp-server-madeonsol · Smithery · Glama |
| ElizaOS | @madeonsol/plugin-madeonsol |
| Solana Agent Kit | solana-agent-kit-plugin-madeonsol |
