This article is for educational purposes only and does not constitute financial advice. Trading involves risk of loss. Past performance does not guarantee future results. Consult a licensed financial advisor before making investment decisions.
AI & Automation9 min readUpdated July 3, 2026
TW

Kalshi API Guide: How to Build a Trading Bot

Build a trading bot on the Kalshi API. Authentication with RSA request signing, core REST and WebSocket endpoints, and a verified order-placement example in Python.

Want to put this into practice?

Tradewink uses AI to scan markets, generate signals with full analysis, and execute trades automatically through your broker.

Preview Signals

Kalshi API Overview (REST + WebSocket)

The Kalshi API lets you trade CFTC-regulated event contracts programmatically. Kalshi is a US prediction-market exchange. Every contract is binary: it settles at $1 if the event happens and $0 if it does not, priced in US dollars between one and 99 cents. The Kalshi API exposes market data, order placement, and portfolio management over REST, plus a WebSocket feed for real-time order-book and price updates.

There are two surfaces:

  • REST — request/response for market data, orders, and portfolio.
  • WebSocket — a persistent stream for live order-book deltas, trades, and fills.

The production REST base URL (recommended) is https://external-api.kalshi.com/trade-api/v2, and the WebSocket URL is wss://external-api-ws.kalshi.com/trade-api/ws/v2. Kalshi also runs a separate demo environment with its own credentials — build against demo first.

Reading public market data needs no authentication. Placing orders and reading your portfolio require a signed request. This is a build-it-yourself guide. If you would rather not maintain signing, sizing, and monitoring code, skip to the managed option at the end.

Getting API Credentials

You need a funded Kalshi account. Then:

  1. Log in and open Account Settings, then Profile.
  2. Find the API Keys section and generate a new key.
  3. Kalshi returns two things: an API key ID (a public identifier) and an RSA private key in PEM format.

Save the private key immediately. Kalshi shows it once and cannot retrieve it later. The key ID is safe to reference in code; the private key is a secret — keep it out of source control and inject it from an environment variable or secret manager. Demo credentials are separate from production: a demo key will not authenticate against production hosts, and the reverse is also true.

Authentication and Request Signing

Kalshi does not use a bearer token. Every authenticated request is signed with your RSA private key using RSA-PSS.

Each request sends three headers:

  • KALSHI-ACCESS-KEY — your API key ID.
  • KALSHI-ACCESS-TIMESTAMP — the current time in milliseconds.
  • KALSHI-ACCESS-SIGNATURE — an RSA-PSS signature, base64-encoded.

The signed message is the concatenation of the timestamp, the HTTP method, and the request path, in that order. Strip the query string before signing — sign only the path.

Here is a signing helper in Python using the cryptography library:

import base64
import time
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

with open("kalshi-private-key.pem", "rb") as f:
    private_key = serialization.load_pem_private_key(f.read(), password=None)

def signed_headers(api_key_id, method, path):
    timestamp = str(int(time.time() * 1000))
    message = (timestamp + method + path).encode("utf-8")
    signature = private_key.sign(
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.DIGEST_LENGTH,
        ),
        hashes.SHA256(),
    )
    return {
        "KALSHI-ACCESS-KEY": api_key_id,
        "KALSHI-ACCESS-TIMESTAMP": timestamp,
        "KALSHI-ACCESS-SIGNATURE": base64.b64encode(signature).decode("utf-8"),
        "Content-Type": "application/json",
    }

Sign the path you actually request. For GET /trade-api/v2/portfolio/balance, the signed path is /trade-api/v2/portfolio/balance. A timestamp that drifts too far from Kalshi's clock is rejected, so generate a fresh millisecond timestamp on each call.

Core Endpoints

The REST API groups endpoints by concern. The paths below are relative to the /trade-api/v2 base.

Market data (public, no signature required):

  • GET /markets — list markets. Filter by status (for example open) and event_ticker, and paginate with the returned cursor.
  • GET /events — list events, each of which groups related markets.
  • The market order-book endpoint returns resting bids and asks for a single market ticker.

Portfolio and orders (signed):

  • GET /portfolio/balance — your available balance.
  • GET /portfolio/orders — your resting and historical orders.
  • POST /portfolio/events/orders — place an order.

A quick unauthenticated market pull with curl:

curl "https://external-api.kalshi.com/trade-api/v2/markets?limit=5&status=open"

Reading your balance is a signed GET:

import requests

BASE = "https://external-api.kalshi.com/trade-api/v2"
path = "/trade-api/v2/portfolio/balance"
headers = signed_headers(API_KEY_ID, "GET", path)
r = requests.get(BASE + "/portfolio/balance", headers=headers)
print(r.json())

Note the split: the signed path includes /trade-api/v2, but you send the request to the full base URL.

Want Tradewink to trade these setups for you?

Tradewink's AI scans markets, generates signals with full analysis, and executes trades automatically through your broker — 24/7.

Preview Signals

Placing an Order (Verified Example)

Orders go to POST /portfolio/events/orders. Prices are quoted from the YES side as fixed-point dollars — "0.6000" means 60 cents. The side field is bid (buy) or ask (sell). Selling YES is economically the same as buying NO at one minus the price, but this endpoint quotes everything from the YES side.

Always send a unique client_order_id. Kalshi uses it to deduplicate, so a retried request never double-fills.

