GET /v1/candles for one series, POST /v1/candles/batch for up to 200 at
once, and the binary format when JSON parsing is your
bottleneck.
Get candles
light bucket, cost
1 + 1 per 5,000 requested bars.
Request
Example
Response
volume is in
contracts.
Errors
Behavior
-
Half-open windows, bucket-aligned.
startfloors to a bucket boundary;endmid-bucket extends to include the current bucket, and is then clamped to at most one bucket past now. A request ending “now” includes the live, still-forming bar. A window that aligns to nothing is widened to exactly one bucket rather than rejected. -
Sparse series. Buckets with no trades are omitted. Gaps in the array
reflect periods with no trading. An empty
candlesarray is a valid200— the window is authoritatively empty, not an error. -
Range caps per timeframe.
startis clamped, never rejected:1-second data older than 24 hours is not retained — a 1s window entirely older than that returns{"candles": []}without querying anything. - Rollups. Only 1s and 1m bars are stored; 5m, 15m, 1h, 4h and 1d are aggregated from the 1m base on the fly. If that comes back empty, 1m, 1h, 4h and 1d retry against the 1s base (which is what gives brand-new contracts a series); 5m and 15m have no such retry.
-
Multi-outcome markets. Pass
outcometo select the token; each outcome has independent OHLC. -
Caching. A window still in progress is
no-store; a recent window is cached briefly; a fully historical one is cached for a day. The latter two carry a strong ETag and honorIf-None-Matchwith a304.
Send
kalshi, not the kalshi_offchain alias. The alias takes a legacy
path that derives outcome 1 as 100 − price instead of reading its own
series. On a multi-outcome Kalshi market that is the wrong number.Batch
light bucket, cost 1 up front plus 1 per 5,000 bars summed over every item.
Request
Body: JSON, at most 4 MiB.Example
Response
Results come back index-aligned with per-item errors — one bad request doesn’t fail the batch:Errors
A single item’s failure never fails the call. It comes back inside a200as{"index": n, "candles": [], "error": "…"}. Check every item’serrorfield; do not infer success from the HTTP status alone.
Batch overage is charged late. Because the true cost is only known once the body is parsed, the excess lands against your next request, not this one. See rate limits.Batch responses are always
Cache-Control: no-store and never carry an ETag.
Binary format
For chart-heavy or high-volume consumers, request the columnar binary encoding — roughly 8× smaller than JSON before compression:Accept header is equivalent:
Layout
Little-endian throughout:GET always has num_results = 1. A batch frame has one
section per request index, in input order.
The high bit of
count is an error flag, not part of the count. On a
batch, a failed item sets 0x80000000 on its count. Mask it off
(count & ~0x80000000) before using the value, and treat a set bit as
“this series errored” — not as “this series has no data”. A decoder that
skips this step will try to read roughly two billion candles. The error
message is only available from the JSON response.
