Documentation Index
Fetch the complete documentation index at: https://docs.ekiden.fi/llms.txt
Use this file to discover all available pages before exploring further.
This page explains how to connect to Ekiden WebSocket streams, authenticate for private data, and manage subscriptions. For full schema details, see the AsyncAPI spec.
- Public stream: AsyncAPI → channel
public
- Private stream: AsyncAPI → channel
private
Environments & Endpoints
- Production
- REST:
https://api.ekiden.fi/api/v1
- WS Public:
wss://api.ekiden.fi/ws/public
- WS Private:
wss://api.ekiden.fi/ws/private
- Staging
- REST:
https://api.staging.ekiden.fi/api/v1
- WS Public:
wss://api.staging.ekiden.fi/ws/public
- WS Private:
wss://api.staging.ekiden.fi/ws/private
Choose your environment before connecting. Public feeds require no auth; private feeds require a bearer token from the matching REST environment.
No authentication is required for the public stream. Private stream requires bearer token auth.
Quick Start (JavaScript)
const ws = new WebSocket("wss://api.ekiden.fi/ws/public"); // use staging URL for testing
ws.onopen = () => {
// Subscribe to orderbook and trades for a market
ws.send(JSON.stringify({ op: "subscribe", args: [
"orderbook.1.BTC-USDC",
"trade.BTC-USDC"
], req_id: "100001" }));
};
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
// Handle acks
if (msg.op === "subscribed") console.log("Subscribed:", msg.args);
if (msg.op === "unsubscribed") console.log("Unsubscribed:", msg.args);
if (msg.op === "error") console.warn("Error:", msg.message);
// Ping/pong timestamps are uint64 Unix nanoseconds in the schema; omit `ts` if your language can’t safely represent u64.
if (msg.op === "pong") console.log("Pong:", msg);
// Handle events (market or user, depending on stream)
if (msg.op === "event") {
console.log("Topic:", msg.topic, "Data:", msg.data);
}
};
// Heartbeat every ~20s
setInterval(() => {
// `ts` is optional; when provided it should be Unix nanoseconds (u64).
// Many clients simply omit it and use pong as a liveness check.
ws.send(JSON.stringify({ op: "ping", req_id: "hb" }));
}, 20_000);
Private Stream Authentication
- Obtain a bearer token via the REST Authorize endpoint (use the same environment as your WS connection).
- Connect to
wss://api.ekiden.fi/ws/private (or staging equivalent).
- Send
AuthRequest once connected.
{ "op": "auth", "bearer": "<JWT>", "req_id": "200001" }
Server response examples:
{ "op": "auth", "success": true, "user_id": "0x88a70ff...", "req_id": "200001" }
{ "op": "auth", "success": false, "message": "INVALID_TOKEN", "req_id": "200001" }
After a successful auth you can subscribe to private topics: order, position, execution, account_balance.
Subscribe / Unsubscribe
Request:
{ "op": "subscribe", "args": ["orderbook.1.BTC-USDC"], "req_id": "100002" }
Ack:
{ "op": "subscribed", "args": ["orderbook.1.BTC-USDC"], "req_id": "100002" }
Unsubscribe:
{ "op": "unsubscribe", "args": ["orderbook.1.BTC-USDC"], "req_id": "100003" }
Ack:
{ "op": "unsubscribed", "args": ["orderbook.1.BTC-USDC"], "req_id": "100003" }
Heartbeat (Ping/Pong)
There are two kinds of heartbeats:
-
Application-level ping/pong
- Client sends
op=ping with an optional ts to measure RTT.
- Server replies with
op=pong echoing client_ts and adding server_ts.
- Timestamps containing Unix nanoseconds (
u64).
-
WebSocket-level ping/pong (control frames)
- The server also sends standard WebSocket ping frames at intervals.
- Your WS client library should automatically reply with WS-level pong frames.
- If the server doesn’t receive WS-level pongs within the timeout window, it will close the connection.
Send an application-level ping periodically to measure RTT (optional):
{ "op": "ping", "ts": 1731541800000000000, "req_id": "100004" } // ts in Unix nanoseconds
Pong response:
{ "op": "pong", "server_ts": 1731541800123000000, "client_ts": 1731541800000000000, "req_id": "100004" } // both in Unix nanoseconds
Keep the connection healthy:
- Your client should automatically respond to WebSocket-level pings with pongs.
- Optionally send application-level pings every ~20 seconds to monitor RTT.
- Idle sockets without responding to WS-level pings may be closed by the server.
Rate Limits & Best Practices
- Avoid repeatedly opening/closing sockets. Reuse a connection per environment.
- Batch subscriptions in a single request where possible (multiple topics in
args).
- Backoff and retry on
error responses; watch for policy messages like rate limits.
- Do not assume ordering across different topics; sequence/order applies within a topic only.
For message shapes and fields, refer to AsyncAPI messages: SubscribeRequest, UnsubscribeRequest, Pong, Error, and Event.
Topics
Public stream topics (subscribe on /ws/public):
orderbook.<depth>.<symbol>
trade.<symbol>
ticker.<symbol>
kline.<interval>.<symbol>
all_liquidations.<symbol>
Private stream topics (after auth on /ws/private):
order
position
execution
account_balance
Regex patterns enforced server-side:
^(orderbook\.[^.]+\.[^.]+|trade\.[^.]+|ticker\.[^.]+|kline\.[^.]+\.[^.]+|all_liquidations\.[^.]+)$ # public
^(order|position|execution|account_balance)$ # private
Unified Event Envelope
All streamed payloads share a common envelope:
{
"op": "event",
"topic": "ticker.BTC-USDC",
"server_ts_ms": 1731541800000,
"data": { /* object or array variant */ }
}
Orderbook events additionally include a type field (snapshot or delta).
data variants:
OrderBookSnapshot (orderbook events also include type: "snapshot" | "delta")
PublicTrade[]
TickerSnapshot
KlineSnapshot
Order[], Execution[], Position[], AccountBalance (private)
Note: server_ts_ms in the event envelope is always milliseconds.