WebSocket API
Real-time subscriptions for market updates, trades, and order book changes.
Connection
Endpoint
| Environment | URL |
|---|---|
| Production | wss://api.seesaw.markets/ws |
| Testnet | wss://api.testnet.seesaw.markets/ws |
Connection Example
const ws = new WebSocket('wss://api.seesaw.markets/ws');
ws.onopen = () => {
console.log('Connected');
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log('Received:', message);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Disconnected');
};
Message Format
Client Messages
interface ClientMessage {
type: 'subscribe' | 'unsubscribe' | 'ping';
channel?: string;
marketId?: string;
// Additional parameters
}
Server Messages
interface ServerMessage {
type: 'subscribed' | 'unsubscribed' | 'update' | 'error' | 'pong';
channel?: string;
data?: any;
error?: string;
timestamp: number;
}
Channels
market.update
Receive updates when market state changes.
Subscribe:
{
"type": "subscribe",
"channel": "market.update",
"marketId": "1234567"
}
Updates:
{
"type": "update",
"channel": "market.update",
"data": {
"marketId": "1234567",
"field": "startPrice",
"value": "68543210000",
"state": "TRADING",
"timestamp": 1704067200
},
"timestamp": 1704067200
}
Fields that trigger updates:
startPrice- Start snapshot capturedendPrice- End snapshot capturedoutcome- Market resolvedstate- State transition
orderbook.update
Receive order book updates.
Subscribe:
{
"type": "subscribe",
"channel": "orderbook.update",
"marketId": "1234567"
}
Updates:
{
"type": "update",
"channel": "orderbook.update",
"data": {
"marketId": "1234567",
"action": "add",
"side": "bid",
"order": {
"orderId": "123456789",
"owner": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"priceBps": 5800,
"quantity": "10000000000"
},
"bestBid": 5800,
"bestAsk": 6000,
"timestamp": 1704067300
},
"timestamp": 1704067300
}
Actions:
add- New order addedremove- Order cancelled or fully filledupdate- Order partially filled
orderbook.snapshot
Receive full order book snapshots periodically.
Subscribe:
{
"type": "subscribe",
"channel": "orderbook.snapshot",
"marketId": "1234567",
"interval": 5000
}
Snapshots:
{
"type": "update",
"channel": "orderbook.snapshot",
"data": {
"marketId": "1234567",
"bestBid": 5800,
"bestAsk": 6000,
"bids": [
{ "price": 5800, "quantity": "10000000000" },
{ "price": 5700, "quantity": "25000000000" }
],
"asks": [
{ "price": 6000, "quantity": "5000000000" },
{ "price": 6100, "quantity": "12000000000" }
],
"timestamp": 1704067300
},
"timestamp": 1704067300
}
trade.new
Receive new trade notifications.
Subscribe:
{
"type": "subscribe",
"channel": "trade.new",
"marketId": "1234567"
}
Updates:
{
"type": "update",
"channel": "trade.new",
"data": {
"id": "trade_abc123",
"marketId": "1234567",
"taker": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"maker": "8yLYtg3CW87d97TXJSDpbD5jBkheTqA83TZRuJosgBsV",
"side": "BuyYes",
"priceBps": 5800,
"quantity": "5000000000",
"timestamp": 1704067500,
"signature": "5wHu..."
},
"timestamp": 1704067500
}
position.update
Receive updates for a specific position.
Subscribe:
{
"type": "subscribe",
"channel": "position.update",
"marketId": "1234567",
"owner": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
}
Updates:
{
"type": "update",
"channel": "position.update",
"data": {
"marketId": "1234567",
"owner": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"yesShares": "105000000000",
"noShares": "50000000000",
"change": {
"field": "yesShares",
"delta": "5000000000",
"reason": "trade"
},
"timestamp": 1704067500
},
"timestamp": 1704067500
}
oracle.price
Receive Pyth oracle price updates.
Subscribe:
{
"type": "subscribe",
"channel": "oracle.price",
"pythFeed": "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
}
Updates:
{
"type": "update",
"channel": "oracle.price",
"data": {
"feed": "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
"price": "68550000000",
"confidence": "12000000",
"exponent": -8,
"publishTime": 1704067501
},
"timestamp": 1704067501
}
Subscription Management
Subscribe
{
"type": "subscribe",
"channel": "market.update",
"marketId": "1234567"
}
Response:
{
"type": "subscribed",
"channel": "market.update",
"marketId": "1234567",
"timestamp": 1704067200
}
Unsubscribe
{
"type": "unsubscribe",
"channel": "market.update",
"marketId": "1234567"
}
Response:
{
"type": "unsubscribed",
"channel": "market.update",
"marketId": "1234567",
"timestamp": 1704067200
}
Ping/Pong
Keep connection alive:
{ "type": "ping" }
Response:
{
"type": "pong",
"timestamp": 1704067200
}
Error Handling
Error Response
{
"type": "error",
"error": "INVALID_CHANNEL",
"message": "Unknown channel: invalid.channel",
"timestamp": 1704067200
}
Error Codes
| Code | Description |
|---|---|
INVALID_CHANNEL | Unknown channel name |
INVALID_MARKET | Invalid market ID |
SUBSCRIPTION_LIMIT | Too many subscriptions |
RATE_LIMITED | Too many messages |
INTERNAL_ERROR | Server error |
Reconnection
Strategy
class SeesawWebSocket {
constructor(url) {
this.url = url;
this.subscriptions = new Map();
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('Connected');
this.resubscribe();
};
this.ws.onclose = () => {
console.log('Disconnected, reconnecting...');
setTimeout(() => this.connect(), 1000);
};
this.ws.onmessage = (event) => {
this.handleMessage(JSON.parse(event.data));
};
}
subscribe(channel, params, callback) {
const key = `${channel}:${JSON.stringify(params)}`;
this.subscriptions.set(key, { channel, params, callback });
this.ws.send(
JSON.stringify({
type: 'subscribe',
channel,
...params,
})
);
}
resubscribe() {
for (const [key, sub] of this.subscriptions) {
this.ws.send(
JSON.stringify({
type: 'subscribe',
channel: sub.channel,
...sub.params,
})
);
}
}
handleMessage(message) {
if (message.type === 'update') {
const key = `${message.channel}:${JSON.stringify(message.data.marketId || {})}`;
const sub = this.subscriptions.get(key);
if (sub) {
sub.callback(message.data);
}
}
}
}
// Usage
const ws = new SeesawWebSocket('wss://api.seesaw.markets/ws');
ws.subscribe('market.update', { marketId: '1234567' }, (data) => {
console.log('Market update:', data);
});
ws.subscribe('trade.new', { marketId: '1234567' }, (data) => {
console.log('New trade:', data);
});
Complete Example
Real-Time Trading Dashboard
class TradingDashboard {
constructor(marketId) {
this.marketId = marketId;
this.market = null;
this.orderbook = { bids: [], asks: [] };
this.trades = [];
this.connect();
}
connect() {
this.ws = new WebSocket('wss://api.seesaw.markets/ws');
this.ws.onopen = () => {
// Subscribe to all relevant channels
this.ws.send(
JSON.stringify({
type: 'subscribe',
channel: 'market.update',
marketId: this.marketId,
})
);
this.ws.send(
JSON.stringify({
type: 'subscribe',
channel: 'orderbook.update',
marketId: this.marketId,
})
);
this.ws.send(
JSON.stringify({
type: 'subscribe',
channel: 'trade.new',
marketId: this.marketId,
})
);
// Start ping interval
this.pingInterval = setInterval(() => {
this.ws.send(JSON.stringify({ type: 'ping' }));
}, 30000);
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
this.ws.onclose = () => {
clearInterval(this.pingInterval);
setTimeout(() => this.connect(), 1000);
};
}
handleMessage(message) {
if (message.type !== 'update') return;
switch (message.channel) {
case 'market.update':
this.handleMarketUpdate(message.data);
break;
case 'orderbook.update':
this.handleOrderbookUpdate(message.data);
break;
case 'trade.new':
this.handleNewTrade(message.data);
break;
}
this.render();
}
handleMarketUpdate(data) {
if (data.field === 'outcome') {
console.log(`Market resolved: ${data.value === 1 ? 'UP' : 'DOWN'}`);
}
this.market = { ...this.market, [data.field]: data.value };
}
handleOrderbookUpdate(data) {
if (data.action === 'add') {
const side = data.side === 'bid' ? this.orderbook.bids : this.orderbook.asks;
side.push(data.order);
side.sort((a, b) =>
data.side === 'bid' ? b.priceBps - a.priceBps : a.priceBps - b.priceBps
);
} else if (data.action === 'remove') {
this.orderbook.bids = this.orderbook.bids.filter((o) => o.orderId !== data.order.orderId);
this.orderbook.asks = this.orderbook.asks.filter((o) => o.orderId !== data.order.orderId);
}
}
handleNewTrade(data) {
this.trades.unshift(data);
if (this.trades.length > 50) {
this.trades.pop();
}
}
render() {
// Update UI
console.log('=== Dashboard Update ===');
console.log('Best Bid:', this.orderbook.bids[0]?.priceBps);
console.log('Best Ask:', this.orderbook.asks[0]?.priceBps);
console.log('Recent Trades:', this.trades.length);
}
}
// Usage
const dashboard = new TradingDashboard('1234567');
Next Steps
- Review REST Endpoints for data queries
- See SDK Guide for transaction building