madeonsol-x402
v4.4.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
Agent Gateway and x402: Current versions of this SDK support the existing documented keyed and keyless data routes. The new six-tool Agent Intelligence Gateway is a separate coordinated release, not an automatically priced client method. A paid composite requires a real route in the source-of-truth price catalog, exact Solana-USDC payment terms and same-proof paid-result recovery. For sustained agent activity, use the appropriate API subscription and preserve each underlying source's tier. See Gateway contract and content/package release checklist.
New in 4.1.0: managed ShredPrism early-stream client (additive).
MadeOnSolREST.earlyStream()plus thewalletTrades()/followTokens()subscription helpers connect to the separate ULTRA/BUSINESS/ENTERPRISE early gateway with the same stream token: reconnect with backoff, separate safe checkpoints per subscription, bounded process-local replay with deduplication, explicit gaps, optionalcompact-v1frames (expanded before your callback) and private saved wallet list controls (manageWalletList()). It only connects whengetStreamToken()advertisesearly_ws_url, and only the channels listed inearly_stream.channelsare served; otherwise it stops withearly_stream_unavailableor a server rejection. New types cover the complete six-family wire contract:EarlyObservationBase, per-family deploy/trade/liquidity/migration/lock/token-change observation data,EarlyOutcomeData, plusEarlyChannel, filters, controls and private-list types;StreamToken.early_streamgains the six channel names andcoverage. Released channels areearly:deploys,early:locks,early:trades,early:liquidity,early:migrations, andearly:token_changes. Observations are provisional instruction intent, not executed fills; coverage is partial and replay is never durable history. Existing methods are unchanged.
📂 Examples · 📚 API docs · 💰 Get a free API key
TypeScript SDK for the MadeOnSol Solana KOL intelligence API.
Real-time Solana trading intelligence: track 2,000+ KOL wallets with <3s latency on paid keys and x402 pay-per-call (free-tier live feeds are 5-min delayed), score 85K+ Pump.fun deployers, surface deshred deploy signals ~500ms before on-chain confirmation, score 1.5M+ 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.
Server update 2026-10-05/08: realtime developer activity and USDC/USDT trade sizing. The webhook event
dev:activity(PRO+) and ULTRA+ DEX-socketdev_subscribedeliverdev_sell,dev_buy,dev_token_transfer_outanddev_token_transfer_in(a transfer is never a sell; PRO gets direct-developer events without identity stitching). Transfers now combine the legacy bounded wallet watch with the broad SPL Token/Token-2022 candidate source for attributed creators;transfer_watch_coveragedescribes the legacy watch only, whiletransfer_sources.broadreports the broad source's state/gaps. Recovered broad-source rows are REST history only, not retroactive realtime alerts. Ondex:trades, a swap paid in USDC or USDT now carriessol_amount= the SOL equivalent of its stable leg (used bydust,min_sol,max_sol) plus the additive fieldssol_amount_basis(native_sol|stable_quote_equivalent|stable_quote_unconverted) andstable_quote;stable_quote_unconvertedmeanssol_amount0 (size unknown). Details: changelog.
Server update 2026-10-04 (no package change needed): copy-trade rules follow any valid source wallet.
source_walletsno longer have to be tracked KOL wallets: any valid Solana wallet fires, KOL membership is optional enrichment, and copy-trade sources do not use Wallet Tracker quota. Each rule reportssource_admission(any_wallet) andoperational_state(eligible, or an infrastructure statemonitoring_pending/monitoring_unavailable/source_capacity_unavailable).source_wallets_tracked/source_wallets_untrackedand theuntracked_source_walletswarning are legacy fields, still filled. This supersedes the "signals fire only for tracked KOL wallets" wording in older notes below. Limits are unchanged: PRO 3 rules × 5 wallets, ULTRA 20 × 50, BUSINESS 100 × 250.
New in 4.0.0: sniper is ULTRA+ only and no longer keyless (breaking).
sniperRecent()in keyless (x402) mode now throwsKeylessNotAvailableErrorbefore any request or payment: the server retired/api/x402/sniper/recenton 2026-10-06 (HTTP 410x402_endpoint_retired, nothing charged). With an ULTRA, BUSINESS or ENTERPRISE API keysniperRecent()/sniperByDeployer()work as before (PRO keys now get 403tier_required). Types:SniperDeploygains the early-observation fields (event_id,sourceshredprism | deshred,outer_instruction_index,observation_stage,execution_status,transaction_version,transaction_config,fee_payer),sniperByDeployer()returns the typedSniperByDeployerResponse,attribution_statusis the closed set unverified | confirmed | corrected, andStreamTokengainsearly_ws_url/early_stream(ULTRA+ only, present while the early stream is active). Observations are intent, not execution proof.
New in 2.9.0: token lock provenance. Typed: Token lock rows carry
provider(identityverified | compatible | unverified;lock_urlalways null on Solana, never constructed),explorer(Solana Explorer links),price_usd,seconds_until_end,seconds_until_next_unlockand, for Bonfida, the trancheschedule(server 2026-10-02). Additive only.
Deployer activity timeline (server-expanded 2026-10-08).
client.deployerActivity(wallet, { limit?, cursor?, since?, types? })bindsGET /deployer-hunter/{wallet}/activity: launches,dev_buy,dev_sell,dev_token_transfer_out,dev_token_transfer_in, creator transfers, fee claims, funding in and capital out, newest first. Realtime dev rows come from persisteddev_exit_events; recovered transfer rows can carryrecovered,recovered_at,time_basis: "chain",late_classifiedandcounterparty_count. Readcoverage.families.*, retention boundaries and the history plan before interpreting an empty range; transfer history has a shorter online retention than trade history. PRO+, keyed (v1) only.
New in 3.1.0 (types and docs only, no runtime change):
DeployerProfile.first_deploy_at(earliest deploy observed by MadeOnSol) andDeployerProfileResponse.capital_intelligence(PRO+) are typed; REST-only parameters are documented as rejected on the x402 route with400 param_not_supported_on_x402before any payment (they were documented as ignored);DeployerTokensParams.only_bondedis documented as applied.
New in 3.0.0: the declared types now match what the API returns (breaking for TypeScript only; no runtime change). Renamed or removed fields that were always
undefinedat runtime:KolCompareProfile.wallet_address→wallet,twitter_url,roi_30d/consistency_30dremoved, responsecountremoved;KolEntryOrderEntry.wallet_address→wallet,traded_at→first_buy_at;SignalPerformance.series→history;DeployerTrajectoryResponse.deployer/trajectorynullable,runner_rate/labeled_tokens/avg_time_to_bond_minutesremoved;WalletTrackerSummaryStatsrewritten to the real shape;KolAlert.details/wallet_address, legacyDeployerTokennames andvwap_usdremoved;DeployerTier= elite | good | moderate | rising | cold (+ unranked on recent-bonds). Filter values the API rejected with 400 are gone: deployer leaderboardsort(bonding_rate | recent_bond_rate | total_bonded | last_deploy_at | post_bond_survival_rate), KOL leaderboardsort,unique_strategiesis boolean,min_lp_burnt_pctremoved.tokenRisk()returnsTokenRiskSingleResponse(addsresolved_from).kolLeaderboard()(x402) rows now use the canonicalwallet/pnl/volumeof the REST route; the oldwallet_address/pnl_sol/total_buy_sol/total_sell_solare no longer typed (the server still sends them as deprecated aliases until 2026-11-03). Added: pagination/coverage/free-delay metadata across the feeds, tradehistory, depth/poolsuniverse, buyer-qualitysignal_stats, streamtoken_prices/named_subscriptions.
New in 2.10.0: types for honest degraded reads.
TradeCoverage.size_floor(the stored trade tape drops buys under 0.05 SOL / $3.50; SOL-receiving sells are kept),WalletStatsResponse.stats_unavailable/enrichment_unavailable/degraded_fields(a failed lookup is unknown, never empty), and holder-census fallback reasons includingcensus_inconsistent_sum_exceeds_supply. Type fixes (the runtime was always right, the declared types were not):kolConsensus()returns its figures underconsensus,peakHistory()underpeak_history,deployerProfile()underdeployer(newKolConsensus,PeakHistory,DeployerProfiletypes); the wallet-tracker watchlist iswallets+count(add/update return{ wallet });WebhookEventlists all nine registry events andWebhookFiltersthe surge/revival and custom-condition filters;ApiTierincludesBUSINESSandENTERPRISE. Code that read the old top-level fields gotundefined; switch to the nested ones. Types and docs only; no runtime change.
New in 2.8.0: a paid call whose answer is lost is recovered, never paid twice (PAY-05). In keyless (x402) mode, when the answer to a paid GET is lost or still pending (timeout, dropped connection, gateway 502/503/504, 429, or the server's
payment_uncertain/paid_result_in_progress/paid_result_missing), the client re-sends the SAME proof for the SAME URL with aPAYMENT-RECOVERYheader signed by your payer key and receives the stored answer, byte-identical. It never signs a new payment, and a recovery retry never uses payment-policy budget. Retries are bounded (default 8 sends / 10 min,recovery: RecoveryOptions); after that a typedX402PaymentErrorcarriescode,paymentId,retryable,newPaymentAllowed(true only for a server-provennot_paid) andresume().lastPaidResultgives the provenance of the last paid answer (originoriginal/deferred,sourcelive/stored, paid/generated times, body sha256); adeferredanswer was produced after the payment. POST/PATCH/DELETE are still sent once. Against a server that does not offer recovery (older, or with recovery switched off) the result is the previous behaviour: a re-presented proof ends in the 409replay_detectedand is surfaced, never re-paid. API-key (msk_) users: no change.
New in 2.7.0: keyless calls that x402 cannot serve now fail fast. In x402 (keyless) mode,
peakHistory,kolConsensus,tokenBundle,scoutLeaderboardandcoordinationHistorythrowKeylessNotAvailableErrorbefore any request or payment: those routes are not priced for x402 and returned 404. With an API key they work as before. New exportsX402_UNAVAILABLE_PATHSandx402UnavailablePath(). Token risk is described as risk evidence for your own policy, not a verdict (no "safety score" wording). The deployer-profile description names the fields the API returns:is_deployerand the nesteddeployerobject (total_tokens_deployed, ...); an untracked wallet returnsis_deployer: falseanddeployer: null.
New in 2.6.0 — the Wallet Tracker and copy-trade types now match the API.
WalletTrackerTradesResponseis{ events, count, ordered_by, next_cursor, next_cursor_slot }: that is whatGET /wallet-tracker/tradeshas always returned. The oldtrades/has_more/nextfields never existed at runtime, sores.tradeswas alwaysundefined. Each event now types its real fields (wallet_address,slot,replayed,ingested_at,token_name, …), andactionis"buy" | "sell" | null(nullon transfers).WalletTrackerActionis"buy" | "sell": the API has always rejectedtransfer_in/transfer_outwith 400, so select transfers withevent_type: "transfer". The params gainorder("slot"|"block_time") and thebefore_slotcursor. Copy-trade create/update params,CopyTradeSubscriptionandcopyTradeSignalsparams gain the market-cap bandmin_mc_usd/max_mc_usd. Optional response fields for the 2026-09-25 server release:CopyTradeSubscription.source_wallets_tracked/source_wallets_untracked/warnings(only tracked KOL wallets can fire;codeuntracked_source_walletsorsource_wallet_tracking_unavailable),copyTradeUpdate()now returnsCopyTradeUpdateResponse(still{ subscription }, pluswarnings, andwebhook_secret+noteONCE when the PATCH sets awebhook_urlon a rule without a signing secret),testWebhook(id, { event })picks which subscribed event to sample andWebhookTestResult.eventechoes it, and/mefeatures.wallet_tracker_watchlist.limit. All are optional: older servers simply omit them. Compile errors from this release only appear in code that could not have worked at runtime: no working call changes behaviour.
New in 2.3.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.2.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.0.0 — BREAKING for keyless (x402) mode only: an explicit
paymentPolicyis required (security fix, SDK-01).new MadeOnSolX402({ privateKey, paymentPolicy: { payTo, feePayer, maxAmountAtomic, maxTotalAmountAtomic, rpcUrl } })(optionalbeforePayment); the deprecatedMadeOnSolX402Optionstype gains the same required field. Before, keyless mode signed whatever Solana USDC amount, recipient and fee payer a 402 challenge asked for. Now every challenge is checked BEFORE signing against a trusted merchantpayTo, a trusted facilitatorfeePayer(which must differ from your wallet), the USDC mint,solana:5eykt…mainnet, theexactscheme, a per-call cap and a lifetime cap. Use the canonical values in the keyless section below; caps must be at least20000(0.02 USDC) per call to reach every endpoint. The budget is per client instance / process: not wallet-wide, not shared between processes, reset on a new instance or restart. Keyless requires the base URL exactlyhttps://madeonsol.com. API-key (msk_) users: no change, no new config.
New in 1.30.0 — REST/x402 parity fix, top traders, and sniper detection. Found by an internal agentic-infra coverage audit:
MadeOnSolRESTnever got the free-tier/live-feed reads thatMadeOnSolX402already had (kolFeed,kolCoordination,kolLeaderboard,deployerAlerts,kolPairs,kolHotTokens,kolTrendingTokens,kolTokenEntryOrder,kolCompareWallets,kolAlertsRecent), plustokenBatch,tokensBatchBuyerQuality, and the sniper feature (sniperByDeployer,sniperWatchlist,sniperWatchlistAdd,sniperWatchlistRemove—sniperRecentalready existed).rest.tokenTopTraders(mint, params?)— previously present but with no MCP/ElizaOS/SAK tool anywhere — andrest.updateWebhook(id, params)(PATCH) round out the surface.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 pool with sufficient authoritative data for depth (seeunsupported_pools[].reason; tracked pools may still be listed there). 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.SVM_PRIVATE_KEY!, undefined, paymentPolicy);
// See the required paymentPolicy example below.
const { trades } = await client.kolFeed({ limit: 10 });
console.log(trades);Advanced initialization
Keyless mode requires paymentPolicy. API-key mode does not initialize signing or require these settings.
import { MadeOnSolX402, type SolanaPaymentPolicy } from "madeonsol-x402";
const paymentPolicy: SolanaPaymentPolicy = {
payTo: "GLu63pRCYrp4BJu5P5ciYKxgeZFW9c8TJ8jWzK3TB9AR", // canonical merchant (see below)
feePayer: "2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4", // PayAI facilitator fee payer (see below)
maxAmountAtomic: "20000", // 0.02 USDC per authorization (the highest Solana leg)
maxTotalAmountAtomic: "1000000", // 1 USDC across this client's lifetime
rpcUrl: process.env.SVM_RPC_URL!, // your trusted HTTPS RPC
timeoutMs: 30_000,
// Optional additional approval; literal true is required if this hook is set.
beforePayment: async proposal => BigInt(proposal.amountAtomic) <= 20000n,
};
const client = new MadeOnSolX402({ privateKey: process.env.SVM_PRIVATE_KEY!, paymentPolicy });
// Equivalent: createClient(privateKey, undefined, paymentPolicy).
console.log(client.authorizedAmountAtomic);Canonical MadeOnSol values (Solana mainnet USDC). Pinned here (GitHub + npm README) so you do not have to take them from a 402:
- merchant
payTo/X402_PAY_TO:GLu63pRCYrp4BJu5P5ciYKxgeZFW9c8TJ8jWzK3TB9AR(also shown on https://madeonsol.com/x402 and https://madeonsol.com/.well-known/x402) - facilitator
feePayer/X402_FEE_PAYER:2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4. This is the fee payer of PayAI, the third-party facilitator MadeOnSol's Solana rail uses. If PayAI rotates it, keyless calls fail closed (the client refuses to sign) until you update this value; a MadeOnSol release will announce the new one. - prices: Solana legs are 5000–20000 atomic (0.005–0.02 USDC), so
maxAmountAtomic/X402_MAX_AMOUNT_ATOMICmust be at least20000to reach every endpoint.
The budget is per client instance / process: not wallet-wide, not shared between processes, reset when a new instance or process starts. Keyless mode requires the base URL exactly https://madeonsol.com.
Breaking keyless upgrade: missing policy now fails closed. Only exact mainnet USDC payments to the configured merchant and facilitator are signed. The agent cannot also be the facilitator fee payer. URLs must use HTTPS; requests stay on the configured API origin and redirects are refused. There is no public RPC fallback. Amounts use positive integer strings or bigint, never floating-point numbers; USDC has 6 decimals. Choose caps for the endpoints you use.
Reuse one long-lived client. Concurrent calls share its allowance. An unsigned approval denial releases its reservation; once payment creation starts, the allowance remains consumed even after RPC/signing/network errors or an ambiguous response. This is authorized attempts, not settled spend, with no automatic refund or replay. The frozen beforePayment proposal cannot override built-in checks.
The 30-second default bounds challenge reading, approval, signing waits and submission. Late RPC results cannot invoke the signer after timeout. Solana transaction validity still follows the signed recent blockhash; the client deadline cannot revoke a proof already sent. Response-body consumption after returned headers is not covered by this deadline.
A new client or process starts a new allowance. These limits are not a durable, wallet-wide budget; coordinate externally when multiple agents/processes share a wallet. API-key behavior and precedence are unchanged.
x402 Endpoints (per-request micropayments)
Only declared query parameters are accepted in keyless (x402) mode. MadeOnSolX402 methods share their param types with MadeOnSolREST, but each x402 route accepts exactly the parameters its 402 challenge advertises (accepts[].outputSchema.input.queryParams, also listed per endpoint at https://madeonsol.com/api/x402). Any other query parameter is refused with 400 before any payment is verified or settled, so it is never charged: a known REST-only parameter answers param_not_supported_on_x402 with the rest_route that implements it, anything else answers undeclared_parameter with an unsupported_params list. Cache-busters are not exempt. Declared parameters with an invalid value keep their own 400.
| 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?) | Retired in keyless mode; raises KeylessNotAvailableError before the request. Use an ULTRA/BUSINESS/ENTERPRISE API key. |
| 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 the current keyless endpoints, prices, and parameter docs (free) |
API key only (not on the x402 rail): scoutLeaderboard(), coordinationHistory(), kolConsensus(), peakHistory() and tokenBundle() have no keyless x402 route (the server answers 404). With an API key they call /api/v1/ as before; in private-key (x402) mode they throw KeylessNotAvailableError before any request or payment. The list is exported as X402_UNAVAILABLE_PATHS. discovery() returns the live x402 catalog.
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 1.5M+ 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 kol_activity.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 risk score (evidence, not a verdict) 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?) | ULTRA/BUSINESS/ENTERPRISE | Early deploy observations with action identity and separate execution status; unknown enrichment stays null. |
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 is the lowercase hex HMAC-SHA256 (no prefix) of `${X-MadeOnSol-Timestamp}.${rawBody}`, keyed with webhook_secret used as-is (do not hex-decode it). X-MadeOnSol-Timestamp is unix milliseconds and is regenerated on every retry. Compute the HMAC over the exact raw request bytes, compare in constant time over equal-length buffers (validate the header as 64 hex characters first), and reject stale deliveries (for example older than 5 minutes) on your side: the server does not enforce a replay window. Full Node.js and Python verifiers: https://madeonsol.com/api-docs#webhook-payload
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.
