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

Kalshi

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

Helps with ai & agent building tasks during AI-assisted development.

About

kalshi is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.

  • kalshi
  • AI & Agent Building
  • AI-coding skill

Kalshi by the numbers

  • 44 all-time installs (skills.sh)
  • +5 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #7,844 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/starchild-ai-agent/official-skills --skill kalshi

Add your badge

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

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

What it does

Helps with ai & agent building tasks during AI-assisted development.

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:
    import re
    raw = os.environ["KALSHI_PRIVATE_KEY"]
    # Decode literal escape sequences first (some input channels save '\n' as
    # two chars backslash+n instead of a real newline).
    raw = (raw.replace('\\r\\n', '\n').replace('\\n', '\n')
              .replace('\\r', '\n').replace('\\t', ' '))
    # Strip PEM markers if present, then strip ALL whitespace and rewrap.
    body = re.sub(r'-----BEGIN[^-]+-----', '', raw)
    body = re.sub(r'-----END[^-]+-----', '', body)
    body = re.sub(r'\s+', '', body)
    wrapped = '\n'.join(body[i:i+64] for i in range(0, len(body), 64))
    pem = f"-----BEGIN RSA PRIVATE KEY-----\n{wrapped}\n-----END RSA PRIVATE KEY-----\n"
    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 accepts any of: raw base64 body, full PEM with real newlines, or full PEM with newlines collapsed to whitespace (common when pasted through web forms). The helper normalises all formats automatically. For the most robust storage, save as a double-quoted multi-line PEM with \n escapes in .env so python-dotenv rehydrates it to a real PEM string.

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.