Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
starchild-ai-agent avatar

Kalshi Api

  • 2 installs
  • 21 repo stars
  • Updated August 3, 2026
  • starchild-ai-agent/official-skills

Query and trade the Kalshi prediction-market exchange via REST - events, markets, orderbooks, candlesticks, positions, balances, and RSA-signed order placement.

About

Wraps the Kalshi REST API for the CFTC-regulated prediction market, covering event/market discovery, orderbooks, candlesticks, portfolio, and RSA-PSS-signed order placement, cancellation, and RFQs. A developer uses it to read implied probabilities or place binary event-contract orders.

  • RSA-PSS signature auth; navigate Series -> Events -> Markets for sports/elections
  • All prices and balances in cents; demo environment available

Kalshi Api by the numbers

  • 2 all-time installs (skills.sh)
  • Ranked #870 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/starchild-ai-agent/official-skills --skill kalshi-api

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs2
repo stars21
Last updatedAugust 3, 2026
Repositorystarchild-ai-agent/official-skills

What it does

Query and trade the Kalshi prediction-market exchange via REST - events, markets, orderbooks, candlesticks, positions, balances, and RSA-signed order placement.

Files

SKILL.mdMarkdownGitHub ↗

Kalshi API

Trade and query the first CFTC-regulated prediction market exchange. Binary yes/no contracts on real-world events priced 1-99 cents.

Base URL: https://external-api.kalshi.com/trade-api/v2 Demo URL: https://external-api.demo.kalshi.co/trade-api/v2

Get your API key at https://kalshi.com/account/api-keys (Premier or Market Maker tier required)

Key Concepts

Hierarchy: Series > Events > Markets

  • Series — recurring event templates (e.g., "Monthly Jobs Report", "Weekly Jobless Claims")
  • Events — specific instances within a series (e.g., "May 2026 Jobs Report")
  • Markets — individual binary outcomes within an event (e.g., "Will jobs added be above 200k?")

Binary Contract Pricing

  • Prices are in cents (1-99), representing implied probability
  • A Yes contract at 65c = market implies 65% probability
  • Yes bid at price X is equivalent to No ask at (100 - X) — orderbooks show Yes bids and No bids only
  • Settlement: pays $1.00 (100 cents) if Yes, $0 if No
  • All monetary values (balance, prices, settlements) are in cents

Ticker Format

  • Series: KXBTC, KXJOBLESS, KXINX
  • Event: KXBTC-25MAY30 (series + date)
  • Market: KXBTC-25MAY30-T100000 (event + threshold/outcome)

Sports game tickers follow a different pattern: {SERIES}-{YYMONDD}{TEAM1}{TEAM2}-{TEAM}

  • Example: KXNHLGAME-25MAY12EDMORL-EDM (NHL, May 12 2025, Edmonton vs Orlando, Edmonton to win)
  • Each game event has two mutually exclusive YES markets — one per team

CRITICAL: Series-Based Market Navigation

DO NOT use `/markets?keyword=` for sports, elections, or any series-based category. It only surfaces multi-game parlay bundles, not actual game-level markets. Searching "NHL", "Fulham", "Premier League" etc. returns zero or irrelevant results.

Always navigate: Series → Events → Markets. Game-level markets live under the series/event hierarchy and must be accessed via /events?series_ticker=KXXX. Also, /markets/{ticker} may return 404 for game markets — bid/ask prices only appear in the event endpoint response (/events/{event_ticker} with with_nested_markets=true).

This pattern applies to all series-based categories, not just sports:

  • Sports games — NHL, NBA, NFL, MLB, EPL, etc.
  • Political races — individual candidate markets within multi-candidate events
  • Company-specific markets — earnings, CEO changes within a series
  • Recurring economic data — jobs reports, jobless claims, CPI, etc.

Known Sports Series Tickers:

