WebSocket
Endpoint: wss://v4-api.dev.parcllabs.com/v1/ws
The WebSocket provides real-time data: orderbook updates, fills, price changes, and block production. Connect, subscribe to channels, and receive events.
Connecting
const ws = new WebSocket("wss://v4-api.dev.parcllabs.com/v1/ws");Send { "type": "ping" } to get a pong back with current block height and timing info. Use this to measure latency and confirm the connection is alive.
Subscribing
Send a subscribe message with the channels you want:
{
"type": "subscribe",
"channels": [
{ "channel": "orderbook", "market_id": 0 },
{ "channel": "trades", "market_id": 0 }
]
}The server does not send a confirmation message โ subscriptions are silent. You will begin receiving events on the subscribed channels immediately.
To unsubscribe:
{
"type": "unsubscribe",
"channels": [{ "channel": "orderbook", "market_id": 0 }]
}Channels
orderbook
Full orderbook snapshot on subscribe and on every subsequent change. Each message contains the complete book state โ there are no incremental deltas.
Subscribe: { "channel": "orderbook", "market_id": 0 }
{
"type": "orderbookSnapshot",
"market_id": 0,
"bids": [
{ "price": "58100000000", "size": "500000", "orderCount": 1 },
{ "price": "58050000000", "size": "1200000", "orderCount": 3 }
],
"asks": [
{ "price": "58200000000", "size": "300000", "orderCount": 1 },
{ "price": "58250000000", "size": "800000", "orderCount": 2 }
]
}A new snapshot is sent on every order, fill, or cancellation that changes the book. Replace your local state entirely on each message.
trades
Fires on every trade in a market. Uses the same fill event format as the fills channel.
Subscribe: { "channel": "trades", "market_id": 0 }
{
"type": "fill",
"market_id": 0,
"price": "58150000000",
"size": "100000",
"taker_order_id": 42,
"maker_order_id": 38,
"taker_account_id": 12,
"maker_account_id": 3,
"taker_side": "Long",
"taker_fee": "0",
"maker_fee": "0",
"tx_hash": "9dd57899..."
}fills
Your fills (requires your account ID). Fires when your orders get filled.
Subscribe: { "channel": "fills", "account_id": 12 }
{
"type": "fill",
"market_id": 0,
"price": "58150000000",
"size": "100000",
"taker_order_id": 42,
"maker_order_id": 38,
"taker_account_id": 12,
"maker_account_id": 3,
"taker_side": "Long",
"taker_fee": "203000",
"maker_fee": "-58000",
"tx_hash": "9dd57899..."
}The event is sent to both the taker's and the maker's fills channel. Check whether your account_id matches taker_account_id or maker_account_id to determine your side and fee. Negative fees indicate a maker rebate.
orders
Your order updates (placed and canceled).
Subscribe: { "channel": "orders", "account_id": 12 }
Placed:
{
"type": "orderPlaced",
"order_id": 42,
"account_id": 12,
"market_id": 0,
"side": "Long",
"price": "58000000000",
"size": "100000"
}Canceled:
{
"type": "orderCanceled",
"order_id": 42,
"account_id": 12,
"market_id": 0,
"reason": "UserCanceled"
}liquidations
Fires when your account is liquidated.
Subscribe: { "channel": "liquidations", "account_id": 12 }
{
"type": "liquidation",
"account_id": 12,
"positions_closed": 1,
"liquidation_fee": "250000",
"positions_remaining": 0
}positions
Subscribe: { "channel": "positions", "account_id": 12 }
Position updates are delivered indirectly via fills and liquidations events rather than as a separate position snapshot. To get your current position state after a fill, call GET /v1/accounts/{id}/positions.
funding
Funding rate updates for a market. Fires hourly when funding settles.
Subscribe: { "channel": "funding", "market_id": 0 }
{
"type": "fundingUpdate",
"market_id": 0,
"funding_rate": "-15800",
"last_funding_timestamp": 1712345678000
}oracleUpdates
Oracle price updates for a market.
Subscribe: { "channel": "oracleUpdates", "market_id": 0 }
{
"type": "oraclePriceUpdate",
"market_id": 0,
"price": "58035000000",
"previous_price": "57890000000",
"positions_updated": 14,
"valid_until": 1712433900
}explorer
Block production info. Fires on every finalized block.
Subscribe: { "channel": "explorer" }
{
"type": "blockFinalized",
"height": 73001,
"hash": "abc123...",
"parent_hash": "def456...",
"state_root": "4d419b60...",
"proposer": "e1e7af3f...",
"timestamp": 1712345678943,
"tx_count": 3,
"transactions": [
{
"hash": "0x9dd57899...",
"tx_index": 0,
"tx_type": "PlaceOrder",
"sender": "0x35504e68...",
"status": "success",
"timestamp": 1712345678943
}
]
}Only the first transaction in each block is sent over WebSocket. For the full transaction list, use the block explorer REST API.
Ping/pong
Send a ping to measure latency and get current block info:
{ "type": "ping" }Response:
{
"type": "pong",
"blockHeight": 73001,
"blockTimeMs": 200
}Example: orderbook tracker
const WebSocket = require("ws");
const ws = new WebSocket("wss://v4-api.dev.parcllabs.com/v1/ws");
let bids = [];
let asks = [];
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "subscribe",
channels: [{ channel: "orderbook", market_id: 0 }],
}),
);
});
ws.on("message", (data) => {
const msg = JSON.parse(data);
if (msg.type === "orderbookSnapshot") {
// Full snapshot on every change โ replace local state entirely
bids = msg.bids; // [{ price, size, orderCount }, ...]
asks = msg.asks;
if (bids.length > 0 && asks.length > 0) {
console.log(
`Best bid: ${bids[0].price}, Best ask: ${asks[0].price}, Spread: ${asks[0].price - bids[0].price}`,
);
}
}
});