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 carries X-RateLimit-Limit and X-RateLimit-Remaining; past the limit you get 429.
  • 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: 0 is the first side, 1 the second, in the order of the market’s sideLabels.

Endpoints

Paths are relative to /api/v1.

EndpointParametersReturns
GET /marketstag, limit (≤200), offsetThe board, newest first, a page at a time; nextOffset is null on the last page.
GET /markets/searchq, limit (≤50), offsetMarkets whose question, ticker, tags or handle match q.
GET /markets/{marketId}—One market: prices, consensus, supply, TVL, 24h volume.
GET /markets/{marketId}/chartresolution (1m, 1h, 1d), from, toPrice and TVL history; from and to are in epoch seconds.
GET /markets/{marketId}/holderslimit (≤200), offsetWho holds each side.
GET /markets/{marketId}/stats—Volume and trade counts.
GET /activitymarketId, 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 /leaderboardrangeHours, 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/quoteJSON bodyWhat 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/mcp

Other 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):

ToolReturns
search_marketsMarkets by words, ticker or tag, or the whole board: ticker, question, consensus, TVL, 24h volume.
get_marketOne market by ticker, id or question: prices, consensus, TVL, supply, totals and recent trades.
get_market_historyA market’s closing prices, volume, TVL and consensus per 1m, 1h or 1d bucket.
get_leaderboardTraders ranked by profit.
get_traderOne 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.

StreamWhat arrives
GET /stream/marketsThe 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.

  1. 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, then POST /gate/verdict) and sends the r8gate cookie 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, answer 403 GATE_REQUIRED.
  2. Sign in to Privy with Sign-In With Solana, through the stand’s agents app client: no browser and no Origin header. 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 at https://r8rate.dev/api/v1/auth/agent-client.
  3. Exchange the tokens for an R8 session: POST /auth/session with the Privy access and identity tokens, {"privyAccessToken": "…", "privyIdentityToken": "…"}.
  4. 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 /profile with {"username": "…", "acceptTerms": true}.
  5. Connect and trade. Connect to https://r8rate.dev/mcp with two headers, Authorization: Bearer <session token> and x-privy-identity-token: <identity token>. Then trade builds the transaction, the agent signs its message locally, submit sends it, and submission reports whether it landed.
  6. 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.
ToolDoes
tradeBuilds a buy, sell or swap and shows what it costs: answers submissionId and the message to sign.
claimBuilds a claim of the fees your seat on a market earned.
submitTakes the transaction you signed for a build and sends it.
submissionWhere 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),
} });