SportSeries TickerExample
NHLKXNHLGAMEKXNHLGAME-25MAY12EDMORL
NBAKXNBAGAMEKXNBAGAME-25MAY12BOSLAL
NFLKXNFLGAMEKXNFLGAME-25SEP07KCDET
MLBKXMLBGAMEKXMLBGAME-25MAY12NYYLAD
EPL (Premier League)KXEPLGAMEKXEPLGAME-25MAY12FULARS

Correct workflow for sports:

# 1. List open games for a sport
curl -s "https://external-api.kalshi.com/trade-api/v2/events?series_ticker=KXNHLGAME&status=open&with_nested_markets=true"

# 2. Get specific game with live bid/ask
curl -s "https://external-api.kalshi.com/trade-api/v2/events/KXNHLGAME-25MAY12EDMORL?with_nested_markets=true"

# WRONG — do not do this:
# curl -s "https://external-api.kalshi.com/trade-api/v2/markets?keyword=NHL"  ← returns parlays, not games

How to Call

Kalshi uses RSA key-pair signature authentication. Three headers are required on authenticated requests:

HeaderValue
KALSHI-ACCESS-KEYAPI key ID ($KALSHI_ACCESS_KEY)
KALSHI-ACCESS-SIGNATURERSA-PSS signature of timestamp + method + path
KALSHI-ACCESS-TIMESTAMPUnix timestamp (ms)

Public GET endpoints (markets, events, orderbooks) can be called without auth headers.

# Public endpoint — no auth needed
curl -s "https://external-api.kalshi.com/trade-api/v2/markets?limit=10&status=open"

For authenticated endpoints, use direct RSA-PSS signing (not the kalshi_python SDK — it doesn't work):

import os, time, base64, requests
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
from dotenv import load_dotenv
load_dotenv('/data/workspace/.env')

BASE_URL = "https://api.elections.kalshi.com"

def kalshi_headers(method: str, path: str) -> dict:
    raw_pk = os.environ["KALSHI_PRIVATE_KEY"].strip().replace(' ', '')
    pem = f"-----BEGIN RSA PRIVATE KEY-----\n{raw_pk}\n-----END RSA PRIVATE KEY-----"
    private_key = serialization.load_pem_private_key(pem.encode(), password=None, backend=default_backend())
    ts = str(int(time.time() * 1000))
    msg = (ts + method + path).encode()  # path must NOT include query string
    sig = private_key.sign(msg, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.DIGEST_LENGTH), hashes.SHA256())
    return {
        "KALSHI-ACCESS-KEY": os.environ["KALSHI_ACCESS_KEY"].strip(),
        "KALSHI-ACCESS-TIMESTAMP": ts,
        "KALSHI-ACCESS-SIGNATURE": base64.b64encode(sig).decode(),
        "Content-Type": "application/json"
    }

path = "/trade-api/v2/portfolio/balance"
resp = requests.get(BASE_URL + path, headers=kalshi_headers("GET", path))
print(resp.json())  # {"balance": 5000, ...} — balance is in cents

Signing gotchas:

  • PSS not PKCS1v15 — PKCS1v15 padding returns 401. Must use PSS with SHA256.
  • Strip query strings before signing — signature is computed on the bare path only (e.g. /trade-api/v2/portfolio/balance), not /trade-api/v2/portfolio/balance?param=value. Including query string will 401.
  • Key env var formatKALSHI_PRIVATE_KEY should be stored as the raw base64 body with no PEM headers. The helper wraps it automatically.

API Spec

Full OpenAPI spec: https://docs.kalshi.com/openapi.yaml

Intent Routing

Map user intent to the right endpoint. All paths are relative to the base URL.

Events

MethodEndpointPrimary ParamsDescription
GET/eventslimit (1-200), cursor, status, series_ticker, with_nested_marketsList events (excludes multivariate)
GET/events/{event_ticker}event_ticker, with_nested_marketsGet specific event
GET/events/{event_ticker}/metadataevent_tickerEvent metadata only
GET/events/multivariatelimit, cursor, series_ticker, collection_ticker, with_nested_marketsList multivariate (combo) events

Markets

