# Streaming

> The public WebSocket (candles, trades, and the board-wide feed for Arc and Robinhood Chain — prices, 24h counters, first-time tokens) and the server-sent events feed, with the full protocol.

Source: https://getchord.xyz/docs/concepts/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`

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

```json
{ "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

```json
{ "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" }
```

- `candle` frames repeat the open candle as it updates; `closed: true` marks the last frame for a bucket. Seed the chart from `/v1/tokens/chart` first, then apply frames by `t`.
- `trades` items have the same shape as `/v1/tokens/trades`.
- `prices` sends a snapshot of recent prints on subscribe (the last 15 minutes), then one frame every ~400 ms carrying only the tokens that traded. Apply by `address`; later `block` wins.
- 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 `volume24h` and `trades24h` appear there. `at` is the server time of the frame in milliseconds — REST boards carry `updatedAt` on 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 and `totalSupply` in whole tokens (market cap = `totalSupply × priceUsd`). A snapshot of up to 100 recent ones follows the `prices` snapshot when there are any; a `prices` snapshot means start the list over. Unscreened: for the screened list use `GET /v1/arc/graduated`.
- Error codes: `bad_op` · `bad_token` · `bad_tf` · `bad_chain` · `channel_cap`.

### Unsubscribe

```json
{ "op": "unsub", "channel": "candles", "token": "0x171a…", "tf": "1m" }
{ "op": "unsub", "channel": "prices", "chain": 5042 }
```

### Browser example

```ts
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`

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 |

```ts
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

- 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](/docs/concepts/rate-limits/).
