Endpoint
Connection flow
1. Connect
Open a WebSocket connection. No authentication is needed at connection time.
2. Login
Send a login message within 30 seconds of connecting:
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": []
}
An empty channels array subscribes to all available channels. To subscribe to specific channels:
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets", "settlements"]
}
You can optionally enable reliable delivery with message acknowledgments:
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets"],
"reliableDelivery": true
}
3. Login confirmation
On success, the server responds with:
{
"type": "login_ok",
"clientName": "your-client",
"channels": ["orders", "bets", "settlements"],
"subscriptionId": 1,
"access": {
"clientFiltered": ["orders", "bets", "settlements", "accounts", "balance", "betslip"],
"global": ["fixtures", "currencies", "status", "emergency"]
},
"reliableDelivery": false,
"features": {
"messageOrdering": true,
"acknowledgments": false,
"batchAck": false
}
}
4. Receive updates
Data messages follow this format:
{
"type": "data",
"channel": "orders",
"event": "INSERT",
"payload": { ... },
"ts": 1771183909789,
"seq": 1
}
| Field | Description |
|---|
type | Always "data" for data messages |
channel | Which channel this message belongs to |
event | Event type: INSERT, UPDATE, DELETE, SETTLED, STATUS |
payload | The full updated object (see per-channel payload schemas below) |
ts | Server timestamp (milliseconds since epoch) |
seq | Per-subscription monotonically increasing sequence number |
requireAck | Present and true only when reliable delivery is enabled |
5. Keep alive
The server sends a ping every 30 seconds:
Respond with pong within 120 seconds or the connection is closed:
You can also send pings from the client — the server responds with pong.
Channels
Client-filtered channels
These channels only deliver data belonging to your clientName:
| Channel | Events | Description |
|---|
orders | INSERT, UPDATE, DELETE | Order placement, fills, status changes |
bets | INSERT, UPDATE, DELETE | Bet placement, confirmation, and removal |
settlements | SETTLED | Settlement status updates (same payload as bets) |
accounts | INSERT, UPDATE, DELETE | Account creation, updates, and deletion |
balance | UPDATE | Balance change notifications |
betslip | UPDATE | Real-time odds updates for active betslip subscriptions (60s window after each GET /betslip call) |
Global channels
All subscribers receive these:
| Channel | Events | Description |
|---|
fixtures | UPDATE | Fixture metadata and score changes |
currencies | UPDATE | Currency exchange rate updates |
status | STATUS | System status changes |
emergency | STATUS | Emergency mode activation/deactivation |
Payload schemas
Orders payload
Full database row from the orders table:
{
"orderId": 327,
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"client": "demo",
"clientName": "demo",
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"orderPrice": 1.95,
"orderStake": 10.0,
"filledStake": 10.0,
"remainingStake": 0.0,
"orderStatus": "FILLED",
"statusReason": null,
"userRef": "bettor1234",
"testOrder": false,
"acceptBetterOdds": true,
"acceptPartialStake": true,
"orderCurrency": "USD",
"back": true,
"allowedBookmakers": "*",
"oddsInfo": null,
"meta": {},
"expiresAt": "2026-02-07T17:29:37+00:00",
"filledAt": "2026-02-07T17:29:32+00:00",
"createdAt": "2026-02-07T17:29:32+00:00",
"updatedAt": "2026-02-07T17:29:32+00:00"
}
Bets / settlements payload
Full database row from the bets table:
{
"betId": 73,
"orderId": 327,
"bookmaker": "pinnacle",
"bookmakerBetId": "3332684214",
"betStatus": "CONFIRMED",
"settlementStatus": "UNSETTLED",
"placedStake": 10.0,
"placedPrice": 1.98,
"placedCurrency": "USD",
"account": "pinnacle_main",
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"userRef": "bettor1234",
"testBet": false,
"client": "demo",
"clientName": "demo",
"sentData": { "stake": 10.0, "price": 1.95 },
"receivedData": { "betId": "3332684214", "status": "accepted", "price": 1.98 },
"settlementAmount": null,
"settlementReason": null,
"settledAt": null,
"declineReason": null,
"betRequestId": null,
"oddsInfo": null,
"meta": {},
"placedAt": "2026-02-07T17:29:32+00:00",
"createdAt": "2026-02-07T17:29:32+00:00",
"updatedAt": "2026-02-07T17:29:32+00:00"
}
Balance payload
{
"clientName": "demo",
"bookmaker": "pinnacle",
"username": "pinnacle_main",
"balance": 4990.0,
"currency": "USD",
"ts": 1771183910123
}
Emergency payload
{
"active": true,
"reason": "Upstream provider maintenance",
"ts": 1771183910123
}
Betslip payload
Real-time odds updates pushed while a betslip subscription is active. Calling GET /betslip registers a 60-second sliding window — during this window, price changes are broadcast in the same shape as the betslip REST response. Each poll resets the timer.
This applies to both fixture-based and futures-based betslip subscriptions. For futures, the payload includes futureId and participantId instead of (or in addition to) fixtureId.
Fixture betslip example:
{
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"client": "demo",
"userRef": null,
"odds": {
"pinnacle": {
"odds_12345": {
"price": 1.98,
"limit": 500.0,
"limitMin": 1.0,
"limitCurrency": "USD",
"limitUsd": 500.0,
"limitMinUsd": 1.0,
"active": true,
"account": "pinnacle_main"
}
}
}
}
Futures betslip example (coming soon):
{
"futureId": "fut_123456",
"outcomeId": 0,
"playerId": 0,
"participantId": 5,
"client": "demo",
"userRef": null,
"odds": {
"pinnacle": {
"fut_123456:pinnacle:0:0:5": {
"price": 3.50,
"limit": 1000.0,
"limitMin": 5.0,
"limitCurrency": "USD",
"limitUsd": 1000.0,
"limitMinUsd": 5.0,
"active": true,
"account": "pinnacle_main"
}
}
}
}
Futures betslip WebSocket broadcasting is not yet enabled. The subscription infrastructure is in place and will be activated in an upcoming release.
Connection limits
| Setting | Value |
|---|
| Max connections per API key | 5 |
| Auth timeout | 30 seconds |
| Server ping interval | 30 seconds |
| Pong timeout (disconnect) | 120 seconds |
| Message buffer (reliable delivery) | 100 messages |
| Output queue per client | 2000 messages |
Example: Python client
import asyncio
import json
import websockets
async def connect():
uri = "wss://v2.55-tech.com/ws"
async with websockets.connect(uri) as ws:
# Login
await ws.send(json.dumps({
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets", "settlements"]
}))
# Wait for login confirmation
login_resp = json.loads(await ws.recv())
print(f"Logged in as {login_resp.get('clientName')}")
# Listen for updates (respond to pings automatically)
async for message in ws:
data = json.loads(message)
if data["type"] == "ping":
await ws.send(json.dumps({"type": "pong"}))
elif data["type"] == "data":
print(f"[{data['channel']}:{data['event']}] {data['payload']}")
asyncio.run(connect())
Example: JavaScript client
const WebSocket = require('ws');
const ws = new WebSocket('wss://v2.55-tech.com/ws');
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'login',
apiKey: 'YOUR_API_KEY',
channels: ['orders', 'bets', 'settlements']
}));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'login_ok') {
console.log(`Logged in as ${msg.clientName}`);
}
if (msg.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }));
}
if (msg.type === 'data') {
console.log(`[${msg.channel}:${msg.event}]`, msg.payload);
}
});