Streaming
Two push channels replace polling. Neither needs a key.
| Channel | URL | Carries | Use it for |
|---|---|---|---|
| WebSocket | wss://api.getchord.xyz/v1/ws/live |
live candles per token and timeframe, trade tapes per token, and the board-wide feed per chain (every print, 24h volume and trade counts, tokens trading for the first time) | token pages, charts, boards and tickers on Arc and Robinhood Chain |
| Server-sent events | https://api.getchord.xyz/v1/stream/market |
Robinhood Chain prices every ~8 s (rh-ticker) |
a fallback where WebSockets are blocked |
WebSocket: /v1/ws/live
Section titled “WebSocket: /v1/ws/live”Open a socket, send JSON frames to subscribe, receive JSON frames. Up to 8 channels per connection, 500 connections in total. The server pings every 25 seconds; reply is not required, but a proxy that idles at 100 s will not cut you.
Subscribe
Section titled “Subscribe”{ "op": "sub", "channel": "candles", "token": "0x171a4217b86a807a64eb94757db6849fb4bdbaa0", "tf": "1m" }{ "op": "sub", "channel": "trades", "token": "0x171a4217b86a807a64eb94757db6849fb4bdbaa0" }{ "op": "sub", "channel": "prices", "chain": 5042 }{ "op": "sub", "channel": "prices", "chain": 4663 }tf is one of 1m · 1h · 4h · 1d · 1w (candles only). prices is keyed by chain, not token: 5042 (Arc), 4663 (Robinhood Chain) or 143 (Monad, prices only). A re-subscribe of a held channel re-acknowledges and re-sends the snapshot without counting twice.
Frames from the server
Section titled “Frames from the server”{ "op": "subbed", "channel": "candles", "token": "0x171a…", "tf": "1m" }{ "op": "candle", "token": "0x171a…", "tf": "1m", "candle": { "t": 1789891800, "o": 80410.1, "h": 80425.0, "l": 80398.7, "c": 80418.3, "v": 1412.5 }, "closed": false }{ "op": "trades", "token": "0x171a…", "items": [ { "tx": "0x…", "logIndex": 41, "dex": "uniswap_v3", "side": "buy", "tokenAmt": 0.0058, "usd": 472.37, "priceUsd": 80418.25, "ts": 1789891817, "wallet": "0x0e17…" } ] }{ "op": "prices", "chain": 5042, "snapshot": true, "at": 1789891400120, "items": [ { "address": "0xece5…", "priceUsd": 0.01923, "ts": 1789891399, "block": 21808201, "side": "buy", "usd": 212.4, "volume24h": 48210.5, "buyVolume24h": 26011.2, "sellVolume24h": 22199.3, "trades24h": 1180, "buys24h": 640, "sells24h": 540 } ] }{ "op": "new", "chain": 5042, "items": [ { "address": "0x9f2c…", "symbol": "TKN", "name": "Token", "decimals": 18, "logoURI": "https://api.getchord.xyz/v1/logo/5042/0x9f2c…", "totalSupply": 1000000000, "firstTs": 1789891402, "priceUsd": 0.0000412 } ] }{ "op": "ping", "t": 1789891855000 }{ "op": "error", "code": "bad_tf", "message": "tf must be one of 1m|1h|4h|1d|1w" }candleframes repeat the open candle as it updates;closed: truemarks the last frame for a bucket. Seed the chart from/v1/tokens/chartfirst, then apply frames byt.tradesitems have the same shape as/v1/tokens/trades.pricessends a snapshot of recent prints on subscribe (the last 15 minutes), then one frame every ~400 ms carrying only the tokens that traded. Apply byaddress; laterblockwins.- On Arc and Robinhood Chain each item also carries the token’s 24h counters as the board should show them now: the hourly rollup plus every trade since. Robinhood Chain has no buy/sell split, so only
volume24handtrades24happear there.atis the server time of the frame in milliseconds — REST boards carryupdatedAton the same clock, and the later of the two is the current number (the API adds the same not-yet-rolled trades to both, so switching between them never double counts). new(Arc and Robinhood Chain) announces a token trading for the first time — neither the 24h board nor the feed had seen it — with its on-chain name andtotalSupplyin whole tokens (market cap =totalSupply × priceUsd). A snapshot of up to 100 recent ones follows thepricessnapshot when there are any; apricessnapshot means start the list over. Unscreened: for the screened list useGET /v1/arc/graduated.- Error codes:
bad_op·bad_token·bad_tf·bad_chain·channel_cap.
Unsubscribe
Section titled “Unsubscribe”{ "op": "unsub", "channel": "candles", "token": "0x171a…", "tf": "1m" }{ "op": "unsub", "channel": "prices", "chain": 5042 }Browser example
Section titled “Browser example”const ws = new WebSocket("wss://api.getchord.xyz/v1/ws/live");ws.onopen = () => { ws.send(JSON.stringify({ op: "sub", channel: "prices", chain: 5042 })); ws.send(JSON.stringify({ op: "sub", channel: "candles", token, tf: "1m" }));};ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.op === "prices") for (const it of m.items) prices.set(it.address, it); // price + 24h counters if (m.op === "new") for (const t of m.items) newest.unshift(t); if (m.op === "candle") chart.update(m.candle, m.closed);};ws.onclose = () => setTimeout(reconnect, 1000 + Math.random() * 2000);Reconnect with jittered backoff and re-subscribe; the server keeps no state for a closed socket.
Server-sent events: /v1/stream/market
Section titled “Server-sent events: /v1/stream/market”An EventSource feed with automatic reconnect built into the browser. Exactly these events:
event: |
Cadence | Data |
|---|---|---|
rh-ticker |
about every 10 s, only when a price moved | { "items": [ { "address", "priceUsd", "change24h" } ] } for chain 4663 |
ticker · whales · defi |
15–60 s | Monad boards — ignore on Chord |
ping |
5 s | a timestamp |
const es = new EventSource("https://api.getchord.xyz/v1/stream/market");es.addEventListener("rh-ticker", (e) => { for (const it of JSON.parse(e.data).items) prices.set(it.address, it.priceUsd);});Capacity is 500 concurrent clients; 503 beyond that. A closed feed reconnects on its own.
Which to use
Section titled “Which to use”- The whole Arc or Robinhood Chain board — prices, volume, trade counts, new tokens: WebSocket
prices. - Where WebSockets are blocked: SSE
rh-ticker(Robinhood Chain prices only). - One token’s chart or tape, either chain: WebSocket
candles/trades. - Anything else: poll the REST endpoint at the cadence in Rate limits.