Skip to main content
Everything you need to identify a market before you chart or trade it: full metadata, batch lookup, venue-identifier resolution, catalog enumeration, settlement outcomes, the resolution lifecycle, and last traded prices. Most integrations start with Identifier resolution (turn a venue ticker or token id into a Kairos market_id), then Market metadata (get the outcomes and their token_ids). All endpoints on this page work anonymously on the free tier; add API-key headers for production budgets.

Market metadata

Full metadata for one market — title, status, outcomes with their token ids, condition id, tick size, fees, imagery. Auth: none required. light bucket, 1 unit.

Request

Example

Response

Errors

Batch metadata

The same metadata for up to 200 markets of one provider, in one round trip. Auth: none required. light bucket, flat 1 unit whatever the batch size.

Request

Example

Response

Ids that could not be found are listed in misses, not raised as errors. A batch where nothing resolved is still a 200. Check misses explicitly.

Errors

The response is Cache-Control: no-store, so it never 304s.

Tick size

Return the price grid a venue currently enforces for one market — the same grid your limit order is validated against. Tick sizes change while a market trades (Polymarket tightens the grid near 0.04 and 0.96), so query this before you place a limit order rather than caching a value from listing time.
The response describes the grid; it never snaps a price for you. It is served from the metadata cache the orderbook streamer keeps current with the venue’s live tick changes, so a Polymarket flip at the 0.96 / 0.04 extremes shows up here without waiting for a catalog cycle. Auth: none required. light bucket, 1 unit.

Parameters

string
required
The venue. Accepted values: polymarket, kalshi, predictfun.
string
required
Kalshi ticker, or Polymarket condition id / market id.
string
Polymarket CLOB token id. When given, the per-token grid is returned, which is where a live tick change lands first.

Example request

Example response

Kalshi’s tick is algorithmic — finer at the tails, coarser in the middle, with boundaries that differ per market. When the live band layout is available you get it, as above. When it is not, you get a single flat band at the finest step, with source: "streamer_projection" and synthetic: true: the minimum tick is correct (it is the orderbook streamer’s projection of the venue’s own finest step, tracked live), but the boundaries are not described. Render the ladder at min_tick in that case — a price the venue’s coarser middle band would reject is caught when the order is submitted.

Response fields

array
The price bands, lowest first. step applies to prices in [start, end); the last band includes end.
string
The finest step anywhere on the grid, decimal string.
string
Where the grid came from: kalshi_price_ranges, metadata_cache, or streamer_projection.
boolean
true when the band layout is not currently known and a single flat band at the finest step stands in for it. The minimum tick is still correct.
string | null
tapered for a grid that tightens at the extremes, null for a flat grid.
string
When the grid was read, RFC 3339.
start, end, step and min_tick are decimal strings. Parse them as decimals, not floats — a float 0.001 is not exactly 0.001, and an off-grid price is rejected by the venue.
Polymarket returns one flat [0, 1] band with source: "metadata_cache". predictfun has no per-market tick, so it returns { "provider": "predictfun", "contract_id": "…", "supported": false, "reason": "…" } rather than a fabricated grid.

Errors

Responses are cached for 5 seconds (public, max-age=5, ETagged).
This is the canonical tick-size endpoint — the Data API’s former GET /markets/tick-size route was retired in its favor (the request parameters and response body are the same, so moving an integration is a base-URL change).

Batch tick sizes

The same grids for up to 200 markets of one provider, in one round trip.

Request body

string
required
One provider for the whole call.
array
required
1–200 objects of { "contract_id": string, "asset_id"?: string }.

Example request

Example response

results is positional: results[i] answers items[i]. Each entry is a grid, the predictfun payload, or a per-item error carrying the code the single endpoint would have returned, so one bad id does not fail the batch. An item can also carry timeout (it was not started before the 15-second batch bound) or cancelled (the caller disconnected). The call costs a flat 1 unit from the light bucket, and responses are Cache-Control: no-store.

