Skip to main content

Getting Orderbook Data

The Get Market Orderbook endpoint returns the current state of bids for a specific market.

Request Format

GET /markets/{ticker}/orderbook
No authentication is required for this endpoint.

Example Request

import requests

# Get orderbook for a specific market
market_ticker = "KXHIGHNY-24JAN01-T60"
url = f"https://api.elections.kalshi.com/trade-api/v2/markets/{market_ticker}/orderbook"

response = requests.get(url)
orderbook_data = response.json()

Response Structure

The orderbook response is wrapped in an orderbook_fp object containing two arrays of bids — yes_dollars for YES positions and no_dollars for NO positions. Each bid is a two-element string array: [price_dollars, count_fp].
  • price_dollars: Price as a dollar string (e.g., "0.4200" = $0.42)
  • count_fp: Number of contracts as a fixed-point string (e.g., "13.00" = 13 contracts)
Both values are strings to support subpenny pricing and fractional contract sizes. See Fixed-Point Migration for details.

Example Response

{
  "orderbook_fp": {
    "yes_dollars": [
      ["0.0100", "200.00"],
      ["0.1500", "100.00"],
      ["0.2000", "50.00"],
      ["0.2500", "20.00"],
      ["0.3000", "11.00"],
      ["0.3100", "10.00"],
      ["0.3200", "10.00"],
      ["0.3300", "11.00"],
      ["0.3400", "9.00"],
      ["0.3500", "11.00"],
      ["0.4100", "10.00"],
      ["0.4200", "13.00"]
    ],
    "no_dollars": [
      ["0.0100", "100.00"],
      ["0.1600", "3.00"],
      ["0.2500", "50.00"],
      ["0.2800", "19.00"],
      ["0.3600", "5.00"],
      ["0.3700", "50.00"],
      ["0.3800", "300.00"],
      ["0.4400", "29.00"],
      ["0.4500", "20.00"],
      ["0.5600", "17.00"]
    ]
  }
}

Understanding the Arrays

  • First element: Price in dollars as a string (e.g., "0.4200")
  • Second element: Number of contracts as a fixed-point string (e.g., "13.00")
  • Arrays are sorted by price in ascending order
  • The highest bid (best bid) is the last element in each array

Why Only Bids?

Important: Kalshi’s orderbook only returns bids, not asks. This is because in binary prediction markets, there’s a reciprocal relationship between YES and NO positions.
In binary prediction markets, every position has a complementary opposite:
  • A YES BID at price X is equivalent to a NO ASK at price ($1.00 - X)
  • A NO BID at price Y is equivalent to a YES ASK at price ($1.00 - Y)

The Reciprocal Relationship

Since binary markets must sum to $1.00, these relationships always hold:
ActionEquivalent ToWhy
YES BID at $0.60NO ASK at $0.40Willing to pay 0.60forYES=Willingtoreceive0.60 for YES = Willing to receive 0.40 to take NO
NO BID at $0.30YES ASK at $0.70Willing to pay 0.30forNO=Willingtoreceive0.30 for NO = Willing to receive 0.70 to take YES
This reciprocal nature means that by showing only bids, the orderbook provides complete market information while avoiding redundancy.

Calculating Spreads

To find the bid-ask spread for a market:
  1. YES spread:
    • Best YES bid: Highest price in the yes_dollars array
    • Best YES ask: $1.00 - (Highest price in the no_dollars array)
    • Spread = Best YES ask - Best YES bid
  2. NO spread:
    • Best NO bid: Highest price in the no_dollars array
    • Best NO ask: $1.00 - (Highest price in the yes_dollars array)
    • Spread = Best NO ask - Best NO bid

Example Calculation

from decimal import Decimal

# Using the example orderbook above
best_yes_bid = Decimal("0.4200")  # Highest YES bid (last in array)
best_yes_ask = Decimal("1.00") - Decimal("0.5600")  # $1.00 - highest NO bid = $0.44

spread = best_yes_ask - best_yes_bid  # $0.44 - $0.42 = $0.02

# The spread is $0.02
# You can buy YES at $0.44 (implied ask) and sell at $0.42 (bid)

Working with Orderbook Data

Display Best Prices

from decimal import Decimal

def display_best_prices(orderbook_data):
    """Display the best bid prices and implied asks"""
    ob = orderbook_data['orderbook_fp']

    # Best bids (if any exist)
    if ob.get('yes_dollars'):
        best_yes_bid = ob['yes_dollars'][-1][0]  # Last element is highest
        print(f"Best YES Bid: ${best_yes_bid}")

    if ob.get('no_dollars'):
        best_no_bid = ob['no_dollars'][-1][0]  # Last element is highest
        best_yes_ask = Decimal("1.00") - Decimal(best_no_bid)
        print(f"Best YES Ask: ${best_yes_ask} (implied from NO bid)")

    print()

    if ob.get('no_dollars'):
        best_no_bid = ob['no_dollars'][-1][0]  # Last element is highest
        print(f"Best NO Bid: ${best_no_bid}")

    if ob.get('yes_dollars'):
        best_yes_bid = ob['yes_dollars'][-1][0]  # Last element is highest
        best_no_ask = Decimal("1.00") - Decimal(best_yes_bid)
        print(f"Best NO Ask: ${best_no_ask} (implied from YES bid)")

Calculate Market Depth

from decimal import Decimal

def calculate_depth(orderbook_data, depth_dollars="0.05"):
    """Calculate total volume within X dollars of best bid"""
    ob = orderbook_data['orderbook_fp']
    depth = Decimal(depth_dollars)

    yes_depth = Decimal("0")
    no_depth = Decimal("0")

    # YES side depth (iterate backwards from best bid)
    if ob.get('yes_dollars'):
        best_yes = Decimal(ob['yes_dollars'][-1][0])
        for price_str, count_str in reversed(ob['yes_dollars']):
            if best_yes - Decimal(price_str) <= depth:
                yes_depth += Decimal(count_str)
            else:
                break

    # NO side depth (iterate backwards from best bid)
    if ob.get('no_dollars'):
        best_no = Decimal(ob['no_dollars'][-1][0])
        for price_str, count_str in reversed(ob['no_dollars']):
            if best_no - Decimal(price_str) <= depth:
                no_depth += Decimal(count_str)
            else:
                break

    return {"yes_depth": str(yes_depth), "no_depth": str(no_depth)}

Next Steps