API Documentation
Build trading bots and integrations with the BBpoly REST API.
Base URL
https://api.bbpoly.io/apiAuthentication
Include your JWT token in the Authorization header:
Endpoints
Authenticate and receive a JWT token. Use this token in the Authorization header for all authenticated requests.
Request Body
{
"email": "trader@example.com",
"password": "your-password"
}Response
{
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "usr_abc123",
"email": "trader@example.com",
"username": "trader1"
}
}
}WebSocket API
Live order book, price, and trade updates are pushed over a socket.io connection on the /markets namespace. Connect, join a market room, and listen for events — no authentication is required for read-only market data.
WebSocket URL
wss://api.bbpoly.io (namespace: /markets)Connecting
Use any socket.io v4 client. Connect to the /markets namespace, then subscribe to a market with the joinMarket event.
import { io } from 'socket.io-client';
const socket = io('wss://api.bbpoly.io/markets', {
transports: ['websocket', 'polling'],
});
socket.on('connect', () => {
socket.emit('joinMarket', 'mkt_001');
});
socket.on('book:update', (book) => {
console.log(book.bestBid, book.bestAsk, book.mid);
});Market rooms
Most events are scoped to a per-market room. Emit joinMarket with a marketId to subscribe, and leaveMarket to unsubscribe. The btc:price event is global and arrives as soon as you connect — no join required.
// Subscribe to a market's book, price, and trade stream
socket.emit('joinMarket', 'mkt_001');
// Stop receiving that market’s events
socket.emit('leaveMarket', 'mkt_001');Events
joinMarketSubscribe to a market room. Send the marketId as the payload. The server acknowledges with a joinedMarket event.
{
"emit": "joinMarket",
"payload": "mkt_001",
"ack": {
"event": "joinedMarket",
"data": {
"marketId": "mkt_001"
}
}
}leaveMarketUnsubscribe from a market room. Send the marketId as the payload. Acknowledged with a leftMarket event.
{
"emit": "leaveMarket",
"payload": "mkt_001",
"ack": {
"event": "leftMarket",
"data": {
"marketId": "mkt_001"
}
}
}book:updateFull top-of-book depth (up to 10 levels each side) whenever the order book changes. Prices are given in both YES and NO space; mid/bestBid/bestAsk are in YES space. ts is a Unix epoch in milliseconds.
{
"marketId": "mkt_001",
"yes": {
"bids": [
{
"price": 0.31,
"size": 500
}
],
"asks": [
{
"price": 0.33,
"size": 400
}
]
},
"no": {
"bids": [
{
"price": 0.67,
"size": 400
}
],
"asks": [
{
"price": 0.69,
"size": 500
}
]
},
"mid": 0.32,
"bestBid": 0.31,
"bestAsk": 0.33,
"ts": 1776000000000
}priceUpdateEmitted after each trade. yesPrice + noPrice always sum to 1; lastTradePrice is the most recent fill price in YES space.
{
"marketId": "mkt_001",
"yesPrice": 0.32,
"noPrice": 0.68,
"lastTradePrice": 0.33,
"timestamp": "2026-04-14T10:30:15Z"
}trade:newA single executed trade. outcome is YES or NO, takerAction is BUY or SELL, and direction (up/down) reflects whether the taker pushed the price toward YES (up) or NO (down).
{
"id": "trd_abc123",
"marketId": "mkt_001",
"price": 0.33,
"size": 100,
"outcome": "YES",
"takerAction": "BUY",
"direction": "up",
"createdAt": "2026-04-14T10:30:15Z"
}oddsUpdateUpdated implied probabilities for each outcome of a market. probability is a fraction in [0, 1]; totalStaked is the cumulative size resting on that outcome.
{
"marketId": "mkt_001",
"odds": [
{
"id": "out_yes",
"label": "Yes",
"probability": 0.32,
"totalStaked": 12500
},
{
"id": "out_no",
"label": "No",
"probability": 0.68,
"totalStaked": 26500
}
]
}marketStatusChangeFired when a market changes lifecycle state (e.g. ACTIVE → CLOSED → RESOLVED). Re-fetch the market on receipt.
{
"marketId": "mkt_001",
"status": "RESOLVED"
}btc:priceGlobal BTC/USDT spot price from the Binance feed, pushed roughly once per second whenever the price moves. Broadcast to every connected client — you do not need to join a room. timestamp is a Unix epoch in milliseconds.
{
"price": 97542.31,
"symbol": "BTCUSDT",
"timestamp": 1776000000000
}Rate Limits
API requests are rate-limited to 100 requests per minute per IP for unauthenticated endpoints, and 300 requests per minute per user for authenticated endpoints. Order placement is limited to 10 orders per second. If you exceed these limits, you will receive a 429 response.