Solutions · Onchain analytics
MadeOnSol serves Solana analytics products over two paths that share field names: REST endpoints for history and intelligence (launchpad trade tapes, 1-minute candles, KOL trades, deployer outcomes, point-in-time reputation) and WebSocket channels or signed webhooks for the events that change that state.
A dashboard needs yesterday's numbers and today's changes in the same shape. Load history and intelligence once, then apply each event to that state instead of re-scanning the chain.
A Free key reads deployer outcome stats, leaderboards, token context and dataset manifests, and the KOL feed 5 minutes delayed. Trade history, candles, point-in-time endpoints and live channels from Pro; the per-trade DEX firehose on Ultra and Business; monthly Parquet exports on Business.
Four layers, one set of keys
One question per layer, the surface that answers it, and its plan from the route or channel gate.
History
What happened before the dashboard opened?
GET /tokens/{mint}/tradesProhistory.postgres_fromhistory.archive_usedcoverage.eligibility
Intelligence
How do launches by each kind of deployer end?
GET /deployer-hunter/alert-statsFreetiers.elite.bond_ratebond_rate.total_bondedmultiplier.pct_10x
Live
What changed since the last read?
deployer:alerts channelProdeployer:alertdeployer:bondid
Provenance
Which dataset state produced this number?
GET /manifestsFreefingerprintschema_hashdata_as_of
The problem
An analytics product is two systems: a history you can query and a live feed that keeps it current. Built on raw chain data, that means decoding each launchpad and venue, attributing launches to deployers and trades to known traders, storing months of rows, and making the live numbers agree with the stored ones after every restart.
Show me
One dashboard panel: launch outcomes by deployer tier over 30 days. REST gives the starting state, the stream moves it, a timer reconciles. Keys are the route's and the emitters' own; values are illustrative and bodies abridged.
alert-stats counts every deploy and bond alert in the window and breaks them out by tier (elite, good, rising). It works on a Free key.
// GET /api/v1/deployer-hunter/alert-stats?period=30d
{
"bond_rate": { "total_deploys": 3120, "total_bonded": 702, "rate": 22.5 },
"tiers": {
"elite": { "deploys": 210, "bonded": 131, "bond_rate": 62.4, "avg_multiplier": 12.4, "total_with_mc": 81 },
"good": { "deploys": 288, "bonded": 122, "bond_rate": 42.4, "avg_multiplier": 9.8, "total_with_mc": 86 },
"rising": { "deploys": 371, "bonded": 109, "bond_rate": 29.4, "avg_multiplier": 6.1, "total_with_mc": 79 }
},
"period": "30d",
"sampled_rows": 452,
"truncated": false
}Subscribe to deployer:alerts (from Pro). Each deployer:alert adds a deploy, each deployer:bond a bond, under the tier in the frame.
import { MadeOnSolREST } from "madeonsol-x402";
const rest = new MadeOnSolREST({ apiKey: process.env.MADEONSOL_API_KEY! });
// 1 · Initial state: the last 30 days of launch outcomes by deployer tier
let state = await rest.deployerAlertStats({ period: "30d" });
// 2 · Stay live: one frame moves one counter
const stream = rest.stream();
stream.on("deployer:alert", (a) => {
state.bond_rate.total_deploys += 1;
const t = state.tiers[a.deployer_tier];
if (t) t.deploys += 1;
});
stream.on("deployer:bond", (b) => {
state.bond_rate.total_bonded += 1;
const t = state.tiers[b.deployer_tier];
if (t) t.bonded += 1;
});
stream.subscribe({ subId: "launch-outcomes", channels: ["deployer:alerts"] });
// 3 · Reconcile: the window slides and deployers change tier
setInterval(async () => {
state = await rest.deployerAlertStats({ period: "30d" });
}, 5 * 60_000);A launch by an elite deployer, then its bond 18 minutes later. Both frames carry a stable id, so a replayed frame never counts twice.
[
{
"channel": "deployer:alerts",
"sub_id": "launch-outcomes",
"event": "deployer:alert",
"id": "deployer:alert:918273",
"seq": 402551,
"data": {
"alert_id": 918273,
"alert_type": "new_deploy",
"deployer_wallet": "7vfC...rXs2",
"deployer_tier": "elite",
"token_mint": "8vdc...pump",
"token_symbol": "NEW",
"launchpad": "pumpfun",
"bonding_rate": 0.64,
"total_bonded": 41,
"total_deployed": 64,
"market_cap_at_alert": 6200,
"tx_signature": "3xQm...",
"slot": 449476601
},
"ts": 1790330400000
},
{
"channel": "deployer:alerts",
"sub_id": "launch-outcomes",
"event": "deployer:bond",
"id": "deployer:bond:8vdc...pump",
"seq": 402987,
"data": {
"deployer_wallet": "7vfC...rXs2",
"deployer_tier": "elite",
"token_mint": "8vdc...pump",
"token_symbol": "NEW",
"launchpad": "pumpfun",
"time_to_bond_minutes": 18,
"instant_bond": false,
"bonding_rate": 0.65,
"total_bonded": 42,
"total_deployed": 64,
"market_cap_at_alert": 69400
},
"ts": 1790331480000
}
]the tiers need not sum to the totals
Totals count every alert in the window. The tier rows count alerts under each deployer's current tier, so deployers that have since dropped out of the tracked tiers are in the totals only.
sampled_rows and truncated
The multiplier statistics are computed over every alert with a market cap of at least $500 unless a safety ceiling is hit, and truncated says whether it was.
rest.deployerAlertStats({ period: "30d" })rest.tokenTrades(mint, { since, until })rest.deployerAsOf(wallet, { date })client.deployer_alert_stats(period="30d")client.token_trades(mint)madeonsol_deployer_alert_statsKeeping them in agreement
Events only add. The REST aggregate is recomputed from storage on each read. These are the cases where the two part ways, and the fix for each.
| When | What happens to your numbers | What to do |
|---|---|---|
| A deployer:alert or deployer:bond frame arrives | One deployer_alerts row was inserted just before the frame, so one counter moves by one: deploys for an alert, bonded for a bond, under the frame's deployer_tier. | Count each id once. The SDK drops ids it saw recently; a counter that survives restarts should store the ids it applied. |
| A deployer changes tier | REST counts each alert under the deployer's current tier; a frame keeps the tier it had when it was sent. The per-tier numbers drift apart while the totals still agree. | Re-read REST on a timer. To see the change as it happens, the wallet:scores channel sends deployer:tier_changed for the deployer wallets you list. |
| The period window slides | Alerts older than the 7d or 30d window leave the REST count; events only ever add. "all" is bounded too: alerts are kept 365 days. | Re-read REST at least as often as your dashboard's resolution. |
| You re-read REST | The aggregate is cached for 60 seconds, so a fresh read can trail the stream by up to a minute. | Keep applying frames that arrive after the read, and compare with a minute of slack. |
| The socket drops | On reconnect the SDK resumes from the last frame your handlers finished. The server replays from memory or rebuilds up to 60 minutes and 2,000 rows per channel from storage; rebuilt deployer frames carry the deployer's current stats, and replay_end says whether recovery was complete. | When replay_end reports complete: false or the SDK emits a gap, re-read REST instead of trusting the counters. |
Streaming reference for resume, replay_end and gap reporting.
Try it
The public demo key on the API docs calls GET /api/v1/deployer-hunter/alerts with no signup (20 calls per hour per IP). Those alert rows, alert_type new_deploy and bonded, are what alert-stats counts and what the deployer:alerts channel pushes. A free key adds alert-stats itself, the leaderboards and GET /token/{mint}.
Beyond raw RPC
An RPC node returns transactions and account state. The questions an analytics product asks need attribution, prices you can reproduce, coverage you can read and a record of what was known when.
deployer_tierbonding_ratetime_to_bond_minutestiers.good.bond_rate
price_solmarket_price_solsol_amounttoken_amount
coverage.history_startcoverage.eligibilitycoverage.completeness
snapshot.tiersnapshot.bonding_ratesnapshot.carriedfirst_snapshot_date
history.archive_monthshistory.truncatedX-Read-Source
X-Request-Idfingerprintschema_hash
Choosing the surface
Plans come from each route's own gate and the channel table. For every swap on a venue, see DEX Data; this page covers what sits around it. Endpoint catalog
| Product need | Use | Where | Plan |
|---|---|---|---|
| Initial dashboard state | REST aggregates and lists, read once per view and on a timer | GET /deployer-hunter/alert-statsGET /kol/leaderboardGET /token/{mint} | From Free |
| Live updates to that state | WebSocket channels, or signed webhooks when your backend cannot hold a socket | deployer:alertskol:tradestoken:candles | From Pro; webhooks from Pro |
| Every swap on the venues you chart | The DEX firehose, a separate WebSocket with server-side filters | dex:trades on /ws/v1/dex-stream | Ultra and Business |
| Wallet activity | The Wallet Tracker for a watchlist; the Universal Wallet API for any address | GET /wallet-tracker/tradesGET /wallet/{address}/tradeswallet_tracker:events | From Pro; the channel from Ultra |
| Token context | One call for price, liquidity, deployer and KOL activity; per-token flow and candles | GET /token/{mint}GET /tokens/{mint}/flowGET /tokens/{mint}/candles | Free for /token; Pro for flow and candles |
| Trader context | KOL records and alpha-wallet scores, keyed by wallet | GET /kol/leaderboardGET /alpha/leaderboardGET /wallet/{address} | Free for the leaderboards; Pro for any address |
| Backtests without look-ahead | Point-in-time reputation and wallet flags | GET /deployer-hunter/{wallet}/as-ofGET /wallet/{address}/flags?as_of= | From Pro |
| Whole closed months in your warehouse | Presigned links to zstd Parquet files | GET /exports | Business |
| Field and dataset definitions | The data dictionary, and the nightly manifests behind it | /data-dictionaryGET /manifests | No key for the page; Free for manifests |
Historical data
MadeOnSol keeps the datasets it models, each with a stated start and window. It is not an index of every Solana program: trade history covers launchpad tokens, and anything outside the table below needs another source. Data dictionaryHow the archive is verified
| Dataset | From | Window and scope | Read it with | Plan |
|---|---|---|---|---|
| Launchpad trade tape | 2026-04-12 | No retention window. Scope: the pump.fun pipeline (pump.fun, LaunchLab/bonk, bags), while each mint is inside its capture window. Older closed months come from the Parquet archive through the same endpoint. | GET /tokens/{mint}/trades | Pro |
| 1-minute candles | June 2026 | No retention window, no earlier backfill: candles are built as trades arrive and cannot be rebuilt later. Pro reads the last 30 days; Ultra the full history plus buy/sell volume and liquidity. | GET /tokens/{mint}/candles | Pro |
| KOL trades | No window since 2026-06-05 | No retention window since 2026-06-05; rows older than the 180-day window that applied before were already deleted. | GET /kol/feed | Free (5 min delayed) |
| Deployer alerts and bonds | Rolling 365 days | Alerts older than 365 days are pruned, so alert-stats with period=all covers that window. | GET /deployer-hunter/alerts, /alert-stats | Free (alerts 5 min delayed) |
| Deployer reputation as of a date | Per deployer (first_snapshot_date) | Write-on-change snapshots, kept with no window. One known gap: 2026-07-10 has no snapshot and folds into the next day. | GET /deployer-hunter/{wallet}/as-of, /history | Pro |
| Wallet flags as of a date | 2026-08-28 | Write-on-change, kept with no window. Nothing earlier: before that date only the current flags exist. | GET /wallet/{address}/flags?as_of= | Pro |
| Wallet Tracker events | Rolling 120 days | Events of your watched wallets older than 120 days are pruned. | GET /wallet-tracker/trades | Pro |
| Monthly Parquet files | Every fully archived closed month | token_trades, rhc_trades, token_ohlc_1m, rhc_ohlc_1m. The open month is never exported; read it over REST. | GET /exports | Business |
Architecture
1 · Source
Chain activity
Transactions and program events
2 · MadeOnSol
Data and intelligence layer
3 · Delivery
4 · Yours
Your state store and reconcile job
Keys and business logic stay server-side
5 · Users
Your dashboard or research tool
Build outcomes
Showing MadeOnSol data to your own users requires a Business plan. See pricing
Keep going
Solution
DEX Data
The per-trade firehose, when the dashboard needs every swap.
Solution
Smart Money
KOL and alpha-wallet records to rank and chart traders.
Solution
Token Risk
Risk factors and deployer history per token.
Product
On-chain datasets
Schemas and free samples of the packaged datasets.
Guide
Solana datasets for quant research
Citation-ready data for studies.
Guide
Backtest a copy-trade strategy
Priced KOL trades in a backtest.
FAQ
Next step
Check how the data is kept and verified, look at it in the browser, then pick the plan that carries your history and channels.