Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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");

The server sends a heartbeat ping every 5 seconds. Send { "type": "ping" } to get a pong back with current block height and latency info.

Subscribing

Send a subscribe message with the channels you want:

{
  "type": "subscribe",
  "channels": [
    { "channel": "orderbook", "market_id": 0 },
    { "channel": "trades", "market_id": 0 }
  ]
}

Response:

{
  "type": "subscribed",
  "channels": [
    { "channel": "orderbook", "market_id": 0 },
    { "channel": "trades", "market_id": 0 }
  ]
}

To unsubscribe:

{
  "type": "unsubscribe",
  "channels": [
    { "channel": "orderbook", "market_id": 0 }
  ]
}

Channels

orderbook

Full orderbook snapshot on subscribe, then incremental updates on every change.

Subscribe: { "channel": "orderbook", "market_id": 0 }

Snapshot (sent immediately on subscribe):

{
  "type": "orderbookSnapshot",
  "market_id": 0,
  "bids": [["58100000000", "500000"], ...],
  "asks": [["58200000000", "300000"], ...]
}

Updates (on each order/fill/cancel):

{
  "type": "orderbookUpdate",
  "market_id": 0,
  "bids": [["58100000000", "450000"]],
  "asks": []
}

A size of "0" means that price level was removed.

trades

Recent trades for a market.

Subscribe: { "channel": "trades", "market_id": 0 }

{
  "type": "trade",
  "market_id": 0,
  "price": "58150000000",
  "size": "100000",
  "side": "Long",
  "timestamp": 1712345678000
}

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",
  "side": "Long",
  "fee": "203000",
  "order_id": 42,
  "timestamp": 1712345678000
}

orders

Your order updates (placed, canceled, modified).

Subscribe: { "channel": "orders", "account_id": 12 }

positions

Your position changes.

Subscribe: { "channel": "positions", "account_id": 12 }

funding

Funding rate updates for a market. Fires hourly when funding settles.

Subscribe: { "channel": "funding", "market_id": 0 }

{
  "type": "funding",
  "market_id": 0,
  "funding_rate": "-15800",
  "timestamp": 1712345678000
}

oracle / oracleUpdates

Oracle price updates for a market.

Subscribe: { "channel": "oracleUpdates", "market_id": 0 }

{
  "type": "oracleUpdate",
  "market_id": 0,
  "price": "58035000000",
  "previous_price": "57890000000",
  "valid_until": 1712433900
}

explorer

Block production info. Fires on every finalized block.

Subscribe: { "channel": "explorer" }

{
  "type": "blockFinalized",
  "height": 73001,
  "hash": "abc123...",
  "proposer": "e1e7af3f...",
  "timestamp": 1712345678943,
  "tx_count": 3
}

Ping/pong

Send a ping to measure latency and get current block info:

{ "type": "ping" }

Response:

{
  "type": "pong",
  "blockHeight": 73001,
  "blockTimeMs": 198,
  "timestamp": 1712345678943
}

Example: full orderbook bot

const WebSocket = require("ws");
 
const ws = new WebSocket("wss://v4-api.dev.parcllabs.com/v1/ws");
 
let bids = new Map(); // price -> size
let asks = new Map();
 
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") {
    bids = new Map(msg.bids.map(([p, s]) => [p, s]));
    asks = new Map(msg.asks.map(([p, s]) => [p, s]));
    console.log(`Snapshot: ${bids.size} bids, ${asks.size} asks`);
  }
 
  if (msg.type === "orderbookUpdate") {
    for (const [price, size] of msg.bids) {
      if (size === "0") bids.delete(price);
      else bids.set(price, size);
    }
    for (const [price, size] of msg.asks) {
      if (size === "0") asks.delete(price);
      else asks.set(price, size);
    }
 
    const bestBid = [...bids.keys()].sort().pop();
    const bestAsk = [...asks.keys()].sort()[0];
    console.log(`Best bid: ${bestBid}, Best ask: ${bestAsk}`);
  }
});