Kalshi API v2 Endpoint Reference
If you landed here after pasting external-api.kalshi.com/trade-api/v2/markets?status=open&limit=1000into Google because the response had no bids, you are in the right place. That host is the wrong one for market data. Below is the corrected endpoint map, the v2 field renames, and the auth details that are easy to get wrong.
The short answer
external-api.kalshi.com is the trading host, not the market data host.Its /markets endpoint mostly returns multivariate combo markets with zero bid depth. For market data with live bids, use api.elections.kalshi.com.
https://external-api.kalshi.com/trade-api/v2/markets?status=open&limit=1000https://api.elections.kalshi.com/trade-api/v2/events?status=open&with_nested_markets=true&limit=50Base URLs: which host for which job
| Purpose | Base URL | Auth required |
|---|---|---|
| Market data (events, markets, order books, series) | https://api.elections.kalshi.com/trade-api/v2 | No |
| Trading and portfolio (balance, orders, positions, settlements) | https://external-api.kalshi.com/trade-api/v2 | Yes (RSA-PSS) |
The elections hostname is a historical artifact. It serves every category: weather, crypto, economics, sports and politics. Do not read it as election-only.
Market discovery that actually works
The plain /markets listing is a poor discovery tool. Use the events endpoint with nested markets, which returns roughly 250 or more markets across 50 events, and in our testing about 85 percent of those carried real bid depth.
GET https://api.elections.kalshi.com/trade-api/v2/events
?status=open
&with_nested_markets=true
&limit=50If you must use /markets, add mve_filter=exclude to strip multivariate events. Expect a narrower and more obscure result set than the events route.
Field renames: v1 to v2
A large share of broken integrations are simply reading v1 field names that no longer exist. Prices moved to dollar strings, sizes moved to fixed point numbers.
| v1 field (gone) | v2 field | Type | Example |
|---|---|---|---|
yes_bid | yes_bid_dollars | string | "0.3300" |
yes_ask | yes_ask_dollars | string | "0.3400" |
no_bid | no_bid_dollars | string | "0.6600" |
no_ask | no_ask_dollars | string | "0.6700" |
last_price | last_price_dollars | string | "0.3300" |
volume | volume_fp | number | 785807.77 |
open_interest | open_interest_fp | number | 246292.19 |
— | yes_bid_size_fp | number | 2574.02 |
— | yes_ask_size_fp | number | 269.02 |
All _dollars values are strings. "0.3300" means 33 cents. Cast to float before doing arithmetic, or you will silently concatenate strings.
Authentication: RSA-PSS signing
The signature covers the full path including the /trade-api/v2 prefix. Signing only the short path is the most common auth failure.
# Sign the FULL path, including /trade-api/v2
path_clean = f"/trade-api/v2{endpoint.split('?')[0]}"
message = f"{timestamp_ms}{method}{path_clean}".encode()
signature = private_key.sign(message, PSS(...), SHA256())Required headers:
KALSHI-ACCESS-KEYKALSHI-ACCESS-TIMESTAMP(milliseconds)KALSHI-ACCESS-SIGNATURE
KALSHI-CLIENT-KEY-ID is the old header format and will not authenticate against v2.
Placing orders on v2
The v1 order path returns 410 deprecated_v1_order_endpoint. Use the events order path.
POST https://external-api.kalshi.com/trade-api/v2/portfolio/events/orders
{
"ticker": "KXBTC15M-26JUL231315-15",
"side": "bid",
"count": "1",
"price": "0.3300",
"time_in_force": "good_till_canceled",
"self_trade_prevention_type": "taker_at_cross",
"client_order_id": "edgeoutcome-1234567890"
}The response is flat, not wrapped in an order object. Read order_id,status, filled_count and remaining_count at the top level. Cancellation still works via DELETE /portfolio/orders/{order_id}.
GET /portfolio/balance returns {"balance": 1983} in cents, not dollars. There is no available_balance field.
Six mistakes that cost the most time
- Using
external-api.kalshi.comfor market data. It returns combo markets with no bids. - Signing the path without the
/trade-api/v2prefix. Every authenticated call fails. - Calling the retired
/portfolio/orderspath and getting a 410. - Reading
available_balanceinstead ofbalance, and treating cents as dollars. - Expecting integer cents when v2 returns dollar strings such as
"0.3300". - Unwrapping the order response under an
orderkey that does not exist.
Frequently asked questions
Why does external-api.kalshi.com/trade-api/v2/markets?status=open&limit=1000 return markets with no bids?
Because external-api.kalshi.com is the trading and portfolio host, not the market data host. Its /markets endpoint mostly returns multivariate combo markets (tickers like KXMVESPORTSMULTIGAMEEXTENDED) which typically show zero bid depth. For real market data with live bids, use https://api.elections.kalshi.com/trade-api/v2 instead. The name says elections for historical reasons, but it serves every market category.
What is the correct Kalshi API base URL for market data?
Use https://api.elections.kalshi.com/trade-api/v2 for market data such as events, markets, order books and series. Use https://external-api.kalshi.com/trade-api/v2 for authenticated trading calls such as balance, orders, positions and settlements. Mixing the two is the single most common integration mistake.
Is the Kalshi API free?
Yes. Public market data endpoints require no authentication and no API key. You only need an API key pair for authenticated endpoints such as placing orders, reading your balance, or listing your positions. Rate limits apply on both.
Why do yes_bid and no_bid return undefined in API v2?
Those field names belong to v1. In v2 the price fields are suffixed with _dollars and returned as strings, for example yes_bid_dollars with the value "0.3300" meaning 33 cents. Volume and open interest use the _fp suffix and are returned as numbers, for example volume_fp and open_interest_fp.
Why does POST /trade-api/v2/portfolio/orders return 410 deprecated_v1_order_endpoint?
The v1 order path is retired. Place orders against POST /trade-api/v2/portfolio/events/orders instead. The v2 request body sends count and price as strings, and the response is flat rather than nested under an order key.
How do I find open Kalshi markets reliably?
Call GET /events?status=open&with_nested_markets=true&limit=50 on the api.elections.kalshi.com host. It returns events with their markets already embedded, which avoids a second round trip and yields a much higher share of markets with real bid depth than the plain /markets endpoint.
Skip the integration work
The Kalshi AutoTrader Bot Kit ships a working v2 API client with RSA-PSS signing, correct field mapping, rate limit handling and five strategies. Every pitfall on this page is already handled in the code.
See the Bot Kit — 5 strategies, Python, Docker