import requests
import uuid

BASE = "https://external-api.kalshi.com/trade-api/v2"
path = "/trade-api/v2/portfolio/events/orders"

order = {
    "ticker": "HIGHNY-24JAN01-T60",
    "side": "bid",
    "count": "1",
    "price": "0.6000",
    "time_in_force": "good_till_canceled",
    "client_order_id": str(uuid.uuid4()),
}

headers = signed_headers(API_KEY_ID, "POST", path)
r = requests.post(BASE + "/portfolio/events/orders", headers=headers, json=order)
print(r.status_code, r.json())

A successful create returns HTTP 201 and an order_id. Test this against the demo host first — a real order on production spends real money.

Rate Limits and Common Gotchas

Kalshi rate-limits with token buckets, not a flat requests-per-second cap. Each account has a tier (Basic, Advanced, and higher) that sets a per-second token budget, with separate Read and Write buckets. Every request costs a number of tokens, and heavier endpoints cost more. Buckets hold one to two seconds of budget, so you can burst briefly before throttling back to the refill rate. Check your live limits with GET /account/limits.

Common mistakes:

  • Signing the wrong string. Sign timestamp + method + path, path only, query string stripped. A mismatch returns 401.
  • Signing the full URL. Sign the path (/trade-api/v2/...), not https://....
  • Stale timestamps. Generate the millisecond timestamp fresh on every request.
  • Mixing environments. Demo keys do not work on production hosts.
  • Reusing client_order_id. Generate a new UUID for each intended order.
  • Assuming cents. The V2 order endpoint uses fixed-point dollar strings, not integer cents.

Build It Yourself vs a Managed Agent

The API is well-documented and the signing is only a few lines of code. But a production bot is more than a signed POST. You still need probability estimation, position sizing, risk limits, order monitoring, reconnection logic for the WebSocket, and calibration tracking to know whether your edge is real. That is a lot of infrastructure to maintain, and prediction-market trading carries a substantial risk of loss — you can lose your entire stake on any contract.

Tradewink Predictions is a managed alternative. It connects to Kalshi on your behalf and runs the full loop autonomously: multi-model probability estimation, Kelly-criterion sizing, Brier-score calibration, and configurable risk controls. Paper mode is the default, so you can watch it trade with simulated money before committing real capital, and monitor everything from Discord or the dashboard. You supply your Kalshi API credentials and risk limits; it handles auth, signing, sizing, and monitoring.

If you want to learn the mechanics, build your own — this guide is the starting point. If you want the outcome without the maintenance, the managed agent handles it. Either way, start on demo or in paper mode, size conservatively, and treat every contract as capital you can afford to lose.

For more, see how to build a Kalshi trading bot, Kalshi automated trading, Kalshi trading strategies, and the Tradewink Predictions guide.

Frequently Asked Questions

Is the Kalshi API free?

Yes. Kalshi does not charge a subscription fee for API access, and reading public market data requires no authentication. You do need a funded Kalshi account to place trades, and Kalshi's standard trading fees still apply per contract. Higher usage tiers with larger rate-limit budgets exist for high-volume traders.

Does Kalshi allow trading bots?

Yes. Kalshi officially provides a REST and WebSocket API and permits programmatic trading under its Developer Agreement. You generate an API key pair in your account settings and sign each request with your RSA private key. As with any automated trading, you are responsible for your own risk controls, and prediction-market trading carries a substantial risk of loss.

What language should I use for a Kalshi trading bot?

Any language that can make HTTPS requests and produce an RSA-PSS signature works. Python is the most common choice because libraries like requests or httpx plus cryptography cover everything you need, and no proprietary SDK is required. JavaScript, Go, and Rust are all viable too.

How does Kalshi API authentication work?

Kalshi uses API-key plus RSA request signing rather than a bearer token. Each request sends three headers: KALSHI-ACCESS-KEY (your key ID), KALSHI-ACCESS-TIMESTAMP (current time in milliseconds), and KALSHI-ACCESS-SIGNATURE (an RSA-PSS signature). The signed message is the timestamp, HTTP method, and request path concatenated together, with the query string stripped before signing.

Can I test a Kalshi bot without real money?

Yes. Kalshi provides a separate demo environment with its own base URLs and credentials, so you can develop and test order placement without risking real capital. Demo and production keys are not interchangeable. If you use a managed agent like Tradewink Predictions, its paper-trade mode simulates trades on live prices before you enable real orders.

Save a signal preview for later

Get a concise AI signal example in your inbox, then build a watchlist when you are ready. No spam, unsubscribe anytime.

Ready to trade smarter?

Get AI-powered trading signals delivered to you — with full analysis explaining every trade idea.

Try AI signals on your watchlist

Send yourself a signal preview, then add tickers to see ranked entries, exits, and risk notes in Tradewink.

Enter the email address where you want to receive a Tradewink AI signal preview.

TW

Tradewink builds autonomous AI trading systems that combine real-time market analysis, multi-broker execution, and self-improving machine learning models.

Tradewink is not a registered investment adviser, broker-dealer, or financial planner. All data, signals, and analytics on this page are for informational purposes only and do not constitute investment advice, financial advice, or a recommendation to buy or sell any security.

Past performance does not guarantee future results. Trading involves substantial risk of loss, including the possibility of losing more than your initial investment. You are solely responsible for your own trading decisions.