WebSocket Protocol
One connection, many channels. Open a socket, subscribe to the channels you want, and data flows as it happens on-chain — sub-second from transaction to your handler. This page is the protocol; the Channel Catalog lists every channel you can subscribe to.
Connecting
wss://api.conyr.ai/wsconst ws = new WebSocket("wss://api.conyr.ai/ws?api_key=YOUR_API_KEY");
ws.onopen = () => {
ws.send(JSON.stringify({
op: "subscribe",
channels: ["token:EPjFWd...:trades", "token:EPjFWd...:ohlcv:filtered:1m"],
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.channel) console.log(`[${msg.channel}]`, msg.data);
};Your API key’s tier governs the connection — pass it as the api_key query param (browser-friendly), or as an Authorization: Bearer header from a server client. The free tier has 0 WebSocket connections, so streaming needs a Layer 1+ key.
Client → server
| Message | Purpose |
|---|---|
{"op": "subscribe", "channels": [...]} | Subscribe to one or more channels |
{"op": "unsubscribe", "channels": [...]} | Stop receiving those channels |
{"op": "ping"} | Liveness check (server replies pong) |
Server → client
| Message | Meaning |
|---|---|
{"channel": "...", "data": { ... }} | A data event — the payload shape depends on the channel |
{"op": "subscribed", "channels": [...]} | Subscription confirmed |
{"op": "unsubscribed", "channels": [...]} | Unsubscribe confirmed |
{"op": "pong"} | Reply to your ping |
{"op": "error", "message": "..."} | Informational error — does not disconnect you |
Every data event is wrapped in the same {channel, data} envelope, so one onmessage handler can route by channel.
{ "channel": "token:EPjFWd...:trades", "data": { "...": "the primitive's payload" } }Limits by tier
| Tier | Connections | Subscriptions / connection |
|---|---|---|
| Free | 0 | — |
| Layer 1 | 5 | 50 |
| Layer 2 | 20 | 50 |
| Layer 3 | 50 | 50 |
| Enterprise | Unlimited | Unlimited |
Exceeding the subscription cap returns an error message on the socket — it doesn’t drop the connection.
Delivery & heartbeat
If your client falls behind, messages are dropped silently rather than backing up and affecting other consumers — so keep your onmessage handler fast. For guaranteed delivery and a dedicated consumer position, use the Enterprise tier.
The server also sends a WebSocket-level ping roughly every 30 seconds to keep the connection alive; most clients answer automatically.
The reliable pattern: backfill current state from REST, then subscribe to the matching channel — events pick up from the moment you connect, with no gap. See the REST + Streaming Model.