Kline (Candlestick)
Channel Name
spot:kline:{symbol}:{interval}
Authentication: not required (public channel).
Supported intervals: 1m, 5m, 15m, 1h, 4h, 1d. See Enums — K-line Intervals.
Description
Provides the latest candle as a snapshot on subscribe, then pushes a running OHLCV update after every fill that falls within the current candle's time window. There is no push at the window boundary — after a window ends, the next fill produces a push carrying the new candle's open_time. Subscribing to multiple intervals for the same symbol produces one push per interval per fill.
Subscribe
Send:
{ "type": "subscribe", "channel": "spot:kline:DFUSDT:1m" }
Response:
{ "type": "subscribed", "channel": "spot:kline:DFUSDT:1m" }
A spot_kline_snapshot is sent immediately after the ack.
Push Format
spot_kline_snapshot
Sent once on subscribe. Contains the latest completed or in-progress candle from the database.
{
"type": "spot_kline_snapshot",
"channel": "spot:kline:DFUSDT:1m",
"data": {
"symbol": "DFUSDT",
"interval": "1m",
"open_time": 1778399940,
"close_time": 1778399999,
"open": "0.4800",
"high": "0.5100",
"low": "0.4700",
"close": "0.5000",
"volume": "10000",
"quote_volume": "4900",
"trade_count": 234,
"is_closed": false
}
}
spot_kline_update
Pushed after every fill that lands inside the current candle's window. The data format is identical to spot_kline_snapshot.
{
"type": "spot_kline_update",
"channel": "spot:kline:DFUSDT:1m",
"data": {
"symbol": "DFUSDT",
"interval": "1m",
"open_time": 1778399940,
"close_time": 1778399999,
"open": "0.4800",
"high": "0.5100",
"low": "0.4700",
"close": "0.5010",
"volume": "10030",
"quote_volume": "4915",
"trade_count": 235,
"is_closed": false
}
}
is_closed is computed at the instant the frame is generated: it is true when now > close_time at push time. In practice a live per-fill update almost always carries is_closed: false (the fill just happened inside the window); is_closed: true appears mainly on the subscribe-time snapshot when the latest stored candle's window has already elapsed, or when a fill is persisted right at the window edge. Do not wait for an is_closed: true frame to close out a candle — treat a push with a newer open_time as the signal that the previous candle is final.
| Field | Type | Description |
|---|---|---|
symbol | string | Market identifier. |
interval | string | Candle interval, e.g. 1m. |
open_time | integer | Candle open — unix seconds. |
close_time | integer | Candle close — unix seconds. Equal to open_time + interval_seconds − 1. |
open | string (decimal) | Opening price of the candle. |
high | string (decimal) | Highest fill price so far in this candle. |
low | string (decimal) | Lowest fill price so far in this candle. |
close | string (decimal) | Most recent fill price in this candle. |
volume | string (decimal) | Total base volume in this candle. |
quote_volume | string (decimal) | Total quote volume in this candle. |
trade_count | integer | Number of fills in this candle. |
is_closed | boolean | Whether now > close_time at the moment the frame was generated. Not a boundary event — see above. |
All timestamps are unix seconds.
Update Cadence
- One
spot_kline_updateper fill that lands in the interval's time window. - Subscribing to
1m + 5m + 1hfor the same symbol produces three pushes per fill (one per interval, each updating the respective candle). - No fixed polling timer — updates are purely event-driven.
Because each push replaces the running candle completely, a missed push is overwritten by the next one. No resync is needed. See Snapshot ↔ Live Sequencing.
Code Example
const ws = new WebSocket('wss://api.prex.world/ws');
// Local candle state keyed by interval
const candles = {};
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'subscribe', channel: 'spot:kline:DFUSDT:1m' }));
ws.send(JSON.stringify({ type: 'subscribe', channel: 'spot:kline:DFUSDT:1h' }));
setInterval(() => ws.send(JSON.stringify({ type: 'ping' })), 30000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'spot_kline_snapshot' || msg.type === 'spot_kline_update') {
const d = msg.data;
const prev = candles[d.interval];
if (prev && d.open_time > prev.open_time) {
// A push with a newer open_time means the previous candle is final
console.log(`[${d.interval}] Candle closed: O=${prev.open} H=${prev.high} L=${prev.low} C=${prev.close} V=${prev.volume}`);
// Advance your chart to the next candle
} else {
console.log(`[${d.interval}] Live candle: C=${d.close} V=${d.volume}`);
// Repaint the live (rightmost) bar on your chart
}
candles[d.interval] = d;
}
};