Get a single OHLCV candle series
Returns OHLCV candles for one (provider, contract_id, timeframe_seconds, outcome) series over [start, end).
Alignment. start is floored and end is ceiled to the nearest timeframe_seconds bucket boundary; end is additionally clamped to now + 1 bucket. Degenerate windows (end <= start after alignment) are extended by exactly one bucket. The requested window is silently clamped (not rejected) to the per-timeframe retention ceiling: 1s=1 day, 1m=30 days, 5m=90 days, 15m=180 days, 1h=365 days, 4h=365 days, 1d=730 days. For timeframe_seconds=1, if the aligned window falls entirely before now - 24h (the retention window for 1-second candles) the endpoint short-circuits to {"candles":[]} without querying upstream.
Timeframes. Only 1s and 1m candles are stored directly; every other timeframe (5m, 15m, 1h, 4h, 1d) is rolled up server-side from the 1m base. When the first read comes back empty, 60, 3600, 14400 and 86400 retry as a rollup of the 1s base (brand-new contracts whose only history is 1-second candles); 300 and 900 have no such fallback and return an empty series instead. Prices are on a 0-100 scale. For providers that are not natively per-token (i.e. not kalshi, polymarket, dome, opinion, predictfun, hyperliquid), requesting outcome > 0 inverts prices (price = 100 - price, high/low swapped) rather than resolving a distinct token. The check is on the provider string exactly as submitted, so the kalshi_offchain alias takes the inverting path even though kalshi does not — send kalshi for Kalshi candles.
Rate limiting / cost. This route is in the light rate-limit bucket. Larger requests — wider windows, finer timeframes — consume more of your quota; a typical chart paint (a few hundred bars) costs 1 unit.
Caching. Cache-Control depends on how the aligned window relates to now: a window still in progress (end >= now) is no-store; a window entirely in the recent past is cached briefly; anything older is cached for longer. Cacheable responses carry a strong ETag and honor If-None-Match, returning 304 on a match. Responses ≥1KB are gzip-encoded when the client sends Accept-Encoding: gzip.
Binary format. Pass ?fmt=binary or send Accept: application/x-kairos-candles to receive a compact columnar binary frame instead of JSON. Layout: u8 magic=0xCA, u8 version=1, u16 num_results, then per result u32 index, u32 count, followed by the columnar arrays u32[count] t (epoch seconds), u16[count] o,h,l,c (price ×100), i64[count] vol (×100). A single-series GET response always has num_results=1.
Request
- Base URLhttps://md.kairos.trade
- Auth
- none
- or API key in header X-Client-Id and API key in header X-Api-Key and API key in header X-Api-Secret
Query parameters
Market-data provider. Case-insensitive. dome and kalshi_offchain are aliases resolved to polymarket and kalshi respectively. opinion is a disabled provider kept resolvable for historic reads only. Unknown values return 400 invalid_request.
Provider-native contract/market/ticker id. For polymarket this may be either a numeric market id or a 0x-prefixed condition_id (both resolve to the same series). Empty or >128 chars is rejected with 400 invalid_request.
Candle bucket width in seconds. Any value outside this set is rejected with 400 invalid_request. Only 1 and 60 are stored directly; 300/900/3600/14400/86400 are always served as rollups.
Window start, inclusive (before alignment). Accepts RFC 3339 (2024-01-15T10:00:00Z or with a numeric offset), a bare datetime (2024-01-15T10:00:00, treated as UTC), or a bare date (2024-01-15, treated as UTC midnight). Unparseable values return 400 invalid_request.
Window end, exclusive (before alignment). Same accepted formats as start. Must be strictly after start or the request is rejected with 400 invalid_request: end must be after start.
Zero-based outcome index (e.g. 0=Yes, 1=No for a binary market, or an index into a multi-outcome market's token list). Defaults to 0. Negative or non-integer values return 400 invalid_request.
Set to binary to receive the columnar binary frame instead of JSON. Equivalent to sending Accept: application/x-kairos-candles.
Response
Candle series for the requested window. {"candles":[]} (empty array, no-store) is a valid 200 response, not an error — it means the window is authoritatively empty.
Example response
{
"candles": [
{
"contract_id": "21742633143463906290569050155826241533067272736897614950488156847949938836455",
"timeframe_seconds": 3600,
"bucket_start": "2026-07-15T00:00:00+00:00",
"open": 61.5,
"high": 63,
"low": 60.8,
"close": 62.4,
"volume": 184230,
"token_id": "704721957297303853272349184"
}
]
}Changes
No recorded changes to this operation across all 1 revision of this API.