madeonsol-x402 1.25.0 β†’ 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,471 +1,474 @@
1
- # madeonsol-x402
2
-
3
- [![npm version](https://img.shields.io/npm/v/madeonsol-x402?style=flat-square)](https://www.npmjs.com/package/madeonsol-x402)
4
- [![npm downloads](https://img.shields.io/npm/dm/madeonsol-x402?style=flat-square)](https://www.npmjs.com/package/madeonsol-x402)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.4+-blue?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
7
-
8
- > πŸ“‚ **[Examples](./examples/)** Β· πŸ“š **[API docs](https://madeonsol.com/api-docs)** Β· πŸ’° **[Get a free API key](https://madeonsol.com/pricing)**
9
-
10
- TypeScript SDK for the [MadeOnSol](https://madeonsol.com) Solana KOL intelligence API.
11
-
12
- > Real-time Solana trading intelligence: track 1,069 KOL wallets with <3s latency, 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/insider `transfer_delta` detection), push every pump.fun graduation, and stream every DEX trade. Free tier: 200 requests/day, every endpoint β€” no signup payment. Get a key at [madeonsol.com/pricing](https://madeonsol.com/pricing).
13
-
14
- > **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_usd` on 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. Because `sol_amount` is 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 new **`market_price_sol`/`market_price_usd`** fields β€” it is sampled once per token per pool update, so every trade in the same slot shares it. Until now `price_sol` carried 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. Use `price_sol` for cost basis, fills and PnL; `market_price_sol` for a per-token series independent of trade size and direction. Both `rest.tokenTrades(mint)` and `rest.walletTrades(address)` carry all four fields (typed on `TokenTrade` / `WalletTrade`) β€” `walletTrades` returned amounts and no price at all before.
15
-
16
- > **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()` and `deployerRecentBonds()` (poll it incrementally with `next_since`). Read `bonding_rate` (lifetime) against `recent_bond_rate` (rolling) β€” the gap between them is the signal, not either number alone. `runner_rate` only means something once `labeled_tokens >= 3`, and an **untracked wallet returns a profile with zeroed counters, not a 404**, so check `total_deployed` before 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.
17
-
18
- > **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 the `ws` package (which exposes `terminate()`) 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 on `ws` and want the fast exit on Node β‰₯22, `npm i ws`.)
19
- >
20
- > **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 buy `sizes` (each >0 and ≀10000; default `[0.5, 1, 5, 10]`, sent as a CSV `sizes` param); every computable pool returns `spot_price_sol`, `fee_pct`, a `quotes[]` entry per size (`tokens_out`, `avg_price_sol`, `price_impact_pct`), and `to_move_price` β€” the SOL required to move price **1% / 5% / 10%**. Constant-product AMMs are served from stream reserves (`source: "stream"` with `reserves_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 in `unsupported_pools[]` with a `reason` (e.g. `concentrated_liquidity_depth_not_supported`, `curve_graduated_use_amm_pool`) rather than a wrong number; `primary_pool` names the deepest computable pool and `found: false` means no pools are tracked at all. Typed `TokenDepthResponse` (+ `TokenDepthParams`, `TokenDepthPool`, `TokenDepthQuote`, `TokenDepthToMovePrice`, `TokenDepthUnsupportedPool`). **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route.** And `rest.tokenRisk(mint)` now returns a top-level **`dev` block** (typed `TokenRiskDev | 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 β€” and `wallet_empty`: is the dev wallet empty NOW), and `transferred_out` (tokens left without a sell; `null` = unknown when trade coverage or rollup freshness can't prove it β€” never a guess). `dev` is `null` when the mint has no pending_deploys row; the response also carries `as_of`. `deployer:alert` webhook/WS payloads gain `dev_buy_sol` + `dev_buy_supply_pct` β€” the dev's self-buy visible at alert time.
21
- >
22
- > **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`, and `dump_cluster` cohort stats (typed `WalletBatchClassifyResponse`) β€” 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 with `price_sol`/`price_usd`/`early_buyer_rank`/`slot`, filterable by `action`/`wallet`/`since`/`until`, defaulting to the **full history** (starts 2026-04-12; the `coverage` block carries `history_start` + `scope`). `rest.tokenTopTraders(mint, params?)` and `rest.sniperRecent(params?)` are new keyed methods too. The wallet profile `flags` block gains the same `is_sniper`/`is_bundler`/`is_dumper` + `dump_cluster` fields, and **`bot_confidence` is a type fix**: previously typed `number | null` but the API always returned `null` due to a bug β€” it now returns the real value as a string enum `"none" | "low" | "medium" | "high" | null`. `TokenRiskInputs` gains `sniper_footprint` (slot-window snipe rollup, `SniperFootprint | null`) and sniper deploys each carry the same `footprint` block. **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), and `deployerTrajectory` ($0.01) are now callable on the `MadeOnSolX402` client with per-request USDC micropayments. New types: `WalletClassification`, `WalletBatchClassifyResponse`, `TokenTradesParams`, `TokenTrade`, `TokenTradesResponse`, `TokenTopTradersParams`, `TokenTopTrader`, `TokenTopTradersResponse`, `SniperRecentParams`, `SniperDeploy`, `SniperRecentResponse`, `SniperFootprint`, `DumpClusterStats`.
23
- >
24
- > **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 a `transfer_delta` (on-chain amount βˆ’ trade-derived net position) β€” exposing tokens that arrived or left **without a swap** (airdrops, insider funding, wallet-hopping). Distinct from `walletPositions` (trade-derived FIFO): holdings is "what they actually hold right now". Returns typed `WalletHoldingsResponse` with a `summary` (token_accounts / non_zero / returned / priced / total_value_usd / truncated) and `verified_at`. **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route.** ULTRA only.
25
- >
26
- > **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. The `bundle` block carries `wallet_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`, and `tokens_held` (typed `TokenBundleResponse`). Field-gated by tier: BASIC get the `bundle` block only (`wallets: []`); PRO adds the top-10 `wallets` with 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.
27
- >
28
- > **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 in `tokens` is either a full risk result (same shape as `rest.tokenRisk(mint)`, plus `as_of`) or `{ mint, error: "not_tracked" }`; untracked mints don't fail the batch, and `tokens` preserves de-duplicated input order (typed `TokenBatchRiskResponse`). PRO/ULTRA only. Plus `rest.streamSessions()` lists your live WebSocket sessions across ws-streaming + dex-stream (typed `StreamSessionsResponse`), and `rest.streamSessionKill(id)` force-releases a slot by id (typed `StreamSessionEvictResponse`) β€” the self-serve fix for a 4002 lockout when a deploy overlap leaves a ghost socket holding your slot. PRO/ULTRA only.
29
- >
30
- > **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 carries `progress_pct`, `velocity_pct_per_min`, `eta_minutes`, `stalled`, `real_sol_reserves`, `market_cap_usd`, `liquidity_usd`, `authorities_revoked`, `deployer_tier`, and `age_minutes` (typed `AlmostBondedResponse`). `sort` is `velocity_desc` (default) / `progress_desc` / `eta_asc`. **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route.** PRO/ULTRA only. Plus `client.tokensList({ sort })` gains four momentum sorts β€” `mc_change_5m_desc`, `mc_change_1h_desc`, `volume_1h_desc`, and `trending` (composite recent-volume Γ— positive-momentum rank).
31
- >
32
- > **New in 1.16.0** β€” **Token trade flow.** `client.tokenFlow(mint, { window? })` returns a trade-flow aggregate over a `1h`/`24h` window β€” `unique_wallets` / `unique_buyers` / `unique_sellers`, `buy_count` / `sell_count` / `total_trades`, `buy_sol` / `sell_sol` / `net_sol`, and a `trades_per_wallet` wash-trading proxy (typed `TokenFlowResponse`). It's an **organic-vs-fake volume** read. **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route**, so x402-only clients can't reach it. PRO/ULTRA only. Deployer alerts now carry `deployers.deployer_sol_balance` β€” the deployer wallet's SOL balance at alert time (null for historical rows).
33
- >
34
- > **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 a `top_buyers[]` array (typed `TokenSnapshotResponse`). `rest.signalPerformance(name, { history? })` returns the **Signal Scorecard** β€” out-of-sample reliability buckets (hit_rate, base_rate, lift, sample_n, window_days) for `dump_cluster_count`, `runner_rate`, `recycled_early_buyer_count`, or `coordination_count`, with a per-day `series` when `history: true` (typed `SignalPerformanceResponse`). `rest.signals()` is the free catalog of all scored signals (typed `SignalsCatalogResponse`). `rest.tokenRisk(mint)` and `rest.tokenBuyerQuality(mint)` are now fully live server-side.
35
- >
36
- > **New in 1.13.0** β€” **Token risk score.** `rest.tokenRisk(mint)` returns a transparent 0–100 rug-risk/safety score (higher = riskier) with a `band` (safe/caution/danger), an explainable `factors[]` array, and the raw `inputs` (mint/freeze authority, liquidity, liq-to-MC ratio, transfer fee, launch cohort, deployer bond rate, KOL signal, blacklist). Typed as `TokenRiskResponse`. PRO/ULTRA only.
37
- >
38
- > **New in 1.12.0** β€” `/token/{mint}` and `/token/batch` responses now include `liquidity_to_mc_ratio`, `launch_cohort_sol`, and `launch_cohort_size`. `/tokens` gains three new filter params: `min_liq_mc_ratio`, `max_liq_mc_ratio`, and `deployer_tier`. `/tokens` list items now include `liquidity_to_mc_ratio` and `deployer_tier`. `/kol/leaderboard` entries now include `median_hold_minutes_30d` and `percentile_early_entry_30d`.
39
- >
40
- > **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 on `labeled_tokens` β‰₯3) plus `avg_time_to_bond_minutes`, on `DeployerAlert.deployers` and the deployer-trajectory profile.
41
- >
42
- > **New in 1.11** β€” **Graduation events + dump-cluster detection.** Subscribe `token:graduations` for every pump.fun bond in real time (tracked deployer or not, typed `GraduationEvent`). Buyer-quality `breakdown` adds `dump_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-band `dex:graduations` frames.
43
-
44
- > **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. Use `sniper:deploys` WebSocket for push.
45
- >
46
- > **New in 1.9** β€” **Price alerts, scout leaderboard, coordination history.** `rest.priceAlertsCreate()` (PRO=5, ULTRA=25). `scoutLeaderboard()`, `kolConsensus()`, `peakHistory()`, `coordinationHistory()`. `walletStats()` now returns `derived`: win_rate, roi, verdict, biggest_miss.
47
- >
48
- > **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.
49
- >
50
- > **New in 1.7.1** *(2026-05-13)* β€” Velocity field shape corrected to match the API: `mc_change_pct`, `volume_usd`, `mev_volume_pct` are top-level on the token response, each keyed by `5m`/`15m`/`1h`/`2h`/`4h`. The 1.7.0 README documented a `velocity[window]` shape that didn't match the wire format. Runtime is unchanged β€” fix is to typed shape + docs.
51
- >
52
- > **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 include `liquidity_to_mc_ratio` and `deployer_tier`. Default `min_liq=2000` skips phantom-MC dust; pass `min_liq=0` to 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 by `5m`/`15m`/`1h`/`2h`/`4h`) plus `history_age_seconds`. `/token/{mint}` 400s now ship structured `code`, `reason`, `received_length`, `example`, and `docs` β€” stop guessing why a mint failed. Deprecated `avg_entry_mc_usd` fully removed.
53
-
54
- ## Quick start (10 seconds)
55
-
56
- ```bash
57
- npm install madeonsol-x402
58
- ```
59
-
60
- ```ts
61
- import { createClient } from "madeonsol-x402";
62
- const client = createClient("msk_..."); // free tier at https://madeonsol.com/pricing
63
- const { trades } = await client.kolFeed({ limit: 5 });
64
- ```
65
-
66
- ## Authentication
67
-
68
- Two options:
69
-
70
- | Method | Option | Best for |
71
- |---|---|---|
72
- | **MadeOnSol API key** (recommended) | `apiKey` | Developers β€” [get a free key](https://madeonsol.com/pricing) |
73
- | x402 micropayments | `privateKey` | AI agents with Solana wallets |
74
-
75
- > **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 free `msk_` key at [madeonsol.com/pricing](https://madeonsol.com/pricing).
76
-
77
- ## Install
78
-
79
- ```bash
80
- npm install madeonsol-x402
81
- ```
82
-
83
- > x402 peer deps (`@x402/fetch @x402/svm @x402/core @solana/kit @scure/base`) are only needed when using `privateKey`.
84
-
85
- ## Quick Start
86
-
87
- ```ts
88
- import { createClient } from "madeonsol-x402";
89
-
90
- // Option 1: API key β€” get one free at madeonsol.com/pricing
91
- const client = createClient("msk_your_api_key_here");
92
-
93
- // Option 2: x402 micropayments (auto-detected when no msk_ prefix)
94
- // const client = createClient(process.env.SOLANA_PRIVATE_KEY!);
95
-
96
- const { trades } = await client.kolFeed({ limit: 10 });
97
- console.log(trades);
98
- ```
99
-
100
- ### Advanced initialization
101
-
102
- ```ts
103
- import { MadeOnSolX402 } from "madeonsol-x402";
104
-
105
- const client = new MadeOnSolX402({
106
- apiKey: "msk_...", // OR
107
- privateKey: "base58...", // x402 micropayments
108
- });
109
- ```
110
-
111
- ## x402 Endpoints (per-request micropayments)
112
-
113
- | Method | Description |
114
- |---|---|
115
- | `kolFeed(params?)` | Real-time KOL trade feed from 1,000+ tracked wallets |
116
- | `kolCoordination(params?)` | Tokens being accumulated by multiple KOLs simultaneously |
117
- | `kolLeaderboard(params?)` | KOL performance rankings by PnL and win rate (180 days of trade history) |
118
- | `kolPairs(params?)` | KOL affinity matrix β€” which KOLs frequently co-trade the same tokens |
119
- | `kolHotTokens(params?)` | KOL momentum tokens β€” accelerating KOL buy interest |
120
- | `kolTokenEntryOrder(mint, params?)` | Ranked KOL first-buyer order for a token |
121
- | `kolCompareWallets({ wallets })` | Side-by-side comparison of 2–5 KOL wallets |
122
- | `kolAlertsRecent(params?)` | Live KOL alert feed β€” clusters, fresh-token buys, heating-up wallets |
123
- | `deployerAlerts(params?)` | Pump.fun deployer alerts with KOL enrichment. PRO/ULTRA: filter by tier. |
124
- | `walletStats(address)` | **New 1.8** Β· Wallet stats + cross-product flags (is_kol / is_alpha_tracked + bot_confidence / is_deployer). 90-day window. **$0.005** |
125
- | `walletPnl(address)` | **New 1.8** Β· FIFO cost-basis PnL: realized + unrealized SOL, profit factor, drawdown, hold times, daily curve, closed + open positions. **$0.02** |
126
- | `walletPositions(address)` | **New 1.8** Β· Open positions only, live unrealized from market-cap tracker. Shares /pnl cache. **$0.01** |
127
- | `walletTrades(address, params?)` | **New 1.8** Β· Cursor-paginated raw trades with action / token / since-until filters. **$0.005** |
128
- | `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** |
129
- | `tokenCandles(mint, params?)` | **New 1.21** Β· OHLCV candles (1m–1d timeframes, 30d history) with per-candle volume, trade count, and market cap. **$0.01** |
130
- | `almostBonded(params?)` | **New 1.21** Β· Launchpad tokens approaching graduation (pump.fun + LetsBonk LaunchLab) β€” bonding progress, velocity (Ξ”progress/min), ETA, deployer tier. **$0.01** |
131
- | `tokenTopTraders(mint, params?)` | **New 1.21** Β· Wallets ranked by realized PnL (or ROI) on a token, enriched with KOL identity + alpha reputation. **$0.02** |
132
- | `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** |
133
- | `sniperRecent(params?)` | **New 1.21** Β· Deshred sniper deploy feed (elite/good deployers) with per-deploy snipe `footprint`. **$0.01** |
134
- | `deployerTrajectory(wallet, params?)` | **New 1.21** Β· Deployer bond-rate trajectory β€” streaks, rolling bond rates, trend, cadence. `include: "daily_snapshots"` adds 90 days. **$0.01** |
135
- | `discovery()` | Lists all 25 endpoints, prices, and parameter docs (free) |
136
-
137
- ## REST API client
138
-
139
- 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.
140
-
141
- ```ts
142
- import { MadeOnSolREST } from "madeonsol-x402";
143
-
144
- const rest = new MadeOnSolREST({ apiKey: "msk_your_key" });
145
- const { leaderboard } = await rest.alphaLeaderboard({ period: "30d", sort: "win_rate" });
146
-
147
- // Rate-limit headers from the most recent response
148
- console.log(rest.lastRateLimit); // { limit, remaining, reset, requestId }
149
- ```
150
-
151
- ### Alpha wallet intelligence
152
-
153
- Scored from 1M+ early-buyer records (wallets seen in the first 20 buyers of Pump.fun tokens).
154
-
155
- | Method | Tier | Description |
156
- |---|---|---|
157
- | `rest.alphaLeaderboard(params?)` | All | Top profitable wallets. Up to 100 on Free/Pro; ULTRA unlocks 500 + bot signals |
158
- | `rest.alphaWallet(wallet)` | ULTRA | Full per-token breakdown + bot_signals array |
159
- | `rest.alphaLinked(wallet)` | ULTRA | Wallets behaviorally linked (co-bought 3+ tokens within 2s) |
160
-
161
- **alphaLeaderboard params** β€” `period` ("7d" \| "30d" \| "all"), `min_tokens` (1–20), `sort` ("win_rate" \| "pnl" \| "roi"), `exclude_bots` ("true" \| "false")
162
-
163
- ### Token quality
164
-
165
- | Method | Tier | Description |
166
- |---|---|---|
167
- | `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 }` |
168
- | `rest.tokenCapTable(mint)` | PRO+ | First non-deployer early buyers, enriched with PnL/KOL/bot flags. PRO=10, ULTRA=20 |
169
- | `rest.tokenBuyerQuality(mint)` | All | 0–100 buyer-quality score + full breakdown (5-min cached). Live server-side |
170
- | `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 |
171
- | `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 |
172
- | `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`) |
173
- | `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]` |
174
- | `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 |
175
- | `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 |
176
- | `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) |
177
- | `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) |
178
- | `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) |
179
-
180
- **tokenCandles params** β€” `tf` ("1m" \| "5m" \| "15m" \| "1h" \| "4h" \| "1d", default "1h"), `limit` (1–1000, default 200), `from` (ISO 8601), `to` (ISO 8601)
181
-
182
- ```ts
183
- // Score a basket in one request (counts as 1 against quota)
184
- const { tokens, count } = await rest.tokensBatchRisk([mintA, mintB, mintC]);
185
- for (const t of tokens) {
186
- if ("error" in t) console.log(t.mint, t.error); // e.g. "not_tracked"
187
- else console.log(t.mint, t.risk_score, t.band); // full risk result + as_of
188
- }
189
- ```
190
-
191
- ### Signal Scorecard *(new in 1.15)*
192
-
193
- 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.
194
-
195
- | Method | Tier | Description |
196
- |---|---|---|
197
- | `rest.signals()` | All (free) | Catalog of scored signals β€” name, methodology, and each signal's `performance_endpoint`. No payment required |
198
- | `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[]` |
199
-
200
- Valid signal names: `dump_cluster_count`, `runner_rate`, `recycled_early_buyer_count`, `coordination_count`.
201
-
202
- ```ts
203
- const { signals } = await rest.signals();
204
- const scorecard = await rest.signalPerformance("dump_cluster_count", { history: true });
205
- console.log(scorecard.buckets); // [{ bucket, hit_rate, base_rate, lift, sample_n, ... }]
206
- ```
207
-
208
- ### KOL coordination alerts (v1.1 β€” push signals)
209
-
210
- 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.
211
-
212
- ```ts
213
- // Create a rule
214
- const { rule, webhook_secret } = await rest.coordinationAlertsCreate({
215
- name: "fresh pump cluster",
216
- min_kols: 4, // minimum distinct KOLs in window
217
- window_minutes: 15, // peak-density window (1-60)
218
- min_score: 70, // 0-100 composite score cutoff
219
- include_majors: false, // filter WIF/BONK/POPCAT
220
- cooldown_min: 60, // one fire per (rule,token) per 60min...
221
- score_jump_break: 10, // ...unless score jumps +10 vs last fire
222
- delivery_mode: "both",
223
- webhook_url: "https://you.com/hooks/coord",
224
- });
225
- // β†’ store webhook_secret β€” shown ONCE
226
- ```
227
-
228
- `coordinationAlertsList`, `coordinationAlertsGet(id)`, `coordinationAlertsUpdate(id, params)`, `coordinationAlertsDelete(id)` round out the CRUD.
229
-
230
- **Webhook signature:** `X-MadeOnSol-Signature: sha256=<hmac>` where `hmac = HMAC-SHA256(webhook_secret, timestamp + "." + rawBody)`, and `X-MadeOnSol-Timestamp` carries the unix seconds used.
231
-
232
- **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.
233
-
234
- ### KOL first-touch signal *(new in 1.3)*
235
-
236
- 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.
237
-
238
- **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](https://madeonsol.com/kol/scouts).
239
-
240
- ```ts
241
- import { MadeOnSolREST } from "madeonsol-x402";
242
- const rest = new MadeOnSolREST({ apiKey: process.env.MADEONSOL_API_KEY! });
243
-
244
- // S-tier scouts on tokens younger than 1h
245
- const { events } = await rest.firstTouches({ preset: "scout", min_scout_tier: "S" });
246
-
247
- for (const e of events) {
248
- console.log(e.first_kol.name, "scouted", e.token_symbol, `(scout_score=${e.first_kol.scout_score}%)`);
249
- }
250
- ```
251
-
252
- 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"`).
253
-
254
- > **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_touches` WebSocket channel (PRO+) or, on Ultra, create an HMAC-signed webhook subscription.
255
-
256
- **Webhook subscriptions (Ultra)** β€” up to 10 active per user, mirrors `coordinationAlerts`:
257
-
258
- ```ts
259
- const { subscription, webhook_secret } = await rest.firstTouchSubscriptionsCreate({
260
- name: "S-tier scouts on pump tokens",
261
- filters: { min_scout_tier: "S", mint_suffix: "pump" },
262
- delivery_mode: "webhook",
263
- webhook_url: "https://my.bot/hooks/scout",
264
- });
265
- // β†’ store webhook_secret β€” shown ONCE
266
- ```
267
-
268
- `firstTouchSubscriptionsList`, `firstTouchSubscriptionsGet(id)`, `firstTouchSubscriptionsUpdate(id, params)`, `firstTouchSubscriptionsDelete(id)` round out the CRUD.
269
-
270
- ### Price alerts *(new in 1.9)*
271
-
272
- CRUD for token dip/recovery price alerts. Fires via WebSocket (`price:alerts` channel) and/or HMAC-signed webhook when a token's market cap crosses your threshold. PRO=5 rules, ULTRA=25.
273
-
274
- ```ts
275
- const { alert, webhook_secret } = await rest.priceAlertsCreate({
276
- name: "SOL dip buy",
277
- token_mint: "So11111111111111111111111111111111111111112",
278
- condition: "below", // "below" | "above"
279
- threshold_mc_usd: 5_000_000_000,
280
- cooldown_min: 120,
281
- delivery_mode: "both",
282
- webhook_url: "https://you.com/hooks/price",
283
- });
284
- // β†’ store webhook_secret β€” shown ONCE
285
- ```
286
-
287
- `priceAlertsList`, `priceAlertsGet(id)`, `priceAlertsUpdate(id, params)`, `priceAlertsDelete(id)` round out the CRUD.
288
-
289
- ### Scout leaderboard & KOL consensus *(new in 1.9)*
290
-
291
- | Method | Tier | Description |
292
- |---|---|---|
293
- | `rest.scoutLeaderboard(params?)` | PRO+ | Top scout-tier KOLs ranked by first-touch follow-on rate, win rate, and ROI |
294
- | `rest.kolConsensus(params?)` | PRO+ | Tokens with the strongest KOL agreement signal β€” weighted by scout score and recent PnL |
295
- | `rest.peakHistory(mint)` | PRO+ | Historical peak-density windows for a token β€” every coordination spike with KOL breakdown |
296
- | `rest.coordinationHistory(params?)` | PRO+ | Global coordination event log with token, KOL count, score, and outcome |
297
-
298
- ```ts
299
- const { leaderboard } = await rest.scoutLeaderboard({ period: "30d", limit: 25 });
300
- const { tokens } = await rest.kolConsensus({ min_kols: 5, period: "24h" });
301
- ```
302
-
303
- ### Wallet derived stats *(new in 1.9)*
304
-
305
- `walletStats(address)` now includes a `stats` object with derived fields computed from the 90-day trade window:
306
-
307
- ```ts
308
- const { stats } = await rest.walletStats("WALLET_ADDRESS");
309
- // stats.win_rate β€” fraction 0-1, tokens sold above cost basis
310
- // stats.roi β€” aggregate return on invested SOL
311
- // stats.verdict β€” "strong" | "profitable" | "neutral" | "losing"
312
- // stats.biggest_miss β€” token with the highest post-exit gain the wallet missed
313
- ```
314
-
315
- ### Copy-trade rules
316
-
317
- 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.
318
-
319
- | Method | Description |
320
- |---|---|
321
- | `rest.copyTradeList()` | List your rules |
322
- | `rest.copyTradeCreate(params)` | Create a rule. Returns `webhook_secret` **once** β€” store it |
323
- | `rest.copyTradeGet(id)` | Get one rule |
324
- | `rest.copyTradeUpdate(id, params)` | Update fields or toggle `is_active` |
325
- | `rest.copyTradeDelete(id)` | Delete permanently |
326
- | `rest.copyTradeSignals(params?)` | Recent fired signals (up to 7 days). Filter by `subscription_id`, `since`, `limit` (1–500) |
327
-
328
- ### Wallet tracker
329
-
330
- Per-account watchlist with historical swap/transfer history.
331
-
332
- | Method | Description |
333
- |---|---|
334
- | `rest.walletTrackerList()` | List tracked wallets + remaining capacity |
335
- | `rest.walletTrackerAdd(wallet, label?)` | Add a wallet |
336
- | `rest.walletTrackerRemove(wallet)` | Remove a wallet |
337
- | `rest.walletTrackerUpdateLabel(wallet, label)` | Update label (pass `null` to clear) |
338
- | `rest.walletTrackerTrades(params?)` | Historical events. Params: `wallet`, `action`, `event_type`, `limit` (1–200), `before` (cursor) |
339
- | `rest.walletTrackerSummary(params?)` | Per-wallet stats. Params: `period` ("24h" \| "7d" \| "30d"), `wallet` |
340
- | `rest.walletStats(address)` | **New 1.8** Β· Universal wallet stats (90d) + cross-product flags. PRO+. |
341
- | `rest.walletPnl(address)` | **New 1.8** Β· Full FIFO PnL + curve + closed/open positions. PRO+. |
342
- | `rest.walletPositions(address)` | **New 1.8** Β· Open positions only with live unrealized. PRO+. |
343
- | `rest.walletTrades(address, params?)` | **New 1.8** Β· Cursor-paginated raw trades. Params: `limit` (1-500), `cursor`, `action`, `token_mint`, `since`, `until`. PRO+. |
344
- | `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+. |
345
-
346
- ### Webhooks
347
-
348
- | Method | Description |
349
- |---|---|
350
- | `rest.createWebhook(params)` | Create webhook. Returns `secret` once β€” store it for HMAC verification |
351
- | `rest.listWebhooks()` | List your webhooks |
352
- | `rest.getWebhook(id)` | Get one + recent delivery log |
353
- | `rest.updateWebhook(id, params)` | Update URL, events, filters, or re-enable |
354
- | `rest.deleteWebhook(id)` | Delete |
355
- | `rest.testWebhook(id)` | Send test payload |
356
-
357
- ### KOL/deployer detail
358
-
359
- | Method | Description |
360
- |---|---|
361
- | `rest.kolTiming(wallet, params?)` | Entry/exit timing β€” hold duration, exit speed, hour distribution |
362
- | `rest.kolPnl(wallet, params?)` | Per-wallet PnL breakdown |
363
- | `rest.deployerTrajectory(wallet)` | Deployer skill curve β€” streaks, rolling bond rate, trend |
364
- | `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) |
365
-
366
- ### Streaming token
367
-
368
- ```ts
369
- const token = await rest.getStreamToken();
370
- // token.ws_url β€” KOL/deployer streaming (Pro/Ultra)
371
- // token.dex_ws_url β€” all-DEX trade stream (Ultra only)
372
- ```
373
-
374
- ### Managed streaming client *(new in 1.10)*
375
-
376
- `rest.stream()` handles the token fetch + 24h refresh, auto-reconnect (backoff + jitter), heartbeat liveness, and typed events β€” just subscribe and listen.
377
-
378
- ```ts
379
- const stream = rest.stream();
380
- stream.on("kol:trade", (t) => console.log(t.token_symbol, t.action));
381
- stream.on("deployer:alert", (a) => console.log("new deploy", a.token_mint));
382
- stream.subscribe(["kol:trades", "deployer:alerts"]);
383
- // stream.unsubscribe([...]) / stream.close() when done
384
- ```
385
-
386
- Channels: `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`). Lifecycle events: `open`, `close`, `reconnect`, `heartbeat`, `error`. Uses the global `WebSocket` on Node 22+; on Node < 22 also `npm i ws`.
387
-
388
- ### Live stream sessions *(new in 1.18)*
389
-
390
- 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.
391
-
392
- | Method | Tier | Description |
393
- |---|---|---|
394
- | `rest.streamSessions()` | PRO+ | List your live sessions β€” each with `id`, `service`, `tier`, `channels[]`, `connected_at`, `remote_ip`, `messages_sent`. Typed `StreamSessionsResponse` |
395
- | `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` |
396
-
397
- ```ts
398
- const { sessions } = await rest.streamSessions();
399
- for (const s of sessions) console.log(s.id, s.service, s.channels, s.messages_sent);
400
-
401
- // Free a stuck slot after a deploy overlap
402
- if (sessions.length) await rest.streamSessionKill(sessions[0].id); // { evicted: true, id }
403
- ```
404
-
405
- ## DEX Firehose (Ultra)
406
-
407
- 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.
408
-
409
- ```ts
410
- import WebSocket from "ws";
411
-
412
- const { token, dex_ws_url } = await rest.getStreamToken();
413
- const ws = new WebSocket(`${dex_ws_url}?token=${token}`); // token MUST be in the query string
414
-
415
- ws.on("open", () => {
416
- ws.send(JSON.stringify({
417
- type: "subscribe",
418
- sub_id: "fresh-pumpfun",
419
- replay: 50, // up to 500 from ring buffer
420
- filters: {
421
- dex: "pumpfun", // pumpfun | pumpamm | pumpswap | raydium | jupiter | orca | meteora | launchlab
422
- token_age_max_seconds: 300,
423
- min_sol: 0.5,
424
- action: "buy",
425
- },
426
- }));
427
- });
428
-
429
- ws.on("message", (raw) => {
430
- const msg = JSON.parse(raw.toString());
431
- if (msg.channel === "dex:trades") {
432
- // { sub_id, data: { wallet, mint, action, sol_amount, dex, ... }, replay, ts }
433
- }
434
- });
435
- ```
436
-
437
- **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.
438
-
439
- Full protocol reference: [madeonsol.com/api-docs#streaming](https://madeonsol.com/api-docs#streaming).
440
-
441
- ## Rate-limit headers
442
-
443
- 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`:
444
-
445
- ```ts
446
- await rest.alphaLeaderboard();
447
- const { limit, remaining, reset, requestId } = rest.lastRateLimit;
448
- if (remaining !== null && remaining < 5) {
449
- console.warn(`Throttle warning β€” ${remaining}/${limit} requests left until ${reset}`);
450
- }
451
- ```
452
-
453
- ## Discovery
454
-
455
- ```ts
456
- const info = await client.discovery();
457
- console.log(info.endpoints); // all endpoints with prices and params
458
- ```
459
-
460
- Docs: [madeonsol.com/solana-api](https://madeonsol.com/solana-api)
461
-
462
- ## Also Available
463
-
464
- | Platform | Package |
465
- |---|---|
466
- | TypeScript SDK | [`madeonsol`](https://www.npmjs.com/package/madeonsol) on npm |
467
- | Rust SDK | [`madeonsol`](https://crates.io/crates/madeonsol) on crates.io |
468
- | Python (LangChain, CrewAI) | [`madeonsol-x402`](https://pypi.org/project/madeonsol-x402/) on PyPI |
469
- | MCP Server (Claude, Cursor) | [`mcp-server-madeonsol`](https://www.npmjs.com/package/mcp-server-madeonsol) Β· [Smithery](https://smithery.ai/servers/madeonsol/solana-kol-intelligence) Β· [Glama](https://glama.ai/mcp/servers/madeonsol/mcp-server-madeonsol) |
470
- | ElizaOS | [`@madeonsol/plugin-madeonsol`](https://www.npmjs.com/package/@madeonsol/plugin-madeonsol) |
471
- | Solana Agent Kit | [`solana-agent-kit-plugin-madeonsol`](https://www.npmjs.com/package/solana-agent-kit-plugin-madeonsol) |
1
+ # madeonsol-x402
2
+
3
+ [![npm version](https://img.shields.io/npm/v/madeonsol-x402?style=flat-square)](https://www.npmjs.com/package/madeonsol-x402)
4
+ [![npm downloads](https://img.shields.io/npm/dm/madeonsol-x402?style=flat-square)](https://www.npmjs.com/package/madeonsol-x402)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.4+-blue?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
7
+
8
+ > πŸ“‚ **[Examples](./examples/)** Β· πŸ“š **[API docs](https://madeonsol.com/api-docs)** Β· πŸ’° **[Get a free API key](https://madeonsol.com/pricing)**
9
+
10
+ TypeScript SDK for the [MadeOnSol](https://madeonsol.com) Solana KOL intelligence API.
11
+
12
+ > Real-time Solana trading intelligence: track 1,069 KOL wallets with <3s latency, 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/insider `transfer_delta` detection), push every pump.fun graduation, and stream every DEX trade. Free tier: 200 requests/day, every endpoint β€” no signup payment. Get a key at [madeonsol.com/pricing](https://madeonsol.com/pricing).
13
+
14
+ > **New in 1.26.0 β€” live holder census: exact holder count, labelled holders, and pools that are named, not just excluded.** `rest.tokenHolders(mint)` (typed `TokenHoldersResponse`) binds `GET /tokens/{mint}/holders` (PRO+): every token account of the mint read from the ledger at `confirmed` and merged per owner, so `concentration.holder_count` is EXACT (distinct non-zero owners minus pools / bonding curves / burns) β€” never a trade-derived estimate; it is `null` only when the provider refuses the census for a mega-cap, in which case you get the top-20 view and `source.census_fallback_reason` says so. Each disclosed owner carries our labels (`deployer` / `kol` / `early_buyer` / `bundle` / `bot` / `dump_cluster` β€” empty means unknown to us, not clean), and `excluded[]` NAMES what was taken out of the circulating denominator: `reason` = `pool` (with `dex` + `pool_address`), `bonding_curve` (pump.fun / LaunchLab), `burn`, or `program_account` only when we genuinely cannot attribute the PDA; `pool_pct` / `burned_pct` / `program_pct` split 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 get `503 holder_scan_in_progress` with `retry_after_seconds: 20` while the scan finishes into the cache, and the retry is instant. **KEYED (v1) β€” requires an `msk_` API key; the census is not on the x402 rail.**
15
+
16
+ > **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_usd` on 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. Because `sol_amount` is 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 new **`market_price_sol`/`market_price_usd`** fields β€” it is sampled once per token per pool update, so every trade in the same slot shares it. Until now `price_sol` carried 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. Use `price_sol` for cost basis, fills and PnL; `market_price_sol` for a per-token series independent of trade size and direction. Both `rest.tokenTrades(mint)` and `rest.walletTrades(address)` carry all four fields (typed on `TokenTrade` / `WalletTrade`) β€” `walletTrades` returned amounts and no price at all before.
17
+
18
+ > **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()` and `deployerRecentBonds()` (poll it incrementally with `next_since`). Read `bonding_rate` (lifetime) against `recent_bond_rate` (rolling) β€” the gap between them is the signal, not either number alone. `runner_rate` only means something once `labeled_tokens >= 3`, and an **untracked wallet returns a profile with zeroed counters, not a 404**, so check `total_deployed` before 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.
19
+
20
+ > **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 the `ws` package (which exposes `terminate()`) 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 on `ws` and want the fast exit on Node β‰₯22, `npm i ws`.)
21
+ >
22
+ > **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 buy `sizes` (each >0 and ≀10000; default `[0.5, 1, 5, 10]`, sent as a CSV `sizes` param); every computable pool returns `spot_price_sol`, `fee_pct`, a `quotes[]` entry per size (`tokens_out`, `avg_price_sol`, `price_impact_pct`), and `to_move_price` β€” the SOL required to move price **1% / 5% / 10%**. Constant-product AMMs are served from stream reserves (`source: "stream"` with `reserves_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 in `unsupported_pools[]` with a `reason` (e.g. `concentrated_liquidity_depth_not_supported`, `curve_graduated_use_amm_pool`) rather than a wrong number; `primary_pool` names the deepest computable pool and `found: false` means no pools are tracked at all. Typed `TokenDepthResponse` (+ `TokenDepthParams`, `TokenDepthPool`, `TokenDepthQuote`, `TokenDepthToMovePrice`, `TokenDepthUnsupportedPool`). **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route.** And `rest.tokenRisk(mint)` now returns a top-level **`dev` block** (typed `TokenRiskDev | 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 β€” and `wallet_empty`: is the dev wallet empty NOW), and `transferred_out` (tokens left without a sell; `null` = unknown when trade coverage or rollup freshness can't prove it β€” never a guess). `dev` is `null` when the mint has no pending_deploys row; the response also carries `as_of`. `deployer:alert` webhook/WS payloads gain `dev_buy_sol` + `dev_buy_supply_pct` β€” the dev's self-buy visible at alert time.
23
+ >
24
+ > **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`, and `dump_cluster` cohort stats (typed `WalletBatchClassifyResponse`) β€” 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 with `price_sol`/`price_usd`/`early_buyer_rank`/`slot`, filterable by `action`/`wallet`/`since`/`until`, defaulting to the **full history** (starts 2026-04-12; the `coverage` block carries `history_start` + `scope`). `rest.tokenTopTraders(mint, params?)` and `rest.sniperRecent(params?)` are new keyed methods too. The wallet profile `flags` block gains the same `is_sniper`/`is_bundler`/`is_dumper` + `dump_cluster` fields, and **`bot_confidence` is a type fix**: previously typed `number | null` but the API always returned `null` due to a bug β€” it now returns the real value as a string enum `"none" | "low" | "medium" | "high" | null`. `TokenRiskInputs` gains `sniper_footprint` (slot-window snipe rollup, `SniperFootprint | null`) and sniper deploys each carry the same `footprint` block. **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), and `deployerTrajectory` ($0.01) are now callable on the `MadeOnSolX402` client with per-request USDC micropayments. New types: `WalletClassification`, `WalletBatchClassifyResponse`, `TokenTradesParams`, `TokenTrade`, `TokenTradesResponse`, `TokenTopTradersParams`, `TokenTopTrader`, `TokenTopTradersResponse`, `SniperRecentParams`, `SniperDeploy`, `SniperRecentResponse`, `SniperFootprint`, `DumpClusterStats`.
25
+ >
26
+ > **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 a `transfer_delta` (on-chain amount βˆ’ trade-derived net position) β€” exposing tokens that arrived or left **without a swap** (airdrops, insider funding, wallet-hopping). Distinct from `walletPositions` (trade-derived FIFO): holdings is "what they actually hold right now". Returns typed `WalletHoldingsResponse` with a `summary` (token_accounts / non_zero / returned / priced / total_value_usd / truncated) and `verified_at`. **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route.** ULTRA only.
27
+ >
28
+ > **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. The `bundle` block carries `wallet_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`, and `tokens_held` (typed `TokenBundleResponse`). Field-gated by tier: BASIC get the `bundle` block only (`wallets: []`); PRO adds the top-10 `wallets` with 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.
29
+ >
30
+ > **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 in `tokens` is either a full risk result (same shape as `rest.tokenRisk(mint)`, plus `as_of`) or `{ mint, error: "not_tracked" }`; untracked mints don't fail the batch, and `tokens` preserves de-duplicated input order (typed `TokenBatchRiskResponse`). PRO/ULTRA only. Plus `rest.streamSessions()` lists your live WebSocket sessions across ws-streaming + dex-stream (typed `StreamSessionsResponse`), and `rest.streamSessionKill(id)` force-releases a slot by id (typed `StreamSessionEvictResponse`) β€” the self-serve fix for a 4002 lockout when a deploy overlap leaves a ghost socket holding your slot. PRO/ULTRA only.
31
+ >
32
+ > **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 carries `progress_pct`, `velocity_pct_per_min`, `eta_minutes`, `stalled`, `real_sol_reserves`, `market_cap_usd`, `liquidity_usd`, `authorities_revoked`, `deployer_tier`, and `age_minutes` (typed `AlmostBondedResponse`). `sort` is `velocity_desc` (default) / `progress_desc` / `eta_asc`. **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route.** PRO/ULTRA only. Plus `client.tokensList({ sort })` gains four momentum sorts β€” `mc_change_5m_desc`, `mc_change_1h_desc`, `volume_1h_desc`, and `trending` (composite recent-volume Γ— positive-momentum rank).
33
+ >
34
+ > **New in 1.16.0** β€” **Token trade flow.** `client.tokenFlow(mint, { window? })` returns a trade-flow aggregate over a `1h`/`24h` window β€” `unique_wallets` / `unique_buyers` / `unique_sellers`, `buy_count` / `sell_count` / `total_trades`, `buy_sol` / `sell_sol` / `net_sol`, and a `trades_per_wallet` wash-trading proxy (typed `TokenFlowResponse`). It's an **organic-vs-fake volume** read. **KEYED (v1) β€” requires an `msk_` API key; there is no x402 route**, so x402-only clients can't reach it. PRO/ULTRA only. Deployer alerts now carry `deployers.deployer_sol_balance` β€” the deployer wallet's SOL balance at alert time (null for historical rows).
35
+ >
36
+ > **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 a `top_buyers[]` array (typed `TokenSnapshotResponse`). `rest.signalPerformance(name, { history? })` returns the **Signal Scorecard** β€” out-of-sample reliability buckets (hit_rate, base_rate, lift, sample_n, window_days) for `dump_cluster_count`, `runner_rate`, `recycled_early_buyer_count`, or `coordination_count`, with a per-day `series` when `history: true` (typed `SignalPerformanceResponse`). `rest.signals()` is the free catalog of all scored signals (typed `SignalsCatalogResponse`). `rest.tokenRisk(mint)` and `rest.tokenBuyerQuality(mint)` are now fully live server-side.
37
+ >
38
+ > **New in 1.13.0** β€” **Token risk score.** `rest.tokenRisk(mint)` returns a transparent 0–100 rug-risk/safety score (higher = riskier) with a `band` (safe/caution/danger), an explainable `factors[]` array, and the raw `inputs` (mint/freeze authority, liquidity, liq-to-MC ratio, transfer fee, launch cohort, deployer bond rate, KOL signal, blacklist). Typed as `TokenRiskResponse`. PRO/ULTRA only.
39
+ >
40
+ > **New in 1.12.0** β€” `/token/{mint}` and `/token/batch` responses now include `liquidity_to_mc_ratio`, `launch_cohort_sol`, and `launch_cohort_size`. `/tokens` gains three new filter params: `min_liq_mc_ratio`, `max_liq_mc_ratio`, and `deployer_tier`. `/tokens` list items now include `liquidity_to_mc_ratio` and `deployer_tier`. `/kol/leaderboard` entries now include `median_hold_minutes_30d` and `percentile_early_entry_30d`.
41
+ >
42
+ > **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 on `labeled_tokens` β‰₯3) plus `avg_time_to_bond_minutes`, on `DeployerAlert.deployers` and the deployer-trajectory profile.
43
+ >
44
+ > **New in 1.11** β€” **Graduation events + dump-cluster detection.** Subscribe `token:graduations` for every pump.fun bond in real time (tracked deployer or not, typed `GraduationEvent`). Buyer-quality `breakdown` adds `dump_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-band `dex:graduations` frames.
45
+
46
+ > **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. Use `sniper:deploys` WebSocket for push.
47
+ >
48
+ > **New in 1.9** β€” **Price alerts, scout leaderboard, coordination history.** `rest.priceAlertsCreate()` (PRO=5, ULTRA=25). `scoutLeaderboard()`, `kolConsensus()`, `peakHistory()`, `coordinationHistory()`. `walletStats()` now returns `derived`: win_rate, roi, verdict, biggest_miss.
49
+ >
50
+ > **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.
51
+ >
52
+ > **New in 1.7.1** *(2026-05-13)* β€” Velocity field shape corrected to match the API: `mc_change_pct`, `volume_usd`, `mev_volume_pct` are top-level on the token response, each keyed by `5m`/`15m`/`1h`/`2h`/`4h`. The 1.7.0 README documented a `velocity[window]` shape that didn't match the wire format. Runtime is unchanged β€” fix is to typed shape + docs.
53
+ >
54
+ > **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 include `liquidity_to_mc_ratio` and `deployer_tier`. Default `min_liq=2000` skips phantom-MC dust; pass `min_liq=0` to 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 by `5m`/`15m`/`1h`/`2h`/`4h`) plus `history_age_seconds`. `/token/{mint}` 400s now ship structured `code`, `reason`, `received_length`, `example`, and `docs` β€” stop guessing why a mint failed. Deprecated `avg_entry_mc_usd` fully removed.
55
+
56
+ ## Quick start (10 seconds)
57
+
58
+ ```bash
59
+ npm install madeonsol-x402
60
+ ```
61
+
62
+ ```ts
63
+ import { createClient } from "madeonsol-x402";
64
+ const client = createClient("msk_..."); // free tier at https://madeonsol.com/pricing
65
+ const { trades } = await client.kolFeed({ limit: 5 });
66
+ ```
67
+
68
+ ## Authentication
69
+
70
+ Two options:
71
+
72
+ | Method | Option | Best for |
73
+ |---|---|---|
74
+ | **MadeOnSol API key** (recommended) | `apiKey` | Developers β€” [get a free key](https://madeonsol.com/pricing) |
75
+ | x402 micropayments | `privateKey` | AI agents with Solana wallets |
76
+
77
+ > **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 free `msk_` key at [madeonsol.com/pricing](https://madeonsol.com/pricing).
78
+
79
+ ## Install
80
+
81
+ ```bash
82
+ npm install madeonsol-x402
83
+ ```
84
+
85
+ > x402 peer deps (`@x402/fetch @x402/svm @x402/core @solana/kit @scure/base`) are only needed when using `privateKey`.
86
+
87
+ ## Quick Start
88
+
89
+ ```ts
90
+ import { createClient } from "madeonsol-x402";
91
+
92
+ // Option 1: API key β€” get one free at madeonsol.com/pricing
93
+ const client = createClient("msk_your_api_key_here");
94
+
95
+ // Option 2: x402 micropayments (auto-detected when no msk_ prefix)
96
+ // const client = createClient(process.env.SOLANA_PRIVATE_KEY!);
97
+
98
+ const { trades } = await client.kolFeed({ limit: 10 });
99
+ console.log(trades);
100
+ ```
101
+
102
+ ### Advanced initialization
103
+
104
+ ```ts
105
+ import { MadeOnSolX402 } from "madeonsol-x402";
106
+
107
+ const client = new MadeOnSolX402({
108
+ apiKey: "msk_...", // OR
109
+ privateKey: "base58...", // x402 micropayments
110
+ });
111
+ ```
112
+
113
+ ## x402 Endpoints (per-request micropayments)
114
+
115
+ | Method | Description |
116
+ |---|---|
117
+ | `kolFeed(params?)` | Real-time KOL trade feed from 1,000+ tracked wallets |
118
+ | `kolCoordination(params?)` | Tokens being accumulated by multiple KOLs simultaneously |
119
+ | `kolLeaderboard(params?)` | KOL performance rankings by PnL and win rate (180 days of trade history) |
120
+ | `kolPairs(params?)` | KOL affinity matrix β€” which KOLs frequently co-trade the same tokens |
121
+ | `kolHotTokens(params?)` | KOL momentum tokens β€” accelerating KOL buy interest |
122
+ | `kolTokenEntryOrder(mint, params?)` | Ranked KOL first-buyer order for a token |
123
+ | `kolCompareWallets({ wallets })` | Side-by-side comparison of 2–5 KOL wallets |
124
+ | `kolAlertsRecent(params?)` | Live KOL alert feed β€” clusters, fresh-token buys, heating-up wallets |
125
+ | `deployerAlerts(params?)` | Pump.fun deployer alerts with KOL enrichment. PRO/ULTRA: filter by tier. |
126
+ | `walletStats(address)` | **New 1.8** Β· Wallet stats + cross-product flags (is_kol / is_alpha_tracked + bot_confidence / is_deployer). 90-day window. **$0.005** |
127
+ | `walletPnl(address)` | **New 1.8** Β· FIFO cost-basis PnL: realized + unrealized SOL, profit factor, drawdown, hold times, daily curve, closed + open positions. **$0.02** |
128
+ | `walletPositions(address)` | **New 1.8** Β· Open positions only, live unrealized from market-cap tracker. Shares /pnl cache. **$0.01** |
129
+ | `walletTrades(address, params?)` | **New 1.8** Β· Cursor-paginated raw trades with action / token / since-until filters. **$0.005** |
130
+ | `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** |
131
+ | `tokenCandles(mint, params?)` | **New 1.21** Β· OHLCV candles (1m–1d timeframes, 30d history) with per-candle volume, trade count, and market cap. **$0.01** |
132
+ | `almostBonded(params?)` | **New 1.21** Β· Launchpad tokens approaching graduation (pump.fun + LetsBonk LaunchLab) β€” bonding progress, velocity (Ξ”progress/min), ETA, deployer tier. **$0.01** |
133
+ | `tokenTopTraders(mint, params?)` | **New 1.21** Β· Wallets ranked by realized PnL (or ROI) on a token, enriched with KOL identity + alpha reputation. **$0.02** |
134
+ | `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** |
135
+ | `sniperRecent(params?)` | **New 1.21** Β· Deshred sniper deploy feed (elite/good deployers) with per-deploy snipe `footprint`. **$0.01** |
136
+ | `deployerTrajectory(wallet, params?)` | **New 1.21** Β· Deployer bond-rate trajectory β€” streaks, rolling bond rates, trend, cadence. `include: "daily_snapshots"` adds 90 days. **$0.01** |
137
+ | `discovery()` | Lists all 25 endpoints, prices, and parameter docs (free) |
138
+
139
+ ## REST API client
140
+
141
+ 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.
142
+
143
+ ```ts
144
+ import { MadeOnSolREST } from "madeonsol-x402";
145
+
146
+ const rest = new MadeOnSolREST({ apiKey: "msk_your_key" });
147
+ const { leaderboard } = await rest.alphaLeaderboard({ period: "30d", sort: "win_rate" });
148
+
149
+ // Rate-limit headers from the most recent response
150
+ console.log(rest.lastRateLimit); // { limit, remaining, reset, requestId }
151
+ ```
152
+
153
+ ### Alpha wallet intelligence
154
+
155
+ Scored from 1M+ early-buyer records (wallets seen in the first 20 buyers of Pump.fun tokens).
156
+
157
+ | Method | Tier | Description |
158
+ |---|---|---|
159
+ | `rest.alphaLeaderboard(params?)` | All | Top profitable wallets. Up to 100 on Free/Pro; ULTRA unlocks 500 + bot signals |
160
+ | `rest.alphaWallet(wallet)` | ULTRA | Full per-token breakdown + bot_signals array |
161
+ | `rest.alphaLinked(wallet)` | ULTRA | Wallets behaviorally linked (co-bought 3+ tokens within 2s) |
162
+
163
+ **alphaLeaderboard params** β€” `period` ("7d" \| "30d" \| "all"), `min_tokens` (1–20), `sort` ("win_rate" \| "pnl" \| "roi"), `exclude_bots` ("true" \| "false")
164
+
165
+ ### Token quality
166
+
167
+ | Method | Tier | Description |
168
+ |---|---|---|
169
+ | `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 }` |
170
+ | `rest.tokenCapTable(mint)` | PRO+ | First non-deployer early buyers, enriched with PnL/KOL/bot flags. PRO=10, ULTRA=20 |
171
+ | `rest.tokenBuyerQuality(mint)` | All | 0–100 buyer-quality score + full breakdown (5-min cached). Live server-side |
172
+ | `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 |
173
+ | `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 |
174
+ | `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`) |
175
+ | `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]` |
176
+ | `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) |
177
+ | `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 |
178
+ | `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 |
179
+ | `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) |
180
+ | `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) |
181
+ | `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) |
182
+
183
+ **tokenCandles params** β€” `tf` ("1m" \| "5m" \| "15m" \| "1h" \| "4h" \| "1d", default "1h"), `limit` (1–1000, default 200), `from` (ISO 8601), `to` (ISO 8601)
184
+
185
+ ```ts
186
+ // Score a basket in one request (counts as 1 against quota)
187
+ const { tokens, count } = await rest.tokensBatchRisk([mintA, mintB, mintC]);
188
+ for (const t of tokens) {
189
+ if ("error" in t) console.log(t.mint, t.error); // e.g. "not_tracked"
190
+ else console.log(t.mint, t.risk_score, t.band); // full risk result + as_of
191
+ }
192
+ ```
193
+
194
+ ### Signal Scorecard *(new in 1.15)*
195
+
196
+ 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.
197
+
198
+ | Method | Tier | Description |
199
+ |---|---|---|
200
+ | `rest.signals()` | All (free) | Catalog of scored signals β€” name, methodology, and each signal's `performance_endpoint`. No payment required |
201
+ | `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[]` |
202
+
203
+ Valid signal names: `dump_cluster_count`, `runner_rate`, `recycled_early_buyer_count`, `coordination_count`.
204
+
205
+ ```ts
206
+ const { signals } = await rest.signals();
207
+ const scorecard = await rest.signalPerformance("dump_cluster_count", { history: true });
208
+ console.log(scorecard.buckets); // [{ bucket, hit_rate, base_rate, lift, sample_n, ... }]
209
+ ```
210
+
211
+ ### KOL coordination alerts (v1.1 β€” push signals)
212
+
213
+ 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.
214
+
215
+ ```ts
216
+ // Create a rule
217
+ const { rule, webhook_secret } = await rest.coordinationAlertsCreate({
218
+ name: "fresh pump cluster",
219
+ min_kols: 4, // minimum distinct KOLs in window
220
+ window_minutes: 15, // peak-density window (1-60)
221
+ min_score: 70, // 0-100 composite score cutoff
222
+ include_majors: false, // filter WIF/BONK/POPCAT
223
+ cooldown_min: 60, // one fire per (rule,token) per 60min...
224
+ score_jump_break: 10, // ...unless score jumps +10 vs last fire
225
+ delivery_mode: "both",
226
+ webhook_url: "https://you.com/hooks/coord",
227
+ });
228
+ // β†’ store webhook_secret β€” shown ONCE
229
+ ```
230
+
231
+ `coordinationAlertsList`, `coordinationAlertsGet(id)`, `coordinationAlertsUpdate(id, params)`, `coordinationAlertsDelete(id)` round out the CRUD.
232
+
233
+ **Webhook signature:** `X-MadeOnSol-Signature: sha256=<hmac>` where `hmac = HMAC-SHA256(webhook_secret, timestamp + "." + rawBody)`, and `X-MadeOnSol-Timestamp` carries the unix seconds used.
234
+
235
+ **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.
236
+
237
+ ### KOL first-touch signal *(new in 1.3)*
238
+
239
+ 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.
240
+
241
+ **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](https://madeonsol.com/kol/scouts).
242
+
243
+ ```ts
244
+ import { MadeOnSolREST } from "madeonsol-x402";
245
+ const rest = new MadeOnSolREST({ apiKey: process.env.MADEONSOL_API_KEY! });
246
+
247
+ // S-tier scouts on tokens younger than 1h
248
+ const { events } = await rest.firstTouches({ preset: "scout", min_scout_tier: "S" });
249
+
250
+ for (const e of events) {
251
+ console.log(e.first_kol.name, "scouted", e.token_symbol, `(scout_score=${e.first_kol.scout_score}%)`);
252
+ }
253
+ ```
254
+
255
+ 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"`).
256
+
257
+ > **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_touches` WebSocket channel (PRO+) or, on Ultra, create an HMAC-signed webhook subscription.
258
+
259
+ **Webhook subscriptions (Ultra)** β€” up to 10 active per user, mirrors `coordinationAlerts`:
260
+
261
+ ```ts
262
+ const { subscription, webhook_secret } = await rest.firstTouchSubscriptionsCreate({
263
+ name: "S-tier scouts on pump tokens",
264
+ filters: { min_scout_tier: "S", mint_suffix: "pump" },
265
+ delivery_mode: "webhook",
266
+ webhook_url: "https://my.bot/hooks/scout",
267
+ });
268
+ // β†’ store webhook_secret β€” shown ONCE
269
+ ```
270
+
271
+ `firstTouchSubscriptionsList`, `firstTouchSubscriptionsGet(id)`, `firstTouchSubscriptionsUpdate(id, params)`, `firstTouchSubscriptionsDelete(id)` round out the CRUD.
272
+
273
+ ### Price alerts *(new in 1.9)*
274
+
275
+ CRUD for token dip/recovery price alerts. Fires via WebSocket (`price:alerts` channel) and/or HMAC-signed webhook when a token's market cap crosses your threshold. PRO=5 rules, ULTRA=25.
276
+
277
+ ```ts
278
+ const { alert, webhook_secret } = await rest.priceAlertsCreate({
279
+ name: "SOL dip buy",
280
+ token_mint: "So11111111111111111111111111111111111111112",
281
+ condition: "below", // "below" | "above"
282
+ threshold_mc_usd: 5_000_000_000,
283
+ cooldown_min: 120,
284
+ delivery_mode: "both",
285
+ webhook_url: "https://you.com/hooks/price",
286
+ });
287
+ // β†’ store webhook_secret β€” shown ONCE
288
+ ```
289
+
290
+ `priceAlertsList`, `priceAlertsGet(id)`, `priceAlertsUpdate(id, params)`, `priceAlertsDelete(id)` round out the CRUD.
291
+
292
+ ### Scout leaderboard & KOL consensus *(new in 1.9)*
293
+
294
+ | Method | Tier | Description |
295
+ |---|---|---|
296
+ | `rest.scoutLeaderboard(params?)` | PRO+ | Top scout-tier KOLs ranked by first-touch follow-on rate, win rate, and ROI |
297
+ | `rest.kolConsensus(params?)` | PRO+ | Tokens with the strongest KOL agreement signal β€” weighted by scout score and recent PnL |
298
+ | `rest.peakHistory(mint)` | PRO+ | Historical peak-density windows for a token β€” every coordination spike with KOL breakdown |
299
+ | `rest.coordinationHistory(params?)` | PRO+ | Global coordination event log with token, KOL count, score, and outcome |
300
+
301
+ ```ts
302
+ const { leaderboard } = await rest.scoutLeaderboard({ period: "30d", limit: 25 });
303
+ const { tokens } = await rest.kolConsensus({ min_kols: 5, period: "24h" });
304
+ ```
305
+
306
+ ### Wallet derived stats *(new in 1.9)*
307
+
308
+ `walletStats(address)` now includes a `stats` object with derived fields computed from the 90-day trade window:
309
+
310
+ ```ts
311
+ const { stats } = await rest.walletStats("WALLET_ADDRESS");
312
+ // stats.win_rate β€” fraction 0-1, tokens sold above cost basis
313
+ // stats.roi β€” aggregate return on invested SOL
314
+ // stats.verdict β€” "strong" | "profitable" | "neutral" | "losing"
315
+ // stats.biggest_miss β€” token with the highest post-exit gain the wallet missed
316
+ ```
317
+
318
+ ### Copy-trade rules
319
+
320
+ 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.
321
+
322
+ | Method | Description |
323
+ |---|---|
324
+ | `rest.copyTradeList()` | List your rules |
325
+ | `rest.copyTradeCreate(params)` | Create a rule. Returns `webhook_secret` **once** β€” store it |
326
+ | `rest.copyTradeGet(id)` | Get one rule |
327
+ | `rest.copyTradeUpdate(id, params)` | Update fields or toggle `is_active` |
328
+ | `rest.copyTradeDelete(id)` | Delete permanently |
329
+ | `rest.copyTradeSignals(params?)` | Recent fired signals (up to 7 days). Filter by `subscription_id`, `since`, `limit` (1–500) |
330
+
331
+ ### Wallet tracker
332
+
333
+ Per-account watchlist with historical swap/transfer history.
334
+
335
+ | Method | Description |
336
+ |---|---|
337
+ | `rest.walletTrackerList()` | List tracked wallets + remaining capacity |
338
+ | `rest.walletTrackerAdd(wallet, label?)` | Add a wallet |
339
+ | `rest.walletTrackerRemove(wallet)` | Remove a wallet |
340
+ | `rest.walletTrackerUpdateLabel(wallet, label)` | Update label (pass `null` to clear) |
341
+ | `rest.walletTrackerTrades(params?)` | Historical events. Params: `wallet`, `action`, `event_type`, `limit` (1–200), `before` (cursor) |
342
+ | `rest.walletTrackerSummary(params?)` | Per-wallet stats. Params: `period` ("24h" \| "7d" \| "30d"), `wallet` |
343
+ | `rest.walletStats(address)` | **New 1.8** Β· Universal wallet stats (90d) + cross-product flags. PRO+. |
344
+ | `rest.walletPnl(address)` | **New 1.8** Β· Full FIFO PnL + curve + closed/open positions. PRO+. |
345
+ | `rest.walletPositions(address)` | **New 1.8** Β· Open positions only with live unrealized. PRO+. |
346
+ | `rest.walletTrades(address, params?)` | **New 1.8** Β· Cursor-paginated raw trades. Params: `limit` (1-500), `cursor`, `action`, `token_mint`, `since`, `until`. PRO+. |
347
+ | `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+. |
348
+
349
+ ### Webhooks
350
+
351
+ | Method | Description |
352
+ |---|---|
353
+ | `rest.createWebhook(params)` | Create webhook. Returns `secret` once β€” store it for HMAC verification |
354
+ | `rest.listWebhooks()` | List your webhooks |
355
+ | `rest.getWebhook(id)` | Get one + recent delivery log |
356
+ | `rest.updateWebhook(id, params)` | Update URL, events, filters, or re-enable |
357
+ | `rest.deleteWebhook(id)` | Delete |
358
+ | `rest.testWebhook(id)` | Send test payload |
359
+
360
+ ### KOL/deployer detail
361
+
362
+ | Method | Description |
363
+ |---|---|
364
+ | `rest.kolTiming(wallet, params?)` | Entry/exit timing β€” hold duration, exit speed, hour distribution |
365
+ | `rest.kolPnl(wallet, params?)` | Per-wallet PnL breakdown |
366
+ | `rest.deployerTrajectory(wallet)` | Deployer skill curve β€” streaks, rolling bond rate, trend |
367
+ | `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) |
368
+
369
+ ### Streaming token
370
+
371
+ ```ts
372
+ const token = await rest.getStreamToken();
373
+ // token.ws_url β€” KOL/deployer streaming (Pro/Ultra)
374
+ // token.dex_ws_url β€” all-DEX trade stream (Ultra only)
375
+ ```
376
+
377
+ ### Managed streaming client *(new in 1.10)*
378
+
379
+ `rest.stream()` handles the token fetch + 24h refresh, auto-reconnect (backoff + jitter), heartbeat liveness, and typed events β€” just subscribe and listen.
380
+
381
+ ```ts
382
+ const stream = rest.stream();
383
+ stream.on("kol:trade", (t) => console.log(t.token_symbol, t.action));
384
+ stream.on("deployer:alert", (a) => console.log("new deploy", a.token_mint));
385
+ stream.subscribe(["kol:trades", "deployer:alerts"]);
386
+ // stream.unsubscribe([...]) / stream.close() when done
387
+ ```
388
+
389
+ Channels: `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`). Lifecycle events: `open`, `close`, `reconnect`, `heartbeat`, `error`. Uses the global `WebSocket` on Node 22+; on Node < 22 also `npm i ws`.
390
+
391
+ ### Live stream sessions *(new in 1.18)*
392
+
393
+ 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.
394
+
395
+ | Method | Tier | Description |
396
+ |---|---|---|
397
+ | `rest.streamSessions()` | PRO+ | List your live sessions β€” each with `id`, `service`, `tier`, `channels[]`, `connected_at`, `remote_ip`, `messages_sent`. Typed `StreamSessionsResponse` |
398
+ | `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` |
399
+
400
+ ```ts
401
+ const { sessions } = await rest.streamSessions();
402
+ for (const s of sessions) console.log(s.id, s.service, s.channels, s.messages_sent);
403
+
404
+ // Free a stuck slot after a deploy overlap
405
+ if (sessions.length) await rest.streamSessionKill(sessions[0].id); // { evicted: true, id }
406
+ ```
407
+
408
+ ## DEX Firehose (Ultra)
409
+
410
+ 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.
411
+
412
+ ```ts
413
+ import WebSocket from "ws";
414
+
415
+ const { token, dex_ws_url } = await rest.getStreamToken();
416
+ const ws = new WebSocket(`${dex_ws_url}?token=${token}`); // token MUST be in the query string
417
+
418
+ ws.on("open", () => {
419
+ ws.send(JSON.stringify({
420
+ type: "subscribe",
421
+ sub_id: "fresh-pumpfun",
422
+ replay: 50, // up to 500 from ring buffer
423
+ filters: {
424
+ dex: "pumpfun", // pumpfun | pumpamm | pumpswap | raydium | jupiter | orca | meteora | launchlab
425
+ token_age_max_seconds: 300,
426
+ min_sol: 0.5,
427
+ action: "buy",
428
+ },
429
+ }));
430
+ });
431
+
432
+ ws.on("message", (raw) => {
433
+ const msg = JSON.parse(raw.toString());
434
+ if (msg.channel === "dex:trades") {
435
+ // { sub_id, data: { wallet, mint, action, sol_amount, dex, ... }, replay, ts }
436
+ }
437
+ });
438
+ ```
439
+
440
+ **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.
441
+
442
+ Full protocol reference: [madeonsol.com/api-docs#streaming](https://madeonsol.com/api-docs#streaming).
443
+
444
+ ## Rate-limit headers
445
+
446
+ 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`:
447
+
448
+ ```ts
449
+ await rest.alphaLeaderboard();
450
+ const { limit, remaining, reset, requestId } = rest.lastRateLimit;
451
+ if (remaining !== null && remaining < 5) {
452
+ console.warn(`Throttle warning β€” ${remaining}/${limit} requests left until ${reset}`);
453
+ }
454
+ ```
455
+
456
+ ## Discovery
457
+
458
+ ```ts
459
+ const info = await client.discovery();
460
+ console.log(info.endpoints); // all endpoints with prices and params
461
+ ```
462
+
463
+ Docs: [madeonsol.com/solana-api](https://madeonsol.com/solana-api)
464
+
465
+ ## Also Available
466
+
467
+ | Platform | Package |
468
+ |---|---|
469
+ | TypeScript SDK | [`madeonsol`](https://www.npmjs.com/package/madeonsol) on npm |
470
+ | Rust SDK | [`madeonsol`](https://crates.io/crates/madeonsol) on crates.io |
471
+ | Python (LangChain, CrewAI) | [`madeonsol-x402`](https://pypi.org/project/madeonsol-x402/) on PyPI |
472
+ | MCP Server (Claude, Cursor) | [`mcp-server-madeonsol`](https://www.npmjs.com/package/mcp-server-madeonsol) Β· [Smithery](https://smithery.ai/servers/madeonsol/solana-kol-intelligence) Β· [Glama](https://glama.ai/mcp/servers/madeonsol/mcp-server-madeonsol) |
473
+ | ElizaOS | [`@madeonsol/plugin-madeonsol`](https://www.npmjs.com/package/@madeonsol/plugin-madeonsol) |
474
+ | Solana Agent Kit | [`solana-agent-kit-plugin-madeonsol`](https://www.npmjs.com/package/solana-agent-kit-plugin-madeonsol) |