WebSocket

Stream live prices, market state, accounts, transactions, Robinhood Chain blocks, and trades. It’s the same feed the Jeets website uses, open to everyone, with no API key.

Connecting

wss://api.jeets.trade/ws
  • Messages are JSON text frames, in both directions.
  • Amounts are micro-USDG and prices are micro-USD, as integers: 83606360000 is $83,606.36.
  • Subscribe to as many channels as you need on one connection.

Requests

{ "method": "subscribe",   "subscription": { "type": "prices" } }
{ "method": "unsubscribe", "subscription": { "type": "prices" } }
{ "method": "subscribe",   "subscription": { "type": "account", "address": "0x<40 hex>" } }
{ "method": "ping" }

Every server message has the form { "channel": ..., "data": ... }. A subscribe or unsubscribe is confirmed with a subscriptionResponse echoing the request; a bad request gets an error message, and the connection stays open.

Channels

TypeSendsWhen
pricesThe live oracle price per marketWhenever a price changes, at least once a second, at most 20 a second
marketsPrices, open interest, pool, payout queue, feesTwice a second, and after every transaction
accountOne account’s balance, nonce, session key, positions, and queued winningsTwice a second, and after every transaction
positionsEvery open position on the exchange, largest first, each with its ownerTwice a second, and after every transaction
txsNew transactions, as GET /txs rows (not yet written to Robinhood Chain)As soon as they run
blocksEach write of Jeets transactions to Robinhood Chain: its block, transaction, and the transactions it holdsWhen it lands on Robinhood Chain
tradesOpens, closes, and liquidations of one transactionEach transaction that has any

prices, markets, account, and positions send the current state as soon as you subscribe, so there’s nothing to fetch first.

prices

{ "channel": "prices",
  "data": { "prices": { "NVDA": 228960000, "BTC": 83606360000 }, "timestampMs": 1791746814302 } }

A market missing from prices has no fresh oracle price right now. See Oracle.

markets

An excerpt; some fields are left out (the full payload is the same as GET /markets).

{ "channel": "markets",
  "data": {
    "entries": 8879, "oracleFresh": true, "chainId": "3f9a0c51d27e8b46",
    "minNotional": 10000000000, "maxSideGap": 10000000000000,
    "withdrawalFee": 1000000,
    "pool": 211167956, "queuedProfit": 0, "queueLength": 0,
    "feesCollected": 3626227, "impactRetained": 481249157,
    "markets": [
      { "market": "BTC", "price": 83606360000, "maxLeverage": 1000,
        "openInterest": 3000000000, "longOpenInterest": 3000000000, "shortOpenInterest": 0,
        "longCap": null, "shortCap": null,
        "longRoom": 9997000000000, "shortRoom": 10003000000000 },
      { "market": "NVDA", ... }
    ] } }

longRoom and shortRoom are how much new notional fits on each side right now; null is unlimited.

account

An excerpt; the full payload is the same as GET /account/:address.

{ "channel": "account",
  "data": {
    "address": "0x8f3a…c21d", "balance": 1392517066, "nonce": 49, "nextNonce": 49,
    "agent": "0x5c1e…9a07", "agentExpiresMs": 1791833200000,
    "positions": [
      { "id": 41, "market": "BTC", "side": "long", "margin": 10000000, "leverage": 1000,
        "notional": 10000000000, "entryPrice": 83600080000, "markPrice": 83599830000,
        "liquidationPrice": 83516480000, "unrealizedPnl": -29902, "grossPnl": -29902,
        "openedAtMs": 1791746800000 }
    ],
    "pendingClaims": [ { "positionId": 37, "remaining": 2170000, "ahead": 0 } ] } }
  • unrealizedPnl is what closing now would pay, after fees; grossPnl is before them.
  • nextNonce is the nonce to sign next. See API.
  • Up to 10 account subscriptions per connection.

positions

{ "channel": "positions",
  "data": { "entries": 8879, "positions": [
    { "id": 41, "owner": "0x8f3a…c21d", "market": "BTC", "side": "long", "margin": 10000000,
      "leverage": 1000, "notional": 10000000000, "entryPrice": 83600080000,
      "markPrice": 83599830000, "liquidationPrice": 83516480000,
      "unrealizedPnl": -29902, "grossPnl": -29902, "openedAtMs": 1791746800000 }
  ] } }

txs

{ "channel": "txs",
  "data": [ { "seq": 8880, "hash": "0x3d8c…f77d", "kind": "open", "signer": "0x8f3a…c21d",
              "error": null, "tx": { ... }, "receipt": { "error": null, "events": [ ... ] },
              "prices": { "NVDA": 228960000, "BTC": 83606360000 },
              "timestampMs": 1791746815303,
              "chainTxHash": null, "chainBlock": null, "chainLogIndex": null } ] }

seq numbers every transaction in the order it ran, with no gaps. Once a transaction is written to Robinhood Chain, its chain* fields are filled in GET /tx/:hash, and a blocks message covers its seq.

blocks

{ "channel": "blocks",
  "data": { "block": 88370948, "txHash": "0x3e96…80b2", "timestampMs": 1791746816000,
            "fromSeq": 8879, "toSeq": 8880, "kinds": { "open": 1, "deposit": 1 } } }

block is the Robinhood Chain block number and txHash the Robinhood Chain transaction that wrote transactions fromSeq to toSeq. See On Robinhood Chain.

trades

{ "channel": "trades",
  "data": { "seq": 6655, "events": [
    { "type": "positionOpened", "positionId": 39, "owner": "0x41b7…e05a", "market": "BTC",
      "side": "long", "margin": 10000000, "leverage": 50, "entryPrice": 83578225000 }
  ] } }

Events are positionOpened, positionClosed (with exitPrice, pnl after fees, grossPnl, impact, fee, profitPaid, profitQueued), and positionLiquidated (with price and marginLost).

Staying connected

  • The server sends a WebSocket ping every 30 seconds; most clients answer it on their own. Clients that can’t send ping frames, like browsers, can send { "method": "ping" } and get { "channel": "pong", "data": null }.
  • If you reconnect, subscribe again: subscriptions belong to a connection. The snapshots bring you back up to date.
  • A client that reads too slowly skips messages rather than falling behind. Every markets and account message is a full snapshot, so the latest one is always complete; for every transaction without gaps, page through GET /txs by seq.

Examples

JavaScript (browser or Node 22+)

const ws = new WebSocket("wss://api.jeets.trade/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({ method: "subscribe", subscription: { type: "prices" } }));
  ws.send(JSON.stringify({ method: "subscribe", subscription: { type: "trades" } }));
};

ws.onmessage = (message) => {
  const { channel, data } = JSON.parse(message.data);
  if (channel === "prices") console.log("BTC", data.prices.BTC / 1e6);
  if (channel === "trades") for (const event of data.events) console.log(event.type, event.market);
};

Python

# pip install websockets
import asyncio, json, websockets

async def main():
    async with websockets.connect("wss://api.jeets.trade/ws") as ws:
        await ws.send(json.dumps({"method": "subscribe", "subscription": {"type": "prices"}}))
        async for raw in ws:
            message = json.loads(raw)
            if message["channel"] == "prices":
                print({market: price / 1e6 for market, price in message["data"]["prices"].items()})

asyncio.run(main())

Command line

npx wscat -c wss://api.jeets.trade/ws
> {"method":"subscribe","subscription":{"type":"blocks"}}

Wallet

Deposit USDG

Withdraw