Skip to main content

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.

FieldTypeDescription
symbolstringMarket identifier.
intervalstringCandle interval, e.g. 1m.
open_timeintegerCandle open — unix seconds.
close_timeintegerCandle close — unix seconds. Equal to open_time + interval_seconds − 1.
openstring (decimal)Opening price of the candle.
highstring (decimal)Highest fill price so far in this candle.
lowstring (decimal)Lowest fill price so far in this candle.
closestring (decimal)Most recent fill price in this candle.
volumestring (decimal)Total base volume in this candle.
quote_volumestring (decimal)Total quote volume in this candle.
trade_countintegerNumber of fills in this candle.
is_closedbooleanWhether 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_update per fill that lands in the interval's time window.
  • Subscribing to 1m + 5m + 1h for 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;
}
};