MethodEndpointPrimary ParamsDescription
GET/marketslimit, cursor, status, ticker, event_ticker, series_ticker, min_close_ts, max_close_tsList/filter markets
GET/markets/{ticker}tickerGet specific market details
GET/markets/{ticker}/orderbookticker, depthCurrent orderbook (yes bids + no bids)
GET/markets/orderbookstickers (array, max 100)Batch orderbooks
GET/markets/tradesticker, limit, cursor, min_ts, max_tsTrades across markets

Candlesticks

MethodEndpointPrimary ParamsDescription
GET/series/{series_ticker}/markets/{ticker}/candlesticksseries_ticker, ticker, start_ts, end_ts, period_intervalMarket-level candles
GET/series/{series_ticker}/events/{ticker}/candlesticksseries_ticker, ticker, start_ts, end_ts, period_intervalAggregated event-level candles
GET/markets/candlesticksmarket_tickers, start_ts, end_ts, period_intervalBatch candlesticks

period_interval values: 1 (1 min), 60 (1 hour), 1440 (1 day)

Forecast & Live Data

MethodEndpointPrimary ParamsDescription
GET/series/{series_ticker}/events/{ticker}/forecast_percentile_historypercentiles (0-10000), start_ts, end_ts, period_intervalHistorical forecast percentiles
GET/live_data/milestone/{milestone_id}milestone_id, include_player_statsLive milestone data
GET/live_data/batchmilestone_ids (array, max 100), include_player_statsBatch live data
GET/live_data/game-stats/{milestone_id}milestone_idPlay-by-play stats (football, basketball, soccer, hockey, baseball)

Series

MethodEndpointPrimary ParamsDescription
GET/series/{series_ticker}series_ticker, include_volumeGet series template
GET/seriescategory, tags, include_volume, min_updated_tsList all series

Exchange Info

MethodEndpointPrimary ParamsDescription
GET/exchange/statusCurrent exchange status
GET/exchange/scheduleOperating hours
GET/exchange/announcementsPlatform announcements
GET/exchange/user_data_timestampData sync timestamp
GET/series/fee_changesseries_ticker, show_historicalFee change history

Search & Discovery

MethodEndpointPrimary ParamsDescription
GET/search/tags_by_categoriesTags organized by series categories
GET/search/filters_by_sportFilters organized by sport

Orders (Authenticated)

MethodEndpointPrimary ParamsDescription
GET/portfolio/ordersticker, event_ticker, status, limit, cursor, subaccountList user orders
GET/portfolio/orders/{order_id}order_idGet specific order
POST/portfolio/ordersBody: ticker, action, side, type, count, yes_price/no_priceCreate order
DELETE/portfolio/orders/{order_id}order_id, subaccountCancel order
POST/portfolio/orders/{order_id}/amendorder_id, Body: new count/priceAmend order
POST/portfolio/orders/{order_id}/decreaseorder_id, Body: reduction amountDecrease order count
POST/portfolio/orders/batchedBody: array of ordersBatch create (max size scales with tier)
DELETE/portfolio/orders/batchedBody: array of order IDsBatch cancel
GET/portfolio/orders/queue_positionsmarket_tickers, event_ticker, subaccountAll resting order queue positions
GET/portfolio/orders/{order_id}/queue_positionorder_idSpecific order queue position

Orders V2 — Event-Market Orders (Authenticated, Fixed-Point)

MethodEndpointPrimary ParamsDescription
POST/portfolio/events/ordersBody: event ticker, market side, price (fixed-point), countCreate V2 order
POST/portfolio/events/orders/batchedBody: array of V2 ordersBatch create V2
DELETE/portfolio/events/orders/{order_id}order_id, subaccountCancel V2 order
DELETE/portfolio/events/orders/batchedBody: array of order IDsBatch cancel V2
POST/portfolio/events/orders/{order_id}/amendorder_id, Body: new count/priceAmend V2 order
POST/portfolio/events/orders/{order_id}/decreaseorder_id, Body: new remaining countDecrease V2 order

Order Groups (Authenticated)

