mcpField
K
kalshi-api

kalshi-api

github.com/agiprolabsOther456307 installsUpdated 8w ago
Repository

About kalshi-api

kalshi-api is an integration guide for the Kalshi trading API, for developers who need to retrieve market data and manage orders through REST or WebSocket. It emphasizes RSA-PSS request authentication, interpretation of YES/NO order books, and the dollar-string order schema.

Key features

  1. 01Explains RSA-PSS request signing
  2. 02Covers account, order, and market endpoints
  3. 03Clarifies YES/NO bids and quote calculations
  4. 04Details required order fields and formats
  5. 05Includes order-book helper scripts

Use cases

  1. 01Implement authentication for Kalshi API requests
  2. 02Retrieve market data and historical candlesticks
  3. 03Place, amend, cancel, or batch orders

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/agiprolabs/claude-trading-skills --skill kalshi-api

After installing, type/kalshi-apiin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
auth-and-orders.md
endpoints-and-marketdata.md
websocket.md
scripts/
kalshi_orderbook.py

Details

From the project README

Kalshi API

CFTC-regulated US event exchange. USD-denominated binary contracts settle at $1.00 (YES wins) or $0.00 (NO wins). REST + WebSocket, RSA-PSS authentication on every request.

For contract semantics and settlement rules, see the kalshi-weather-markets and kalshi-crypto-index-markets skills. For strategy, sizing, and backtesting, see prediction-market-strategy.


VERIFY BEFORE CODING. The Kalshi API has broken backward compatibility before: the host changed (old trading-api.kalshi.com → dead), and the order schema changed (integer cents → dollar strings). Always smoke-test signing and order bodies against a live response before shipping.

Canonical sources:


Overview

  • Base URL: https://api.elections.kalshi.com/trade-api/v2
  • Auth: RSA-PSS on every request — there are no public/unauthenticated endpoints
  • No demo parity: the demo environment (demo-api.kalshi.co) has a near-empty book; use production even for read-only pulls
  • Contracts: $0.01–$0.99 per contract; pay price if YES wins, lose price if NO wins; max payout = $1.00

Quick Start

1. Credentials
KALSHI_KEY_ID=<your-key-uuid>
KALSHI_PRIVATE_KEY_PATH=~/.kalshi/private.pem

Generate the key in the Kalshi dashboard. Store secrets in environment variables or a secrets manager — never in code.

2. Install
pip install httpx cryptography
3. Host + auth (the part everyone gets wrong)

The host and signature format are where implementations break. Three common failures:

  1. Using the old trading-api.kalshi.com host → 401
  2. Including the query string in the signed path → 401
  3. Signing with seconds instead of milliseconds → 401
import os, time, base64, httpx
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

BASE = "https://api.elections.kalshi.com/trade-api/v2"
KEY_ID = os.environ["KALSHI_KEY_ID"]
with open(os.environ["KALSHI_PRIVATE_KEY_PATH"], "rb") as f:
    PRIV = serialization.load_pem_private_key(f.read(), password=None)

def _headers(method: str, path: str) -> dict:
    """path must include /trade-api/v2 prefix and exclude query string."""
    ts = str(int(time.time() * 1000))             # milliseconds
    msg = f"{ts}{method}{path}".encode()
    sig = PRIV.sign(
        msg,
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                    salt_length=padding.PSS.DIGEST_LENGTH),
        hashes.SHA256(),
    )
    return {
        "KALSHI-ACCESS-KEY": KEY_ID,
        "KALSHI-ACCESS-TIMESTAMP": ts,
        "KALSHI-ACCESS-SIGNATURE": base64.b64encode(sig).decode(),
    }

def get(path: str, params=None):
    # Sign the path only — query goes into params, not the signature
    r = httpx.get(BASE + path, params=params,
                  headers=_headers("GET", "/trade-api/v2" + path))
    r.raise_for_status()
    return r.json()

def post(path: str, body: dict):
    r = httpx.post(BASE + path, json=body,
                   headers=_headers("POST", "/trade-api/v2" + path))
    r.raise_for_status()
    return r.json()

# Example: open markets in a series
markets = get("/markets", params={"series_ticker": "KXHIGHNY", "status": "open"})

Signature spec: RSA-PSS, MGF1 over SHA-256, salt length = PSS.DIGEST_LENGTH. String to sign: {timestamp_ms}{METHOD}{path} where path includes /trade-api/v2 and excludes the query string.