Identifier resolution

Resolves up to 200 venue-specific identifiers — tickers, market ids, condition-like ids, outcome token ids — to canonical Kairos market ids. Use this when you hold an id from a venue and need the id this API expects. The request may mix providers. Auth: none required. light bucket, 1 unit per submitted item.

Request

Scopes apply consistently to both on-chain and off-chain exchanges.
Id spaces. scope: market matches the venue’s market identity — a Kalshi ticker, a Polymarket Gamma numeric market id, or a 0x… condition id. scope: outcome matches an on-chain outcome/token id only; submitting a market id with scope: outcome returns found: false. Resolution returns the canonical market_id only — never outcome token ids. To get a market’s outcome tokens, read token_ids/outcomes from GET /search/markets or POST /markets/details, then pass them to POST /v1/synthetics and /v1/candles.

Example

Response

Results preserve request order. Unresolved identifiers return found: false and market_id: null, so callers never need to correlate separate hit and miss collections.

Errors

Notes

A rejected batch still costs its full size. The charge is 1 unit per submitted item, applied as soon as the array length is known — before any per-item validation. A 200-item batch rejected for one bad scope costs 200 units. Validate your scope values client-side.
When you don’t know which shape you hold, submit it under both scopes. Put the identifier in the same batch once with scope: "market" and once with scope: "outcome", then use whichever came back found. If the two scopes return different market ids, treat the input as ambiguous rather than picking one.
Unlike Batch metadata, which is a flat 1 unit, this endpoint scales with batch size. The response is not ETagged.

Enumeration

Pages through a provider’s active markets. Auth: none required. light bucket, 1 unit.

Request

Example

Follow next_cursor while has_more is true.

Errors

Settled outcomes

The settlement outcome for resolved markets, for any registered provider — not Polymarket only. Auth: none required. light bucket, 1 unit.

Request

Example

Response

The value is the YES payout fraction — 1.0 YES won, 0.0 NO won, 0.5 split. Scalar/range markets with no payout vector report the venue’s settled value instead.
Markets that haven’t resolved are omitted from the map. Treat a missing key as “not settled yet”, not as an error. A response of {"resolutions": {}} means none of your ids have settled.

Errors

Notes

The lookup is keyed differently per venue: Kalshi tickers identify the market directly, while CTF venues (polymarket, predictfun, opinion) are joined to their on-chain condition id first.

Resolution lifecycle

Where /v1/resolutions answers “did it settle, and how”, these two answer “where is it in the process”. Both take the same {provider}/{market_id} path as Market metadata. Auth: none required. light bucket, 1 unit each.

Current state

Status, the UMA-style proposal/dispute metadata, and — once resolved — the payout numerators.
Proposals move through a roughly two-hour challenge window, so this response is only cached for a short period — long enough to absorb polling, short enough not to report a disputed market as still merely proposed.

Event timeline

The append-only timeline behind that state, oldest first, capped at 200 events.
market_key is the key the events were actually queried under — the ticker for Kalshi, the resolved condition id for CTF venues.
Optional fields are omitted entirely when unset, not sent as null. Use key presence, not a null check, when decoding events.

Errors

Last traded prices (marks)

The last traded price per (contract_id, token_id) pair. A mark moves only when a new trade executes. Auth: none required. heavy bucket, 1 unit per started 100 pairs (so 2 at the 200-pair cap).

Request

Example

Response

Prices are on the 0–100 scale.
Pairs that have never traded are omitted. Treat absence as “no trade history”, not as an error.

Errors

Notes

Do not poll this endpoint for real-time prices. It draws on the heavy bucket and is explicitly uncacheable (Cache-Control: no-store, no ETag, so never a 304). For live updates use the WebSocket API.

Caching summary

Which responses on this page are ETagged and can return 304 on If-None-Match: Shared 401 / 403 / 429 behavior is on the Authentication page.