Overview
The SDK provides high-level methods for trading on Kalshi markets:
executeBuyFlow / executeSellFlow - Place buy/sell orders with automatic signature generation
executeCancelOrderFlow - Cancel existing orders
executeMintFlow / executeBurnFlow - Create/destroy position tokens
getUserOrders / getUserPositions - Query user’s orders and positions
To avoid ambiguity, we denote the smallest possible multiple of USDC (0.000001 USDC) as one uusdc,
which stands for µUSDC (micro-USDC).
Developer Accounts
If you have a developer account, you can set it at the client level to automatically apply it to all orders:
const client = createBisonClient ({
baseUrl: 'https://api.bison.markets' ,
devAccountId: 'your-dev-account-id'
});
// All orders automatically include your dev account ID
await client . executeBuyFlow ({
walletClient ,
publicClient ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
chain: 'base' ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
side: 'yes' ,
number: 10 ,
priceUusdc: 650000 ,
});
You can also override the dev account for specific orders when using the lower-level placeOrder method:
await client . placeOrder ({
chain: 'base' ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
number: 10 ,
priceUusdc: 650000 ,
action: 'buy' ,
side: 'yes' ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
signature: '0x...' ,
expiry: 1234567890 ,
devAccountId: 'different-dev-account' // Overrides client default
});
See the Developer Accounts guide for more information on how to get a dev account and earn from trading fees.
Placing Orders
Place limit orders on Kalshi markets. The SDK handles EIP-712 signature generation automatically.
executeBuyFlow
async executeBuyFlow ( params : {
walletClient: WalletClient ;
publicClient : PublicClient ;
userAddress : `0x ${ string } ` ;
chain : string ;
marketId : string ;
side : 'yes' | 'no' ;
number : number ;
priceUusdc : number ;
onEvent ?: ( event : BisonEvent ) => void ;
onError ?: ( error : Error ) => void ;
}): Promise < { disconnect : () => void ; txHash : `0x ${ string } ` | null } >
Parameters
Viem wallet client for signing messages
Viem public client for reading contract state
Chain identifier ("base" or "bsc"). Vault address is automatically resolved from the /info endpoint.
Kalshi market ticker (e.g., "KXFEDDECISION-26JAN-T425")
Position side: "yes" or "no"
Number of contracts to buy
Optional callback for order events (placed, filled, etc.)
Optional callback for WebSocket errors
Returns
Object containing:
disconnect - Function to stop listening for events
txHash - Transaction hash (currently always null for off-chain orders)
Example
import { createBisonClient } from '@bison-markets/sdk-ts' ;
import { createWalletClient , createPublicClient , http , custom } from 'viem' ;
import { base } from 'viem/chains' ;
const client = createBisonClient ({
baseUrl: 'https://api.bison.markets'
});
const walletClient = createWalletClient ({
chain: base ,
transport: custom ( window . ethereum ! ),
});
const publicClient = createPublicClient ({
chain: base ,
transport: http (),
});
// Buy 10 YES contracts at 65 cents each
const result = await client . executeBuyFlow ({
walletClient ,
publicClient ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
chain: 'base' ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
side: 'yes' ,
number: 10 ,
priceUusdc: 650000 , // $0.65 * 10^6
onEvent : ( event ) => {
if ( event . type === 'order_placed' ) {
console . log ( 'Order placed successfully:' , event . orderId );
} else if ( event . type === 'order_filled' ) {
console . log ( 'Order filled:' , event . orderId );
}
},
onError : ( error ) => {
console . error ( 'WebSocket error:' , error );
},
});
// Stop listening for events when done
// result.disconnect();
executeSellFlow
Works identically to executeBuyFlow but places a sell order.
async executeSellFlow ( params : {
walletClient: WalletClient ;
publicClient : PublicClient ;
userAddress : `0x ${ string } ` ;
chain : string ;
marketId : string ;
side : 'yes' | 'no' ;
number : number ;
priceUusdc : number ;
onEvent ?: ( event : BisonEvent ) => void ;
onError ?: ( error : Error ) => void ;
}): Promise < { disconnect : () => void ; txHash : `0x ${ string } ` | null } >
Example
// Sell 5 NO contracts at 40 cents each
const result = await client . executeSellFlow ({
walletClient ,
publicClient ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
chain: 'base' ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
side: 'no' ,
number: 5 ,
priceUusdc: 400000 , // $0.40 * 10^6
});
Cancelling Orders
Cancel an existing open order.
executeCancelOrderFlow
async executeCancelOrderFlow ( params : {
walletClient: WalletClient ;
userAddress : `0x ${ string } ` ;
chain : string ;
orderId : string ;
}): Promise < void >
Parameters
Viem wallet client for signing messages
Chain identifier ("base" or "bsc"). Vault address is automatically resolved from the /info endpoint.
ID of the order to cancel (from order event or getUserOrders)
Example
await client . executeCancelOrderFlow ({
walletClient ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
chain: 'base' ,
orderId: 'abc123-order-id' ,
});
console . log ( 'Order cancellation requested' );
Querying Orders & Positions
getUserOrders
Get orders for a user with optional filtering and pagination.
async getUserOrders (
userId : string ,
params ?: GetUserOrdersParams
): Promise < GetUserOrdersResponse >
Parameters
Optional filtering and pagination parameters
Filter by order status: "pending", "filled", "cancelled", or "closed" (filled OR cancelled)
Filter by action: "buy" or "sell"
Filter by side: "yes" or "no"
Orders created after this timestamp (milliseconds)
Orders created before this timestamp (milliseconds)
Sort by field: "createdAt" (default) or "updatedAt"
Sort direction: "desc" (default) or "asc"
Max orders per page (default: 50, max: 200)
Pagination cursor from previous response
Response
The response includes an orders array and a pagination object:
{
orders : Order [];
pagination : {
total : number ; // Total matching orders
hasMore : boolean ; // More pages available
nextCursor ?: string ; // Cursor for next page
};
}
Examples
Get all pending orders:
const { orders } = await client . getUserOrders (
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
{ status: 'pending' }
);
console . log ( 'Open orders:' );
orders . forEach ( order => {
console . log ( ` ${ order . action } ${ order . requestedQuantity } ${ order . side } @ $ ${ order . priceUusdc / 1_000_000 } ` );
console . log ( ` Market: ${ order . marketId } ` );
console . log ( ` Order ID: ${ order . kalshiOrderId } ` );
});
Get order history with pagination:
// First page of completed orders
const result = await client . getUserOrders (
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
{
status: 'closed' ,
sortBy: 'updatedAt' ,
sortOrder: 'desc' ,
limit: 10
}
);
console . log ( `Showing ${ result . orders . length } of ${ result . pagination . total } orders` );
// Fetch next page if available
if ( result . pagination . hasMore ) {
const nextPage = await client . getUserOrders (
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
{
status: 'closed' ,
sortBy: 'updatedAt' ,
sortOrder: 'desc' ,
limit: 10 ,
cursor: result . pagination . nextCursor
}
);
}
Filter by market:
const { orders } = await client . getUserOrders (
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
{ marketId: 'KXFEDDECISION-26JAN-T425' }
);
Filter by date range:
const oneWeekAgo = Date . now () - 7 * 24 * 60 * 60 * 1000 ;
const { orders } = await client . getUserOrders (
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
{
status: 'filled' ,
createdAfter: oneWeekAgo
}
);
getUserPositions
Get all positions for a user.
async getUserPositions ( userId : string ): Promise < GetUserPositionsResponse >
Example
const { positions } = await client . getUserPositions (
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
);
console . log ( 'Current positions:' );
positions . forEach ( position => {
console . log ( `Market: ${ position . marketId } ` );
console . log ( ` ${ position . side . toUpperCase () } : ${ position . quantity } contracts` );
});
Position Token Utilities
Get token addresses and balances for position tokens.
getPositionTokenAddress
async getPositionTokenAddress ( params : {
publicClient: PublicClient ;
chain : 'base' | 'bsc' ;
marketId : string ;
side : 'yes' | 'no' ;
}): Promise < `0x ${ string } ` | null >
Returns the ERC20 token address for a position, or null if not yet created. The vault address is automatically resolved from the /info endpoint.
getTokenBalance
async getTokenBalance ( params : {
publicClient: PublicClient ;
tokenAddress : `0x ${ string } ` ;
userAddress : `0x ${ string } ` ;
}): Promise < bigint >
Returns the token balance for a user.
addTokenToWallet
async addTokenToWallet ( params : {
tokenAddress: `0x ${ string } ` ;
marketId : string ;
side : 'yes' | 'no' ;
}): Promise < boolean >
Prompts the user to add a position token to their wallet (MetaMask, etc.).
Example
// Get token address
const tokenAddress = await client . getPositionTokenAddress ({
publicClient ,
chain: 'base' ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
side: 'yes' ,
});
if ( tokenAddress ) {
// Get user's balance
const balance = await client . getTokenBalance ({
publicClient ,
tokenAddress ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
});
console . log ( 'Token balance:' , balance . toString ());
// Add to wallet
const added = await client . addTokenToWallet ({
tokenAddress ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
side: 'yes' ,
});
if ( added ) {
console . log ( 'Token added to wallet' );
}
}
Error Handling
try {
await client . executeBuyFlow ({
walletClient ,
publicClient ,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ,
chain: 'base' ,
marketId: 'KXFEDDECISION-26JAN-T425' ,
side: 'yes' ,
number: 10 ,
priceUusdc: 650000 ,
});
} catch ( error ) {
if ( error instanceof Error ) {
if ( error . message . includes ( 'insufficient balance' )) {
console . error ( 'Not enough USDC deposited' );
} else if ( error . message . includes ( 'User rejected' )) {
console . error ( 'User cancelled signature request' );
} else {
console . error ( 'Failed to place order:' , error . message );
}
}
}
Important Notes
All order prices are in µUSDC (1 USD = 1,000,000 µUSDC, so $0.65 = 650000 µUSDC)
Order signatures expire after 10 minutes
Minting withdraws positions from the exchange to your wallet as ERC-20 tokens
Burning deposits ERC-20 tokens from your wallet back to the exchange as tradeable positions
The SDK automatically handles EIP-712 signature generation
WebSocket connection is automatically established for buy/sell flows
Always call disconnect() to clean up WebSocket connections
Developer account IDs set at the client level are automatically applied to all orders