API reference
The backend is a thin, public read layer over the chain plus a
relay for already-signed transactions. Everything below is
unauthenticated and rate-limited (one exception: posting news,
which is wallet-signature gated, see POST /api/news).
Build a bot, a dashboard, or your own frontend against it if you want.
Base URL is wherever the frontend is served from. These examples
assume same-origin requests (/api/...).
GET /api/health
Indexer health check. Returns 503 if the indexer hasn't polled recently or found the curve yet.
{
"ok": true,
"cluster": "devnet",
"curveFound": true,
"lastPolledAt": 1783956542549,
"staleMs": 812,
"lastError": null,
"tradesIndexed": 21
}
GET /api/curve
The current curve snapshot, plus who's winning the jackpot right now (if anyone has bought yet) and who won it (once complete).
{
"programId": "...",
"cluster": "devnet",
"decimals": 6,
"curve": {
"exists": true,
"address": "...",
"authority": "...",
"mint": "...",
"vault": "...",
"virtualSolReserves": "30162179250",
"virtualTokenReserves": "1067230578122766",
"realSolReserves": "162179250",
"realTokenReserves": "994230578122766",
"targetSolReserves": "85000000000",
"isComplete": false,
"bump": 253
},
"currentLeader": { "trader": "...", "signature": "...", "blockTime": 1783951139 },
"jackpotWinner": null,
"asOf": 1783956824361
}
Before anyone has ever bought, curve.exists is
false and the reserve fields reflect the default shape
the first buy will auto-initialize, enough to quote a price without
an on-chain account existing yet. All numeric reserve fields are
strings (they're u64s on-chain, too large for a plain
JS number).
GET /api/trades
Recent trade history, newest first.
limit: 1-200, default 50. offset: default 0.
{
"trades": [
{
"signature": "...",
"blockTime": 1783951139,
"mint": "...",
"trader": "...",
"isBuy": true,
"solAmount": "500000000",
"tokenAmount": "17845234123"
}
],
"total": 21
}
GET /api/news
The news feed: an auto-generated recap posted the instant each game's jackpot pays out, plus admin announcements. Newest first.
gameId: optional, scopes to one game's posts. limit: 1-100, default 20.
{
"posts": [
{
"id": "...",
"type": "auto",
"gameId": "1",
"title": "Game #1 complete: 3eGj...Fzsu takes the jackpot",
"body": "Game #1 wrapped up after 12 trades from 8 unique wallets...",
"authorWallet": null,
"meta": {
"winner": "...", "winnerAmount": "1751377600", "houseAmount": "500393600",
"rebatePool": "250196800", "realSolReserves": "2000000000",
"targetSolReserves": "2000000000", "totalBuyVolume": "2501968000",
"totalTrades": 12, "uniqueTraders": 8, "signature": "..."
},
"createdAt": 1783956824361
}
],
"total": 1
}
type is "auto" for the end-of-game recap
(with meta) or "admin" for a manually
posted announcement (meta is null,
authorWallet is set instead).
The one authenticated endpoint here: it posts an admin announcement.
Unlike every other write in this app, this isn't an on-chain
transaction: the caller signs
`ClashTX News Post\n${timestamp}\n${title}\n${body}`
with their wallet (no gas, no confirmation wait), and the backend
checks that signature against whichever wallet is currently
GameRegistry.admin. Body: { title, body, wallet, timestamp, signature }
(signature base64, timestamp epoch ms,
valid for 5 minutes).
GET /api/candles
OHLC price candles, bucketed from trade history. Computed on request, not cached.
interval: bucket size in seconds, minimum 5, default 60.
[
{ "time": 1783951020, "open": 2.88e-8, "high": 2.88e-8, "low": 2.88e-8, "close": 2.88e-8 },
{ "time": 1783951080, "open": 3.93e-8, "high": 5.80e-8, "low": 3.93e-8, "close": 5.80e-8 }
]
GET /api/wallet/:pubkey
SOL and token balance for any address, for the current curve's mint, plus their rebate pool status, if they've ever bought.
{
"address": "...",
"lamports": 1695961213,
"tokenAccount": "...",
"tokenAmountRaw": "17845234123",
"tokenUiAmount": 17845.234123,
"rebate": {
"totalSolIn": "500000000",
"claimed": false,
"claimableRebate": "35714285"
}
}
rebate.claimableRebate is only non-zero once the curve
has completed. Before that, or if this address never bought,
totalSolIn is "0". See
The Jackpot for how the pro-rata math works.
GET /api/blockhash
A fresh blockhash for building a transaction client-side.
{ "blockhash": "...", "lastValidBlockHeight": 349812345 }
Relays an already wallet-signed transaction. The backend never signs
anything. It only checks every instruction targets an allowed
program (this program, System, Token, Associated Token, Compute
Budget) before broadcasting, so it can't be used as an open relay.
buy, sell, and claim_rebate are
all just program instructions. There's no separate REST endpoint for
claiming a rebate; it goes through this same relay like any other
transaction.
// request
{ "signedTransaction": "<base64>" }
// response
{ "signature": "..." }
GET /api/stream
Server-Sent Events. Emits {"type":"trade"} the moment the
indexer sees a new trade (pushed from a websocket log subscription,
not polling). Subscribe instead of polling /api/curve
or /api/trades on a timer if you want near-instant
updates.
const es = new EventSource("/api/stream");
es.onmessage = () => {
// re-fetch /api/curve, /api/trades, /api/candles
};
GET /api/sol-price
Current SOL/USD price, for display purposes only.
{ "solUsd": 178.42 }