MethodEndpointPrimary ParamsDescription
GET/portfolio/order_groupssubaccountList all order groups
POST/portfolio/order_groups/createBody: contracts limitCreate order group
GET/portfolio/order_groups/{id}id, subaccountGet specific group
DELETE/portfolio/order_groups/{id}id, subaccountDelete group & cancel all orders
PUT/portfolio/order_groups/{id}/limitid, Body: new limitUpdate contracts limit
PUT/portfolio/order_groups/{id}/triggerid, subaccountTrigger group (cancel all orders)
PUT/portfolio/order_groups/{id}/resetid, subaccountReset matched contracts counter

Portfolio (Authenticated)

MethodEndpointPrimary ParamsDescription
GET/portfolio/balancesubaccountBalance & portfolio value (in cents)
GET/portfolio/positionscursor, limit, count_filter, ticker, event_ticker, subaccountUser positions
GET/portfolio/fillsticker, order_id, min_ts, max_ts, limit, cursor, subaccountAll trade fills
GET/portfolio/settlementslimit, cursor, ticker, event_ticker, min_ts, max_ts, subaccountSettlement history
GET/portfolio/depositslimit, cursorDeposit history
GET/portfolio/withdrawalslimit, cursorWithdrawal history
GET/portfolio/summary/total_resting_order_valueTotal resting order value (FCM only)

Subaccounts (Authenticated — Institutions/Market Makers)

MethodEndpointPrimary ParamsDescription
POST/portfolio/subaccountsCreate subaccount (max 32)
POST/portfolio/subaccounts/transferBody: from, to, amountTransfer between subaccounts
GET/portfolio/subaccounts/balancesAll subaccount balances
GET/portfolio/subaccounts/transferslimit, cursorTransfer history
GET/portfolio/subaccounts/nettingNetting settings
PUT/portfolio/subaccounts/nettingBody: subaccount, enabledUpdate netting

RFQ — Request for Quote (Authenticated)

MethodEndpointPrimary ParamsDescription
GET/communications/rfqscursor, limit (1-100), event_ticker, market_ticker, status, user_filterList RFQs
POST/communications/rfqsBody: market ticker, side, countCreate RFQ (max 100 open)
GET/communications/rfqs/{rfq_id}rfq_idGet specific RFQ
DELETE/communications/rfqs/{rfq_id}rfq_idDelete RFQ
GET/communications/quotescursor, limit (1-500), event_ticker, market_ticker, status, rfq_idList quotes
POST/communications/quotesBody: RFQ ID, side, price, countCreate quote response
GET/communications/quotes/{quote_id}quote_idGet specific quote
DELETE/communications/quotes/{quote_id}quote_idDelete quote
PUT/communications/quotes/{quote_id}/acceptquote_idAccept quote
PUT/communications/quotes/{quote_id}/confirmquote_idConfirm quote (starts execution timer)
GET/communications/idGet user's communications ID

API Keys (Authenticated)

MethodEndpointPrimary ParamsDescription
GET/api_keysList all API keys
POST/api_keysBody: public key, nameCreate key with user RSA public key
POST/api_keys/generateBody: nameGenerate key pair automatically
DELETE/api_keys/{api_key}api_keyDelete API key

Account (Authenticated)

MethodEndpointPrimary ParamsDescription
GET/account/limitsAPI tier rate limits
GET/account/endpoint_costsNon-default endpoint token costs

Historical Data

MethodEndpointPrimary ParamsDescription
GET/historical/cutoff-timestampsBoundary between live and historical data
GET/historical/markets/{ticker}tickerSpecific historical market
GET/historical/marketsmutually exclusive filtersHistorical markets
GET/historical/markets/{ticker}/candlesticksticker, start_ts, end_ts, period_intervalArchived candlesticks
GET/historical/ordersfiltersArchived orders
GET/historical/fillsfiltersAll historical fills
GET/historical/tradesfiltersAll historical trades

Milestones & Structured Targets

