Connecting
Open a WebSocket connection to one of the available endpoints:
wss://socket.bayse.markets/ws/v1/markets
wss://socket.bayse.markets/ws/v1/user
wss://socket.bayse.markets/ws/v1/realtime
The /ws/v1/user endpoint requires per-message authentication. See User orders for details.
On a successful connection, the server sends a connected message:
{
"type": "connected",
"status": "connected",
"clientId": "a1b2c3d4-...",
"message": "Successfully connected to WebSocket server",
"timestamp": 1700000000000
}
const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/markets");
ws.addEventListener("open", () => {
console.log("connected");
});
import asyncio
import websockets
async def connect():
async with websockets.connect("wss://socket.bayse.markets/ws/v1/markets") as ws:
print("connected")
async for message in ws:
print(message)
asyncio.run(connect())
package main
import (
"fmt"
"log"
"github.com/gorilla/websocket"
)
func main() {
conn, _, err := websocket.DefaultDialer.Dial("wss://socket.bayse.markets/ws/v1/markets", nil)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
fmt.Println("connected")
}
All messages are JSON. The server may batch multiple JSON objects into a single WebSocket frame separated by newlines (\n). Clients must split on newlines before parsing each line:
ws.addEventListener("message", (event) => {
for (const line of event.data.split("\n")) {
if (line.trim()) {
const msg = JSON.parse(line);
// handle msg
}
}
});
Client messages
Messages sent from the client to the server follow this structure:
{
"type": "subscribe",
"channel": "prices",
"eventId": "EVENT_ID",
"marketId": "MARKET_ID",
"marketIds": ["MARKET_ID"],
"currency": "USD",
"symbols": ["BTCUSDT"],
"room": "ROOM_NAME"
}
Only include the fields relevant to the channel you are using. The type field is always required.
| Field | Type | Description |
|---|
type | string | Required. One of subscribe, unsubscribe, ping. |
channel | string | Subscription channel (e.g., activity, prices, orderbook, asset_prices). |
eventId | string | Prediction event UUID. Required for activity and prices. |
marketId | string | Market UUID. Optional filter for activity. |
marketIds | string[] | Market UUIDs (max 10). Required for orderbook. |
currency | string | USD or NGN. Optional for orderbook. |
symbols | string[] | Asset symbols (e.g., BTCUSDT). Required for asset_prices. |
room | string | Room name. Required for unsubscribe. |
Server messages
Messages sent from the server to the client:
{
"type": "price_update",
"data": { ... },
"timestamp": 1700000000000
}
| Field | Type | Description |
|---|
type | string | The event type (e.g., price_update). |
data | object | Event payload. Varies by event type. |
timestamp | number | Unix timestamp (milliseconds). |
status | string | Present on the connected message. |
clientId | string | Your assigned client ID. |
message | string | Human-readable text when provided. |
room | string | Room name for subscribe/unsubscribe responses. |
Subscribing and unsubscribing
Subscribe to a channel by sending a subscribe message with the required fields:
{ "type": "subscribe", "channel": "prices", "eventId": "EVENT_ID" }
Unsubscribe by sending an unsubscribe message with the room name:
{ "type": "unsubscribe", "room": "prices:EVENT_ID" }
The server confirms with an unsubscribed message:
{
"type": "unsubscribed",
"room": "prices:EVENT_ID",
"message": "Unsubscribed from: prices:EVENT_ID",
"timestamp": 1700000000000
}
Room naming
Room names are constructed from the channel and subscription parameters:
| Subscription | Room name |
|---|
| Activity feed | activity:<eventId> |
| Activity feed (market-specific) | activity:<eventId>:<marketId> |
| Price updates | prices:<eventId> |
| Orderbook (USD) | orderbook:<marketId> |
| Orderbook (NGN) | orderbook:<marketId>:NGN |
| Orders | orders:<userId>:<marketId> |
| Asset prices | asset_prices:<SYMBOL> |
Keepalive
The server sends WebSocket-level ping frames every ~54 seconds. Most WebSocket libraries handle pong replies automatically.
You can also send an application-level ping to verify the connection is alive:
The server replies with:
{ "type": "pong", "timestamp": 1700000000000 }
If the server does not receive a pong within 60 seconds, the connection is closed. Ensure your client handles WebSocket ping/pong frames.
Reconnection
Connections can drop due to network issues, server restarts, or idle timeouts. Implement reconnection with exponential backoff:
function connect() {
const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/markets");
let attempt = 0;
ws.addEventListener("open", () => {
attempt = 0;
// re-subscribe to channels
});
ws.addEventListener("close", () => {
const delay = Math.min(1000 * 2 ** attempt, 30000);
attempt++;
setTimeout(connect, delay);
});
return ws;
}
import asyncio
import websockets
async def connect():
attempt = 0
while True:
try:
async with websockets.connect("wss://socket.bayse.markets/ws/v1/markets") as ws:
attempt = 0
# re-subscribe to channels
async for message in ws:
print(message)
except websockets.ConnectionClosed:
delay = min(2 ** attempt, 30)
attempt += 1
await asyncio.sleep(delay)
func connectWithRetry() {
attempt := 0
for {
conn, _, err := websocket.DefaultDialer.Dial(
"wss://socket.bayse.markets/ws/v1/markets", nil,
)
if err != nil {
delay := time.Duration(math.Min(math.Pow(2, float64(attempt)), 30)) * time.Second
attempt++
time.Sleep(delay)
continue
}
attempt = 0
// re-subscribe to channels
handleConnection(conn)
}
}
Limits
| Limit | Value | Scope |
|---|
| Message rate | 10 messages/second | Per connection |
WebSocket upgrades are rate-limited per IP.