API
Read markets, accounts, and the chain, stream live data, and submit signed transactions.
Basics
- Base URL:
https://api.jeets.trade - Amounts are micro-USDG and prices are micro-USD, as integers: 1 USDG is
1000000. - Accounts are Robinhood Chain (EVM) addresses, written lowercase as
0xand 40 hex digits; the API accepts checksummed ones too. Hashes, signatures, and session keys are 0x-prefixed hex.
REST
| Request | Returns |
|---|---|
GET /status | chainId, entries (transactions so far), prices, pool, mempool size |
GET /markets | Prices, open interest, pool, payout queue, fees (withdrawalFee), chainId, minNotional (smallest order value), maxSideGap; per market longCap/shortCap and longRoom/shortRoom (new notional that fits on each side now; null is unlimited). Order clients need minNotional and the room fields. |
GET /account/:address | Live balance, nextNonce, session key and agentExpiresMs, positions with PnL, queued winnings |
GET /positions | Every open position on the exchange, largest first, valued at the live price |
GET /prices/history?market=BTC&windowMs=3600000&points=600&endMs=… | Oracle price history for a stretch ending at endMs (default now, at most a week back), led by the last price before it; firstMs is the first recorded price |
GET /markets/volume?windowMs=86400000 | Opened volume and trade count per market over a window (default 24h) |
GET /stats?windowMs=86400000&bucketMs=1800000 | Exchange totals, activity per time bucket, volume per market, top traders |
GET /trades?limit=40 | Recent opens, closes, and liquidations |
GET /txs?before=&limit=25 | Transactions, newest first; before is a seq. Each has its seq, hash, kind, signer, tx, receipt, the prices and timestampMs it ran at, and where it was written on Robinhood Chain (chainTxHash, chainBlock, chainLogIndex; null until it is) |
GET /blocks?before=&limit=25 | Robinhood Chain blocks holding Jeets transactions, newest first, one row per ledger write (txHash, block, fromSeq, toSeq, kinds); before is a Robinhood Chain block number |
GET /blocks/:number, /blocks/latest | A Robinhood Chain block: its ledger writes (records) and the Jeets transactions in them (txs) |
GET /tx/:hash | A transaction, as in /txs; withdrawals add payouts, the vault payment on Robinhood Chain (txHash, status) |
GET /address/:address | Committed account view with trading stats and withdrawals (each with the vault payment’s txHash once sent) |
GET /address/:address/txs?before=&limit= | An account’s transactions, newest first; before is a seq |
GET /evm | Where Jeets lives on Robinhood Chain: chainId, rpcUrl, explorerUrl, the vault and USDG token addresses (null on a node without deposits), and the ledger contract |
POST /tx | Submit a signed transaction; returns its hash |
Paged lists take limit (default 25, at most 100) and before, an exclusive cursor: pass the last item’s seq (or, for /blocks, its block) to get the next page.
WebSocket
For live prices, markets, accounts, open positions, transactions, Robinhood Chain blocks, and trades, connect to /ws. See WebSocket.
Deposits
There is no deposit endpoint. Approve USDG to the vault, then call deposit(amount) on the vault from the account’s own wallet (addresses from GET /evm). The node credits the amount once it sees the vault’s Deposited log. USDG sent to the vault with a plain transfer isn’t credited.
Signing transactions
A transaction names the chain (chainId from GET /status or /markets: the Jeets chain, not the EVM chain id), the account, the account’s next nonce (nextNonce from GET /account/:address, which counts transactions already queued), and an action: open, close, withdraw, or setAgent, and for the admin account only, setLimits, setSideGap, and fundPool ({ "type": "fundPool", "amount": 5000000000 }, signed text action: fund pool / amount: 5000.000000 USDG). Build the text below and sign it, then post it.
- The text is UTF-8, with
\nline breaks and no trailing newline. Amounts have 6 decimals. The chain id is 16 hex digits without0x. The account is lowercase. - Wallet signatures are
personal_sign(EIP-191) over the text: the 65-byte secp256k1 signature as0xhex. The node recovers the signer from it, so it must be the account’s own key. - Session keys are ed25519 keys. Their 64-byte signature over the raw text goes in
signatureas0xhex, with the key itself (32 bytes,0xhex) inagent.
jeets.trade
chain: <chainId>
account: 0x<40 lowercase hex digits>
nonce: <n>
action: open
market: BTC
side: long
margin: 10.000000 USDG
leverage: 1000xThe first four lines are the same for every action. The lines after them:
action: close
position: <position id>
action: withdraw
amount: 5.000000 USDG
action: set session key
session key: 0x<64 hex digits>
expires: 2026-10-13 04:20:00 UTC
action: set session key
session key: noneThe expiry is UTC, with seconds; the last form revokes the session key.
POST /tx
{
"tx": {
"chain": "<chainId>",
"signer": "0x…",
"nonce": 3,
"action": { "type": "open", "market": "BTC", "side": "long", "margin": 10000000, "leverage": 1000 }
},
"signature": "0x…",
"agent": "<session key, 0x + 64 hex, only when it signed>"
}- Other actions:
{ "type": "close", "positionId": 12 },{ "type": "withdraw", "amount": 5000000 },{ "type": "setAgent", "agent": "0x…", "expiresAtMs": 1791866400000 }(a session key for at most 24 hours), and{ "type": "setAgent", "agent": null, "expiresAtMs": 0 }to revoke it. - The response is
{ "hash": "0x…" }. The transaction’s result (or error) is atGET /tx/:hashonce it has run, usually within half a second, and its Robinhood Chain block a second or two later. - A request refused up front comes back as HTTP 400 with
{ "error": "…" }, for exampleinvalid wallet signature,stale nonce: next is 12, or a session key that’s expired or not authorized. - Session keys can sign only
openandclose. - A transaction signed for another chain id is refused without using up the nonce, so signatures can’t be replayed on a different Jeets chain.
JavaScript (viem)
import { privateKeyToAccount } from "viem/accounts";
const API = "https://api.jeets.trade";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const signer = account.address.toLowerCase();
const { chainId } = await (await fetch(`${API}/markets`)).json();
const { nextNonce } = await (await fetch(`${API}/account/${signer}`)).json();
const text = [
"jeets.trade",
`chain: ${chainId}`,
`account: ${signer}`,
`nonce: ${nextNonce}`,
"action: open",
"market: BTC",
"side: long",
"margin: 10.000000 USDG",
"leverage: 1000x",
].join("\n");
const signature = await account.signMessage({ message: text }); // personal_sign
const response = await fetch(`${API}/tx`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
tx: {
chain: chainId,
signer,
nonce: nextNonce,
action: { type: "open", market: "BTC", side: "long", margin: 10_000_000, leverage: 1000 },
},
signature,
}),
});
console.log(await response.json()); // { hash: "0x…" }Python (eth_account)
import os, requests
from eth_account import Account
from eth_account.messages import encode_defunct
API = "https://api.jeets.trade"
account = Account.from_key(os.environ["PRIVATE_KEY"])
signer = account.address.lower()
chain = requests.get(f"{API}/markets").json()["chainId"]
nonce = requests.get(f"{API}/account/{signer}").json()["nextNonce"]
text = "\n".join([
"jeets.trade",
f"chain: {chain}",
f"account: {signer}",
f"nonce: {nonce}",
"action: withdraw",
"amount: 5.000000 USDG",
])
signed = account.sign_message(encode_defunct(text=text)) # personal_sign
tx = {"chain": chain, "signer": signer, "nonce": nonce,
"action": {"type": "withdraw", "amount": 5_000_000}}
print(requests.post(f"{API}/tx", json={"tx": tx, "signature": "0x" + bytes(signed.signature).hex()}).json())