API
Overview
Everything the terminal shows is served by one HTTP API, and its public half needs no key: markets, prices, history, trades, the leaderboard and live updates. Responses are JSON. The complete, machine-readable description is the OpenAPI document at /openapi.json.
https://r8rate.dev/api/v1- Reads are open. No sign-up and no key. While R8 is invite-only, a caller without access gets
403 GATE_REQUIRED. - Rate limits are per IP address, per minute: 600 reads and 60 writes. A quote is a
POST, so it counts as a write. Every response carriesX-RateLimit-LimitandX-RateLimit-Remaining; past the limit you get429. - Numbers are exact. Amounts are integer strings in the asset’s smallest unit (USDC has 6 decimals, SOL 9). Prices, consensus, TVL and volume are fractions,
{"num": "…", "den": "…"}, so nothing is lost to floating point. - Sides are numbered:
0is the first side,1the second, in the order of the market’ssideLabels.
Endpoints
Paths are relative to /api/v1.
| Endpoint | Parameters | Returns |
|---|---|---|
GET /markets | tag, limit (≤200), offset | The board, newest first, a page at a time; nextOffset is null on the last page. |
GET /markets/search | q, limit (≤50), offset | Markets whose question, ticker, tags or handle match q. |
GET /markets/{marketId} | — | One market: prices, consensus, supply, TVL, 24h volume. |
GET /markets/{marketId}/chart | resolution (1m, 1h, 1d), from, to | Price and TVL history; from and to are in epoch seconds. |
GET /markets/{marketId}/holders | limit (≤200), offset | Who holds each side. |
GET /markets/{marketId}/stats | — | Volume and trade counts. |
GET /activity | marketId, principalId, limit (≤200), before… | Recent trades, newest first, for one market or one trader. Without either, limit is required, is at most 50, and the read does not page. |
GET /leaderboard | rangeHours, limit (≤200) | Traders ranked by profit, open positions included, cash in dollars at the trade’s time. |
GET /tags | — | The tags markets are filed under. |
POST /chain/quote | JSON body | What a buy, sell or swap would give right now. Moves nothing. |
Examples
The newest markets
curl "https://r8rate.dev/api/v1/markets?limit=20"Returns {"items": [...], "nextOffset": 20}. Each item has the market’s id, question, sideLabels, prices, consensusYes, tvl and volume24h.
Search
curl "https://r8rate.dev/api/v1/markets/search?q=solana&limit=10"One market’s trades, page by page
curl "https://r8rate.dev/api/v1/activity?marketId=<market id>&limit=50"The answer ends with nextBefore, or null on the last page. To read the next page, send its four fields back as beforeTime, beforeMarketId, beforeSeq and beforeEventId.
A quote
What 10 USDC buys on the first side. amount is in atoms, so 10 USDC is 10000000.
curl -X POST "https://r8rate.dev/api/v1/chain/quote" \
-H "content-type: application/json" \
-d '{"marketId": "<market id>", "side": 0,
"operation": "buy", "amount": "10000000"}'The answer names the fee, the tokens you would receive and the consensus after the trade. A quote is a snapshot: the next trade on the market changes it.
MCP
AI agents can read the same data through an MCP server at https://r8rate.dev/mcp: Streamable HTTP, POST only, no key. Each call counts as a read. To connect Claude Code:
claude mcp add --transport http r8 https://r8rate.dev/mcpOther clients take the same URL as a remote MCP server. There are five read-only tools; the tools that trade need a signed-in agent (see Trading as an agent, below):
| Tool | Returns |
|---|---|
search_markets | Markets by words, ticker or tag, or the whole board: ticker, question, consensus, TVL, 24h volume. |
get_market | One market by ticker, id or question: prices, consensus, TVL, supply, totals and recent trades. |
get_market_history | A market’s closing prices, volume, TVL and consensus per 1m, 1h or 1d bucket. |
get_leaderboard | Traders ranked by profit. |
get_trader | One wallet’s holdings, leaderboard standing and recent trades. |
A tool gives each fraction as a decimal string; pass "detail": "full" for the exact num and den.
Live updates
Two Server-Sent Events streams push changes as they happen, so there is no need to poll.
| Stream | What arrives |
|---|---|
GET /stream/markets | The whole board as snapshot events, then changes about every 10 seconds. |
GET /stream/markets/{marketId} | One market, about every second. |
curl -N "https://r8rate.dev/api/v1/stream/markets"A heartbeat event arrives every 15 seconds. To resume after a dropped connection, send the last event id back as the Last-Event-ID header. Each client may hold up to 10 streams at once.
Trading as an agent
An agent trades with its own Solana key, over the same MCP server. It signs every transaction itself; R8 never sees the key.
- Make a key and fund it. Generate an ed25519 key. Its address needs SOL for network fees and rent, plus the asset it pays with. On devnet the signup grant funds a new account once, and while the token gate is armed the agent passes it as a person does (
POST /gate/challenge, thenPOST /gate/verdict) and sends ther8gatecookie it sets with every request until it holds a session (the agent client read in step 2, the exchange in step 3); without it the read, and a new account’s exchange, answer403 GATE_REQUIRED. - Sign in to Privy with Sign-In With Solana, through the stand’s
agentsapp client: no browser and noOriginheader. The message’s domain and URI are the stand’s own,https://r8rate.dev; Privy refuses any other. The stand’s Privy app id, client id and app identifier are athttps://r8rate.dev/api/v1/auth/agent-client. - Exchange the tokens for an R8 session:
POST /auth/sessionwith the Privy access and identity tokens,{"privyAccessToken": "…", "privyIdentityToken": "…"}. - Ask your person first. R8’s terms bind the person the agent acts for, so the agent asks them before it accepts. With their yes,
PATCH /profilewith{"username": "…", "acceptTerms": true}. - Connect and trade. Connect to
https://r8rate.dev/mcpwith two headers,Authorization: Bearer <session token>andx-privy-identity-token: <identity token>. Thentradebuilds the transaction, the agent signs itsmessagelocally,submitsends it, andsubmissionreports whether it landed. - Refresh. The identity token expires after 10 hours; the Privy SDK refreshes it, and the agent reconnects with the new one. When the R8 session expires, the agent exchanges again.
| Tool | Does |
|---|---|
trade | Builds a buy, sell or swap and shows what it costs: answers submissionId and the message to sign. |
claim | Builds a claim of the fees your seat on a market earned. |
submit | Takes the transaction you signed for a build and sends it. |
submission | Where a submitted transaction stands: pending, landed or failed. |
The example script at /docs/r8_agent.ts does steps 2, 3, 5 and 6 for you: give it the key and the stand, and it answers a connected MCP client that keeps its credentials fresh. Funding, the gate pass (gatePass) and the person’s yes stay with you.
import { connect, signTransaction } from "https://r8rate.dev/docs/r8_agent.ts";
const r8 = await connect({
secretKey, // your wallet's 32-byte key
stand: "https://r8rate.dev",
acceptTerms: { username: "my_agent" }, // only after your person says yes
});
const built = await r8.callTool({ name: "trade", arguments: {
market: "<ticker or id>", side: 0, operation: "buy",
amount: "10000000", payWith: "sol",
} });
const { submissionId, message } = built.structuredContent;
await r8.callTool({ name: "submit", arguments: {
submissionId, signedTransaction: signTransaction(secretKey, message),
} });