Headers: KALSHI-ACCESS-KEY (UUID), KALSHI-ACCESS-TIMESTAMP (ms), KALSHI-ACCESS-SIGNATURE (base64).

4. Candlestick history
# Returns OHLC for yes_bid / yes_ask + volume + open_interest
# Values are dollar strings: {"close": "0.42"}
candles = get(
    f"/series/KXHIGHNY/markets/{ticker}/candlesticks",
    params={"start_ts": start_epoch, "end_ts": end_epoch, "period_interval": 60},
)

period_interval is in minutes: 1, 60, or 1440.


YES/NO Order-Book Convention

On Kalshi, yes and no are both resting BID ladders — there is no separate ask book. To take the other side you lift the opposing bid:

no_ask  = 1 − best_yes_bid    # cost to buy NO right now (lift YES bids)
yes_ask = 1 − best_no_bid     # cost to buy YES right now (lift NO bids)
P(YES) mid = (best_yes_bid + (1 − best_no_bid)) / 2

Getting this backwards silently inverts every signal. Use the helpers in scripts/kalshi_orderbook.py.

Orderbook response comes in two variants depending on API tier — normalize before using:

{"orderbook": {"yes": [[price, size], ...], "no": [[price, size], ...]}}

If a price value is > 1.0, it is integer cents — divide by 100.


Order Schema

POST /trade-api/v2/portfolio/orders uses fixed-point dollar STRINGS, not integers. The old schema (integer cents, count, yes_price) returns 400 invalid_parameters.

{
  "ticker": "KXHIGHNY-26JUN02-B75.5",
  "action": "buy",
  "side": "yes",
  "count_fp": "1.00",
  "yes_price_dollars": "0.01",
  "client_order_id": "my-strategy-001",
  "time_in_force": "good_till_canceled"
}

Critical field rules — each violation returns 400:

FieldRuleCommon mistake that 400s
count_fpfixed-point string "1.00"integer count: 1
{side}_price_dollarsdollar string "0.01"integer cents yes_price: 1
time_in_forcerequired: good_till_canceled | immediate_or_cancel | fill_or_killomitted
client_order_id[A-Za-z0-9-] only. or : in the string — bracket tickers contain ., so never copy the ticker directly
typedo not send"type": "limit"

For the full order lifecycle (amend, decrease, cancel, batch) and strike_type gotchas, see references/auth-and-orders.md.


Endpoint Summary

CategoryEndpointNotes
BalanceGET /portfolio/balance—
PositionsGET /portfolio/positions—
OrdersGET /portfolio/orders?status=resting|canceled|executed
Place orderPOST /portfolio/ordersdollar-string schema above
CancelDELETE /portfolio/orders/{id}returns {"order": {...status: "canceled"}}
AmendPOST /portfolio/orders/{id}/amendticker required in body
DecreasePOST /portfolio/orders/{id}/decrease{"reduce_by_fp": "1.00"}
BatchPOST /portfolio/orders/batched{"orders": [...]}
FillsGET /portfolio/fills?limit=N
SettlementsGET /portfolio/settlements?limit=N
MarketsGET /markets?series_ticker=&status=open&limit=500
OrderbookGET /markets/{ticker}/orderbook?depth=N
CandlesticksGET /series/{series}/markets/{ticker}/candlesticks?start_ts=&end_ts=&period_interval=60
TradesGET /markets/tradesrecent trade prints

Full endpoint surface, market metadata fields, and rate limits: references/endpoints-and-marketdata.md. WebSocket discovery pipeline: references/websocket.md.


Files

References
  • references/auth-and-orders.md — RSA-PSS spec, dollar-string order schema, time_in_force, client_order_id sanitization, amend/decrease/cancel lifecycle, strike_type gotcha, fees
  • references/endpoints-and-marketdata.md — Full endpoint table, orderbook variants, market metadata fields (result, open_time, close_time, series_ticker), candlesticks, rate limits
  • references/websocket.md — WS host, discovery pipeline, channels, signing the WS upgrade
Scripts
  • scripts/kalshi_orderbook.py — YES/NO bid-ladder helpers (no_ask, yes_ask, p_yes_mid, overround, kalshi_fee). Pure stdlib, no keys, runs offline.

Comments

Markdown supported