Solutions · Trading terminals
MadeOnSol supplies the market-data side of a Solana trading terminal: REST endpoints for chart history, token and trader panels, the token:prices and token:candles WebSocket channels for the price header and chart, and the DEX firehose for the trade tape; order entry stays in your product.
A terminal combines live trades, prices and candles, token and trader context, history and the views each user sets up. Your frontend renders state; it does not decode Solana transactions.
Price, candle, risk and wallet-score channels from Pro; the per-trade tape on the DEX firehose in Ultra and Business. Showing the data to your own users needs Business.
One screen, four data sources
Each pane loads over REST, then stays current on a stream.
Chart
token:candlesPro
GET /tokens/{mint}/candlesPro
Price header
token:pricesPro
GET /token/{mint}Free
Trade tape
dex:tradesUltra and Business
GET /tokens/{mint}/tradesPro
Trader panel
wallet:scoresPro
GET /wallet/{address}Pro
The problem
One terminal screen is several data products at once. The chart needs history plus a live bar, the tape needs every trade on the token, the header needs the current price, and the panels need holders, risk and trader context. From raw Solana data that means decoding each venue, pricing tokens, aggregating candles and keeping every pane consistent after a reconnect.
Show me
One token, five steps, in the order a terminal opens it. Keys are what the API sends; values are illustrative.
One REST call fills the header and panel. This route has no plan gate, so a Free key returns it too.
// GET /api/v1/token/8vdc...pump Authorization: Bearer msk_...
{
"token": {
"mint": "8vdc...pump",
"name": "Example",
"symbol": "EXMPL",
"price_usd": 0.0000428,
"market_cap": 42800,
"liquidity_usd": 21870,
"primary_dex": "pumpfun",
"launchpad": "pumpfun",
"mint_authority_revoked": true,
"freeze_authority_revoked": true,
"volume_24h_usd": 58210.4,
"trades_24h": 1462,
"price_age_seconds": 5,
"price_is_stale": false
},
"as_of": "2026-09-23T10:02:05.120Z"
}History comes from REST in 1m, 5m, 15m, 1h, 4h, 1d. Note the field names: t, open, close here; bucket_start and *_price_usd on the stream.
// GET /api/v1/tokens/8vdc...pump/candles?tf=1m&limit=3
{
"mint": "8vdc...pump",
"timeframe": "1m",
"from": "2026-09-23T09:59:05.000Z",
"to": "2026-09-23T10:02:05.000Z",
"count": 2,
"net_flow_included": false,
"candles": [
{ "t": "2026-09-23T09:59:00+00:00", "open": 0.0000398, "high": 0.0000414, "low": 0.0000396, "close": 0.0000411, "volume_usd": 1204.7, "trades": 29, "market_cap_usd": 41100 },
{ "t": "2026-09-23T10:00:00+00:00", "open": 0.0000411, "high": 0.0000415, "low": 0.0000407, "close": 0.0000412, "volume_usd": 988.1, "trades": 24, "market_cap_usd": 41200 }
]
}One named subscription carries both channels; filters.mints scopes them. Price, candle and risk mints each have their own per-connection budget: 25 on Pro, 100 on Ultra and 250 on Business. Over the cap the channel is rejected, never truncated.
// wss://madeonsol.com/ws/v1/stream (token from POST /api/v1/stream/token)
{
"type": "subscribe",
"sub_id": "chart-8vdc",
"channels": ["token:prices", "token:candles"],
"filters": { "mints": ["8vdc...pump"], "updates": true }
}The last minute not yet stored at the REST call arrives as candle:closed; ticks and the open bar follow. Closed candles carry id and seq; price ticks and candle:update are state and carry neither.
[
{
"channel": "token:candles",
"sub_id": "chart-8vdc",
"event": "candle:closed",
"id": "candle:solana:8vdc...pump:1790157660",
"seq": 184311,
"data": {
"chain": "solana",
"mint": "8vdc...pump",
"bucket_start": "2026-09-23T10:01:00.000Z",
"bucket_end": "2026-09-23T10:02:00.000Z",
"closed_at": "2026-09-23T10:02:14.402Z",
"open_price_usd": 0.0000412,
"high_price_usd": 0.0000431,
"low_price_usd": 0.0000409,
"close_price_usd": 0.0000428,
"close_mc_usd": 42800,
"volume_usd": 1843.2,
"trades": 37,
"buy_count": 22,
"sell_count": 15,
"final": true,
"source": "token_ohlc_1m"
},
"ts": 1790157734402
},
{
"channel": "token:prices",
"sub_id": "chart-8vdc",
"event": "token:price",
"data": {
"mint": "8vdc...pump",
"price_usd": 0.0000431,
"vwap_price_usd": 0.0000429,
"market_cap_usd": 43100,
"fdv_usd": 43100,
"liquidity_usd": 21950,
"primary_dex": "pumpfun",
"primary_pool_address": "Gf7h...2kLm",
"mc_change_pct": 0.7,
"volume_usd": 58310.9,
"mev_volume_pct": 3.1,
"history_age_seconds": 86400,
"source": "mc-tracker"
},
"ts": 1790157751120
},
{
"channel": "token:candles",
"sub_id": "chart-8vdc",
"event": "candle:update",
"data": {
"chain": "solana",
"mint": "8vdc...pump",
"bucket_start": "2026-09-23T10:02:00.000Z",
"bucket_end": "2026-09-23T10:03:00.000Z",
"open_price_usd": 0.0000428,
"high_price_usd": 0.0000433,
"low_price_usd": 0.0000426,
"close_price_usd": 0.0000431,
"close_mc_usd": 43100,
"volume_usd": 912.4,
"trades": 18,
"final": false,
"as_of": "2026-09-23T10:02:31.120Z",
"source": "mc-tracker:open_candle"
},
"ts": 1790157751180
}
]The firehose is a second socket with its own filters. min_sol: 0 includes trades under the default floor, marked dust: true. Available on Ultra and Business.
// wss://madeonsol.com/ws/v1/dex-stream
// sent: {"type":"subscribe","sub_id":"tape-8vdc","filters":{"token_mints":["8vdc...pump"],"min_sol":0},"replay":50}
{
"channel": "dex:trades",
"sub_id": "tape-8vdc",
"data": {
"wallet": "7vfC...rXs2",
"mint": "8vdc...pump",
"action": "buy",
"sol_amount": 2.5,
"token_amount": 12354830.7,
"executed_price_sol": 0.00000020235,
"tx_signature": "5K7j...",
"slot": 449476901,
"block_time": 1790157751,
"dex": "pumpfun",
"dust": false
},
"ts": 1790157751412
}| State | Written by | Merge on | After a reconnect |
|---|---|---|---|
| Chart bars | GET /tokens/{mint}/candlescandle:closedcandle:update | REST t = stream bucket_start. Map open, high, low, close to the *_price_usd fields. A candle:update (final: false) redraws the open bar; the candle:closed for that minute replaces it. | Resume replays closed candles from storage. candle:update is never replayed and has no snapshot, so the open bar returns with the next price move. |
| Price header and token panel | GET /token/{mint}token:price | mint; the newest ts wins. The REST body fills the panel once, then ticks update price_usd, market_cap_usd and liquidity_usd. | A fresh subscribe sends no price snapshot. A resume sends one token:price frame per watched mint with snapshot: true. |
| Trade tape | dex:tradesGET /tokens/{mint}/trades | tx_signature; prepend newest first. executed_price_sol is in SOL, the header price in USD. | Resubscribe with replay (up to 500 recent trades from the server buffer) and drop signatures you already hold. The firehose has no resume cursor. |
Try it
A Free key calls GET /api/v1/token/{mint} right away, the same response step 1 shows. The price and candle channels start on Pro and the tape on Ultra and Business; the streaming reference lists each channel's scope and recovery.
Stream or request
Stream what moves while a user watches; request what a user opens. Every row names the real channel and endpoint, with the plan each one starts on.
| Data | Stream | REST |
|---|---|---|
| Price and market cap | token:pricesProtoken:priceWhile the token is on screen. Ticks are coalesced per mint, not one per trade. | GET /api/v1/token/{mint}FreeWhen the panel opens: the stream sends nothing until the next price move. |
| Candles | token:candlesProcandle:closed · candle:update1-minute bars as they close; with updates: true, the open minute at most once per second per token. | GET /api/v1/tokens/{mint}/candlesProChart history and every other timeframe: 1m, 5m, 15m, 1h, 4h, 1d. |
| Trades | dex:tradesUltra and BusinessParsed swaps on the token across 14 DEX programs, filtered by token_mints (up to 50 per subscription). | GET /api/v1/tokens/{mint}/tradesProOlder tape, paged by cursor, for the pump.fun pipeline (pump.fun, LaunchLab/bonk, bags) only. |
| Risk inputs | token:riskProrisk:authority_changed · risk:supply_inflatedMint or freeze authority revoked, a transfer fee changed, supply drift crossing a level. Not a score stream. | GET /api/v1/tokens/{mint}/riskProThe 0-100 score and its factors, computed at read time. |
| Trader context | wallet:scoresProdeployer:tier_changed · kol:score_state_changedDeployer tier and KOL state changes for the wallets you name. | GET /api/v1/wallet/{address}ProWhen a user clicks a trader on the tape: KOL, alpha-wallet, bot and deployer flags. |
| Holders | REST onlyNo stream. | GET /api/v1/tokens/{mint}/holdersProA live holder census with concentration shares, on demand. |
Scoped channels take a list of mints or wallets per connection: 25 on Pro, 100 on Ultra and 250 on Business, each channel its own budget. dex:trades runs on the DEX firehose, a separate socket.
Architecture
Keep the API key and stream token out of the browser: your backend holds the connections and relays state to each viewer. Connections per plan are limited, so one upstream socket feeds every viewer of a token.
1 · MadeOnSol
DEX event
A swap on one of the decoded venues
2 · MadeOnSol
MadeOnSol
Decoded trade, price tick, 1-minute candle
3 · Yours
Your backend
Holds the key and stream token; one upstream socket per stream
4 · Yours
App state
Bars by bucket_start, tape by tx_signature, header by mint
5 · Yours
Chart · tape · panel
Rendered from state, not from raw frames
/api/v1 through the same backend (cache them per token) and seed the state the streams then update.Display rights
Lower plans are licensed for internal use: your own trading, research and tooling. The Business embed licence lets one branded product (its web app, mobile apps and companion bot count as one) display the data to its end users, with a visible "Powered by MadeOnSol" notice near the data. It does not cover offering raw responses or bulk data for download.
Redistribution or white-label: contact us about Enterprise.
Build outcomes
Coverage
Read from the service code, not from marketing copy. For measured delivery and uptime, see the benchmarks, the API status page and data integrity.
pump.fun
1 program
PumpSwap
1 program
Raydium
4 programs
Jupiter
1 program
Orca
1 program
Meteora
4 programs
LaunchLab
1 program
Moonshot
1 program
token:pricesPrice header · filters.mints
token:price
token:candlesChart · filters.mints
candle:closed · candle:update
token:riskRisk badge · filters.mints
risk:authority_changed · risk:supply_inflated
wallet:scoresTrader panel · filters.wallets
deployer:tier_changed · kol:score_state_changed
Keep going
Solution
DEX Data
Every filter and frame of the DEX firehose.
Solution
Wallet Tracking
Watchlist events behind a trader panel.
Solution
Trading Bots
Launch, KOL and copy-trade signals.
Product
Business plan
The embed licence for customer-facing products.
Guide
Price, holder, volume and liquidity panels
A token dashboard in code.
Guide
A live KOL dashboard in Next.js
REST plus the stream in a web UI.
FAQ
Next step
Check how the data is verified, call the panel endpoint on a free key, wire the streams, and pick the plan that lets you show it.