Kalshi 是美国受 CFTC 监管的事件合约交易所,REST + WebSocket API 让你可以以编程方式查询数千个二元市场并下达限价单。本指南是对 2026 年 4 月底 API v2 的忠实讲解,基于 docs.kalshi.com 在 2026 Q1 一系列重大变更(仅 RSA-PSS 签名、token bucket 限流、定点小数定价、移除市价单)后的最新文档校准。如果你参考的 Kalshi 教程超过三个月没更新,请先扫一遍 §0 —— 有几处技术细节的变化会让旧代码静默失效。
0. 与多数旧 Kalshi 教程的差异
如果你手上的资料停留在 2024 或 2025 年初,先看一份精简的对比表,省一轮调试:
| 主题 | 旧版(≤ 2025 年初) | 当前版(2026-04) |
|---|---|---|
| 签名算法 | RSA-SHA256 / PKCS#1 v1.5 | RSA-PSS,MGF1(SHA256),salt = 摘要长度 |
| 限流模型 | RPS 配额,三层 | Token bucket,每请求默认 10 token,五层(Basic / Advanced / Premier / Paragon / Prime),Read 和 Write 是两个独立桶 |
| 429 处理 | 读 Retry-After header | 没有 Retry-After、没有 X-RateLimit-* —— 只能纯指数退避 |
| WebSocket 鉴权 | URL 查询字符串签名 | WebSocket 握手时通过 HTTP headers(与 REST 同一套) |
| WebSocket 心跳 | 客户端发起 | 服务端发起;服务端每 10 秒发 Ping(body=heartbeat),客户端回 Pong |
| 订单类型 | 有 market 和 limit | 只剩 limit 单 —— market 已于 2026-02-11 移除 |
| Orderbook 结构 | bids + asks | 只返回 bids;YES BID @ $0.60 ≡ NO ASK @ $0.40 |
| 价格 / 数量字段 | 整数美分(yes_bid: 42),整数 count(count: 10) | 字符串定点(yes_bid_dollars: "0.4200"、count_fp: "10.00");旧字段已在 2026 Q1 breaking 移除 |
| 公开域名 | 文档在 trading-api.readme.io,API 在 trading-api.kalshi.com | 文档迁至 docs.kalshi.com;生产 API 在 api.elections.kalshi.com(覆盖所有市场分类,不仅是选举) |
如果上面任意一行让你意外,那这份指南就是为你写的。
1. 30 秒:第一个 Kalshi API 响应
Kalshi 最快的一次调用根本不需要鉴权。GET /series 和 GET /markets 是公开端点 —— 不需要 Key,不需要签名,也不需要时钟同步的机器。挑一个 series ticker,拉它的元数据即可。从这一步出发再决定到底要不要做鉴权(很多分析场景压根用不上)。
curl -sS https://api.elections.kalshi.com/trade-api/v2/series/KXHIGHNY \
| jq '.series | {ticker, title, frequency, category}'
npx parlay-mcp@latest
>>> kalshi.get_series("KXHIGHNY")
同一个查询:直接 HTTP vs 走 Parlay 的预测市场 MCP。
两个开始之前要知道的事:
- 生产域名是
api.elections.kalshi.com,尽管路径里没有elections。这个域名服务所有分类(体育、天气、经济指标、政治),不仅是选举。demo-api.kalshi.co(注意是.co)是沙盒。 - 旧的
trading-api.readme.io和trading-api.kalshi.com已废弃。还指向那些 URL 的教程都停留在 2025-07-31 切换之前,处理这些时按"过时资料"对待。
接下来这份指南假设你访问生产域名,并且想做的事比读单个 series 更有意思。
2. 鉴权:RSA-PSS 签名分步走
鉴权是大多数 Kalshi 集成卡住一小时然后过几天再卡几分钟的地方。机制并不复杂,但每个细节都得对 —— padding 错了、时间戳单位错了、path 前缀错了,都是同一个不带说明的 401。建议把这五步全读完再写代码。
Step 1 — How the signature is constructed
Kalshi uses RSA-PSS signing, not HMAC and not RSA PKCS#1 v1.5. PSS adds randomized padding (the "salt"); two signatures of the same payload don't match. Use SHA-256 as the hash and as the MGF1 hash, with a salt length equal to the digest length (32 bytes).
The payload you sign is plain ASCII concatenation: timestamp + METHOD + path. Three rules about that payload:
timestampis Unix milliseconds, not seconds.METHODis the HTTP verb in upper case.pathmust include/trade-api/v2and must not include the query string.
Three headers go on every authenticated request:
KALSHI-ACCESS-KEY: <api_key_id>
KALSHI-ACCESS-TIMESTAMP: <unix_ms>
KALSHI-ACCESS-SIGNATURE: <base64-encoded RSA-PSS signature>
Step 2 — Working signing implementation
完整 Python / TypeScript / cURL 实现见英文版 kalshi-api.mdx §2 Step 2。三段代码均直接照搬 docs.kalshi.com 的官方示例。
Step 3 — Three real traps
Trap A — clock drift. NTP 不同步会被签名失败拒绝,但返回的是无差别的 401。
Trap B — PEM format. PKCS#8 (-----BEGIN PRIVATE KEY-----) vs PKCS#1 (-----BEGIN RSA PRIVATE KEY-----),两者主流加密库都能加载,但中间环节(Vault、K8s Secret、Slack 复制粘贴)容易丢换行。用 openssl rsa -in key.pem -check -noout 验证。
Trap C — production key management. Kalshi 不存私钥;丢了只能重生成。生产环境用 secrets manager,不要塞 ConfigMap 或 git 里的 .env。
Step 5 — Same call, three layers
# 见英文版 §2 Step 5:完整 RSA-PSS 签名 + 游标分页 ≈ 40 行
from kalshi_python import KalshiClient
client = KalshiClient(api_key_id="...", private_key_path="key.pem")
markets = list(client.markets.list(series_ticker="KXHIGHNY", status="open"))
from parlay import Parlay
p = Parlay()
markets = p.kalshi.list_markets(series="KXHIGHNY", status="open")
poly = p.polymarket.list_markets(query="weather", status="open")
≈ 40 行 → 8 行 → 2 行;只有第三个能跨平台。
3. Endpoints that actually matter
完整端点目录见英文版 §3 — 公开端点(/series、/events、/markets、/markets/{ticker}/orderbook)、authenticated portfolio 端点(/portfolio/balance 等)、以及 2026-02-19 起的历史数据分区(/historical/cutoff + /historical/{markets,fills,orders})。
Orderbook 关键事实:只返回 bids,没有 asks;最佳 bid 在数组末尾;价格和数量都是字符串定点。计算 YES ask 用对偶性:YES_ask = 1.00 − best_NO_bid。
4. Pagination, rate limits, and token costs
详见英文版 §4。要点:
- Cursor-based pagination,没有总数字段。
- Token bucket(2026-04-23 上线):每请求默认 10 token,5 tier,Read/Write 双桶独立。Basic = 200 read tokens/s(约 20 req/s),100 write tokens/s(约 10 req/s)。
- 429 没有 Retry-After,只能纯指数退避 + jitter。
- 批量端点不省 token(25 单 × 10 = 250 token),只省往返延迟。
# 见英文版 §4 Step 2:游标持久化 + 指数退避 + jitter + 429/503 区分
from parlay import Parlay
p = Parlay()
markets = p.kalshi.list_markets(series="KXHIGHNY", all_pages=True)
60 行生产级 paginator → 一行调用。
5. Real-time data via WebSocket
WebSocket URL:wss://api.elections.kalshi.com/trade-api/ws/v2。鉴权用握手时的 HTTP headers(不是 query string,签 path = /trade-api/ws/v2)。心跳是服务端发起,每 10 秒一次 Ping。完整代码示例见英文版 §5。
orderbook_delta 是私有频道(消息里带你自己的 client_order_id),但 channel-level 鉴权由 connection-level 鉴权覆盖。断线重连后用 2026-04-20 新加的 get_snapshot action 拿快照,不要尝试手动回放 delta。
# 见英文版 §5:完整 RSA-PSS WebSocket 握手 + 订阅 orderbook_delta ≈ 50 行
from parlay import Parlay
p = Parlay()
async for msg in p.kalshi.subscribe_orderbook(["KXHIGHNY-23DEC25-T75"]):
print(msg)
50 行原生客户端 → 3 行;带自动重连和补 snapshot。
6. Common errors and how to fix them
详细分组见英文版 §6(鉴权 / 限流 / 订单 / WebSocket / 数据迁移)。挑几个高频的:
7. Beyond Kalshi: querying across markets
如果你的应用只跟 Kalshi 打交道,官方 kalshi-python-sync / kalshi-typescript 已经够用,重新写一遍 RSA-PSS 没价值。
差异化的场景从"跨平台"开始:Kalshi 是 4 个二元事件价格主源之一,另外是 Polymarket(链上、EIP-712、Polygon)、Manifold(玩具币、REST + OAuth)、Opinion.trade(英国监管、REST + GraphQL)。每家自己一套鉴权、分页、限流和市场标识符 —— 写 4 套集成加一层抽象,认真做要两个工程季度。
Parlay 的 MCP server 把这一层做了:list_markets、get_orderbook、subscribe_orderbook 在四家上是同一组调用,市场标识规范化,鉴权统一。Kalshi 特有的 RSA-PSS、定点字段、bids-only orderbook 仍然能下钻访问,但你不用写 4 遍。
单平台用官方 SDK;跨平台才是 Parlay 的价值层。
8. 常见问题
9. 下一步
如果你专注 Kalshi,下一步建议看官方 rate-limits 页 和 fixed-point 迁移页。如果是多平台,看本站的 Polymarket 指南,CLOB API 的对应位置。