MethodEndpointPrimary ParamsDescription
GET/milestones/{id}idSpecific milestone
GET/milestonesRFC3339 start date filtersList milestones
GET/structured-targets/{id}idSpecific structured target
GET/structured-targetspagination (max 2000)List targets

Multivariate Collections

MethodEndpointPrimary ParamsDescription
GET/multivariate/collectionsList multivariate event collections
GET/multivariate/collections/{ticker}tickerSpecific collection
POST/multivariate/collections/{ticker}/marketsticker, Body: market paramsCreate market in collection (5000/week limit)

Incentives

MethodEndpointPrimary ParamsDescription
GET/incentivesoptional filtersList incentive programs

Rate Limits

Token-based system that scales by API tier (Standard, Premier, Market Maker).

Endpoint TypeDefault Cost
Most endpoints10 tokens
GetOrder2 tokens
CancelOrder2 tokens
CreateQuote / DeleteQuote2 tokens
Batch operationsN x per-item cost

Check your limits with GET /account/limits. Batch operation max size scales with your tier's write budget.

Pagination

All list endpoints use cursor-based pagination. The response includes a cursor field — pass it back as a query param to get the next page.

ParamDescription
limitMax results per page (varies by endpoint, typically 1-200)
cursorOpaque cursor from previous response for next page

WebSocket Channels

WebSocket connection at wss://external-api.kalshi.com/trade-api/ws/v2 (auth required at handshake).

Public channels:

  • Market Ticker — price, volume, open interest updates
  • Public Trades — trade notifications
  • Market & Event Lifecycle — state changes, new markets/events
  • Multivariate Market & Event Lifecycle — MVE state changes

Authenticated channels:

  • User Orders — order created/updated notifications
  • User Fills — fill notifications
  • Market Positions — real-time position updates
  • Order Group Updates — lifecycle and limit notifications
  • Communications — RFQ and quote notifications
  • Orderbook Updates — incremental price level changes

Order Fields Reference

FieldValuesDescription
actionbuy, sellBuy or sell contracts
sideyes, noWhich outcome side
typelimit, marketOrder type
countintegerNumber of contracts
yes_price1-99 (cents)Price for yes side
no_price1-99 (cents)Price for no side
expiration_tsunix timestampOptional order expiration
sell_position_floorintegerMin position to keep when selling
buy_max_costinteger (cents)Max total cost for market buys

Safety Notes

  • All prices are in cents (1-99). A price of 65 means $0.65 per contract, NOT $65.
  • Balance is in cents. 10000 = $100.00.
  • POST /portfolio/orders submits real orders on production. Always verify ticker, side, and price before placing.
  • Batch operations execute atomically — all succeed or all fail.
  • Demo environment available at https://external-api.demo.kalshi.co/trade-api/v2 for risk-free testing.
  • Before placing orders, always confirm the market ticker, current price, and position size with the user.
  • Max 200,000 open orders per user.

Known Limitations

  • `/markets?keyword=` misses series-based markets — sports games, political races, and other series-based categories are invisible to keyword search. Always navigate via /events?series_ticker=. See "CRITICAL: Series-Based Market Navigation" above.
  • `/markets/{ticker}` returns 404 for game markets — bid/ask prices for sports and similar markets only appear in the event endpoint response. Use /events/{event_ticker}?with_nested_markets=true instead.
  • RSA signing required for authenticated endpoints — cannot use simple API key header like most REST APIs. Use the official Python/TypeScript SDK for signing.
  • Binary markets only — no multi-outcome contracts (except via multivariate events)
  • US-regulated — trading available to eligible US residents only
  • Market hours — markets have defined close times, check close_ts before trading
  • Historical data split — older data requires /historical/* endpoints, check /historical/cutoff-timestamps for the boundary
  • V1 vs V2 order endpoints — V2 uses fixed-point format, V1 uses cents. Both work but V2 is recommended for new integrations.
  • Subaccounts — only available to institutions and market makers (max 32 per user)
  • RFQ limit — max 100 open RFQs at a time
  • Multivariate market creation — max 5000 per week per collection

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.