Overview
Connect to receive real-time orderbook data for a specific market, including initial snapshots and incremental updates.
To avoid ambiguity, we denote the smallest possible multiple of USDC (0.000001 USDC) as one uusdc,
which stands for µUSDC (micro-USDC), and denote the smallest possible multiple of a contract (0.01 contract)
as one ccontract (centicontract). User-facing USDC balances are specified as fixed-point strings (e.g. "1.2625" for USDC). Contract quantities
in the API and SDK are specified as integer ccontracts strings (e.g. "1050" for 10.50 contracts at precision 2).
Connection
wss://api.bison.markets/ws/kalshi/orderbook/{marketTicker}
The Kalshi market ticker (e.g., KXBTC-100K-DEC31)
Example Connection
const marketTicker = "KXBTC-100K-DEC31" ;
const ws = new WebSocket (
`wss://api.bison.markets/ws/kalshi/orderbook/ ${ marketTicker } `
);
// Track orderbook state
let orderbook = {
yes: new Map (), // price_uusdc -> ccontracts (bigint)
no: new Map () // price_uusdc -> ccontracts (bigint)
};
ws . onopen = () => {
console . log ( `Connected to ${ marketTicker } orderbook` );
// Send heartbeat every 30 seconds
setInterval (() => {
if ( ws . readyState === WebSocket . OPEN ) {
ws . send ( JSON . stringify ({ type: "ping" }));
}
}, 30000 );
};
ws . onmessage = ( event ) => {
const data = JSON . parse ( event . data );
if ( data . type === "orderbook_snapshot" ) {
handleSnapshot ( data );
} else if ( data . type === "orderbook_delta" ) {
handleDelta ( data );
}
};
function handleSnapshot ( snapshot ) {
// Reset orderbook with full snapshot
orderbook . yes . clear ();
orderbook . no . clear ();
if ( snapshot . yes ) {
snapshot . yes . forEach ( level => {
orderbook . yes . set ( level . price_uusdc , BigInt ( level . ccontracts ));
});
}
if ( snapshot . no ) {
snapshot . no . forEach ( level => {
orderbook . no . set ( level . price_uusdc , BigInt ( level . ccontracts ));
});
}
console . log ( "Orderbook initialized:" , orderbook );
}
function handleDelta ( delta ) {
// Apply incremental update
const book = delta . side === "yes" ? orderbook . yes : orderbook . no ;
const currentCcontracts = book . get ( delta . price_uusdc ) || 0 n ;
const newCcontracts = currentCcontracts + BigInt ( delta . delta );
if ( newCcontracts <= 0 n ) {
book . delete ( delta . price_uusdc );
} else {
book . set ( delta . price_uusdc , newCcontracts );
}
console . log ( `Orderbook updated: ${ delta . side } @ ${ delta . price_uusdc } : ${ newCcontracts . toString () } ` );
}
Message Types
The Orderbook WebSocket sends two types of messages:
Snapshot Message
Sent immediately upon connection, providing the complete current orderbook state.
{
"type" : "orderbook_snapshot" ,
"market_ticker" : "KXBTC-100K-DEC31" ,
"yes" : [
{ "price_uusdc" : "750000" , "ccontracts" : "100" },
{ "price_uusdc" : "740000" , "ccontracts" : "250" },
{ "price_uusdc" : "730000" , "ccontracts" : "500" }
],
"no" : [
{ "price_uusdc" : "260000" , "ccontracts" : "150" },
{ "price_uusdc" : "270000" , "ccontracts" : "300" },
{ "price_uusdc" : "280000" , "ccontracts" : "400" }
]
}
Always "orderbook_snapshot"
Market ticker for this orderbook
Array of YES side price levels, ordered by price Price level in µUSDC (integer string)
Total ccontracts available at this price (integer string; 1 contract = 100 ccontracts)
Array of NO side price levels, ordered by price Structure is the same as yes array.
Delta Message
Sent for each orderbook change, providing an incremental update.
{
"type" : "orderbook_delta" ,
"market_ticker" : "KXBTC-100K-DEC31" ,
"side" : "yes" ,
"price_uusdc" : "750000" ,
"delta" : "50"
}
Market ticker for this update
Price level being updated (µUSDC integer string)
Change in ccontracts (integer string; positive for additions, negative for removals)
Maintaining Accurate State
To maintain an accurate orderbook:
Initialize with the snapshot on connection
Apply each delta by adding it to the current ccontracts at that price level
Remove price levels when their ccontracts reaches zero or below
Example: Orderbook Display
class OrderbookManager {
constructor ( marketTicker ) {
this . marketTicker = marketTicker ;
this . orderbook = {
yes: new Map (),
no: new Map ()
};
this . ws = null ;
}
connect () {
this . ws = new WebSocket (
`wss://api.bison.markets/ws/kalshi/orderbook/ ${ this . marketTicker } `
);
this . ws . onmessage = ( event ) => {
const data = JSON . parse ( event . data );
if ( data . type === "orderbook_snapshot" ) {
this . applySnapshot ( data );
} else if ( data . type === "orderbook_delta" ) {
this . applyDelta ( data );
}
};
// Heartbeat
setInterval (() => {
if ( this . ws . readyState === WebSocket . OPEN ) {
this . ws . send ( JSON . stringify ({ type: "ping" }));
}
}, 30000 );
}
applySnapshot ( snapshot ) {
this . orderbook . yes . clear ();
this . orderbook . no . clear ();
if ( snapshot . yes ) {
snapshot . yes . forEach ( level => {
this . orderbook . yes . set ( level . price_uusdc , BigInt ( level . ccontracts ));
});
}
if ( snapshot . no ) {
snapshot . no . forEach ( level => {
this . orderbook . no . set ( level . price_uusdc , BigInt ( level . ccontracts ));
});
}
this . render ();
}
applyDelta ( delta ) {
const book = delta . side === "yes" ? this . orderbook . yes : this . orderbook . no ;
const currentCcontracts = book . get ( delta . price_uusdc ) || 0 n ;
const newCcontracts = currentCcontracts + BigInt ( delta . delta );
if ( newCcontracts <= 0 n ) {
book . delete ( delta . price_uusdc );
} else {
book . set ( delta . price_uusdc , newCcontracts );
}
this . render ();
}
render () {
console . log ( ` \n === ${ this . marketTicker } Orderbook ===` );
// YES side (sorted descending by price)
console . log ( " \n YES:" );
const yesPrices = Array . from ( this . orderbook . yes . keys ()). sort (( a , b ) => Number ( b ) - Number ( a ));
yesPrices . forEach ( price => {
const ccontracts = this . orderbook . yes . get ( price ) || 0 n ;
const priceUSDC = ( Number ( price ) / 1_000_000 ). toFixed ( 2 );
console . log ( ` ${ priceUSDC } : ${ ccontracts . toString () } ccontracts` );
});
// NO side (sorted descending by price)
console . log ( " \n NO:" );
const noPrices = Array . from ( this . orderbook . no . keys ()). sort (( a , b ) => Number ( b ) - Number ( a ));
noPrices . forEach ( price => {
const ccontracts = this . orderbook . no . get ( price ) || 0 n ;
const priceUSDC = ( Number ( price ) / 1_000_000 ). toFixed ( 2 );
console . log ( ` ${ priceUSDC } : ${ ccontracts . toString () } ccontracts` );
});
}
getBestBidAsk () {
const yesPrices = Array . from ( this . orderbook . yes . keys ()). map ( Number );
const noPrices = Array . from ( this . orderbook . no . keys ()). map ( Number );
return {
yesBid: yesPrices . length > 0 ? Math . max ( ... yesPrices ) : null ,
yesAsk: yesPrices . length > 0 ? Math . min ( ... yesPrices ) : null ,
noBid: noPrices . length > 0 ? Math . max ( ... noPrices ) : null ,
noAsk: noPrices . length > 0 ? Math . min ( ... noPrices ) : null ,
};
}
disconnect () {
if ( this . ws ) {
this . ws . close ();
}
}
}
// Usage
const manager = new OrderbookManager ( "KXBTC-100K-DEC31" );
manager . connect ();
// Later, get best bid/ask
const { yesBid , yesAsk , noBid , noAsk } = manager . getBestBidAsk ();
console . log ( `Best YES: ${ yesBid } / ${ yesAsk } ` );
console . log ( `Best NO: ${ noBid } / ${ noAsk } ` );
Use Cases
Order Book Depth Display full market depth with all price levels
Trading Interfaces Build professional trading UIs with real-time order flow
Market Making Monitor liquidity for algorithmic trading strategies
Price Discovery Analyze order book imbalances and market sentiment
Best Practices
Initialize with Snapshot
Always start by processing the initial snapshot message to establish baseline state.
Apply Deltas Incrementally
Add delta values to existing ccontracts rather than replacing them.
Clean Up Zero Ccontracts
Remove price levels from your orderbook when their ccontracts reach zero or below.
Sort Price Levels
Sort bids descending and asks ascending for proper display.
Handle Reconnection
On reconnection, the snapshot will reset your state automatically.