Base URL
Authentication
Each endpoint below is annotatedx-kairos-auth: api-key. That means you send API-key credentials as three headers — X-Client-Id, X-Api-Key, and X-Api-Secret. A first-party session JWT or an admin secret is accepted instead; sending no credentials at all returns 401 Authentication required (JWT token / API key / admin secret). No scope is required on any search endpoint.
Session-JWT callers additionally pass an invite gate (403 Invite required if they haven’t); API-key and admin-secret callers bypass it.
Every endpoint on this page shares the same search rate-limit group — 60 requests/minute by default.
Examples below read credentials from KAIROS_CLIENT_ID, KAIROS_API_KEY, and KAIROS_API_SECRET in your shell.
Gotcha:
results[].price is not on one scale. Top-level rows are cents (0–100); sibling rows inside groups[].markets are 0–1. See Price scale before you render a price.Search markets
x-kairos-auth: api-key (any valid credentials — no scope required), 60 requests/minute.
Request
Response
results[] row above is abridged. Every row is the full search-result record and always carries these keys: market_id, provider_id, provider, event_id, event_name, name, symbol, category, status, expires_at, relevance_score, volume_24h, outcome_label, series_key, series_title, text_score, business_score, penalty, price, volume_1h, liquidity, image, icon, slug, token_id, condition_id, token_ids, outcomes.
From discovery to execution.
token_ids (aligned positionally with outcomes) and condition_id are the market’s on-chain identifiers. Feed an outcome token_id straight to POST /v1/synthetics legs and /v1/candles on md.kairos.trade — no second resolve call. token_id is the first outcome’s id for convenience. The token fields are empty for venues with no on-chain tokens (e.g. Kalshi).Search markets and events
type, merges both result sets, sorts by relevance_score descending, and truncates to limit. Same provider-visibility filtering and proxy short-circuit as /search/markets above.
x-kairos-auth: api-key, 60 requests/minute.
Request
Response
The sameresults + meta envelope as /search/markets, abridged here to the keys documented below:
Gotcha:
limit is applied twice. Each of the market and event searches is run with limit, the two sets are merged and re-sorted, and the merged list is then truncated back to limit.Simple search
x-kairos-auth: api-key, 60 requests/minute.
Request
Response
meta on this endpoint carries only query, returned, query_time_ms, and (conditionally) total — there is no requested.
Gotcha:
groups[].markets is not a subset of your matches. Groups are expanded server-side with sibling markets pulled from the discover cache, so a group can contain markets that did not themselves match q; those siblings carry relevance_score: 0. Filter on relevance_score if you only want true matches.results/singles; the same edge prices are kept inside groups.
The response may also carry classifiedGroups, absorbedMarketIds, and correlations — best-effort, fail-open enrichment (interactive threshold-ladder groupings and cross-venue similar-market suggestions) attached when available. They’re never guaranteed present; see API Reference for the full shape.
Resolve a market URL
x-kairos-auth: api-key, 60 requests/minute.
Request
Response
Identical envelope to/search/simple (results, groups, singles, meta), so the same rendering path works for both. Cross-venue correlations enrichment is attached; ladder classification (classifiedGroups / absorbedMarketIds) is not run on this endpoint.
Gotcha: Unrecognised input never errors. Any other host, or bare text, returns200with an empty envelope, so checkmeta.returnedrather than the status code:Fall back to a normal text search when you see it.
Autocomplete suggestions
x-kairos-auth: api-key, 60 requests/minute.
Request
Gotcha: This endpoint’s parameters are not the same as the others’. There is no
provider_id alias — only provider — and no include_expired at all. See Provider parameter alias and Default filters.Response
Default filters
Wheninclude_expired=false (the default on /search/markets, /search/markets-and-events, and /search/simple), expired/closed markets are excluded from results.
/search/suggest has no include_expired parameter: it always excludes markets past their trading-end timestamp and anything in closed/settled status. Markets in disputed status are exempt from the expiry cut-off, since a UMA dispute keeps them tradeable.
Provider parameter alias
/search/markets, /search/markets-and-events, and /search/simple accept the provider filter under either provider or the legacy provider_id query key (aliases of each other). If both are supplied they must match (case-insensitive) or the request fails with 400 provider and provider_id must match when both are provided. /search/suggest does not accept provider_id — only provider.
Visibility filtering
Results from/search/markets, /search/markets-and-events, /search/simple, and /search/resolve-url are post-filtered to drop any market whose provider is currently disabled. This can make meta.returned smaller than meta.requested even when the backend matched more rows. On /search/simple with include_total=true, meta.total falls back to the post-filter count when rows were dropped.
Price scale
results[].price is not normalized to a single scale and can be null.
- Top-level result rows carry the discover cache’s value, which is on the 0–100 (cents) scale; the same scale is used by the edge-price filter and by the ClickHouse backfill that fills a cache miss.
- Sibling markets added to
groups[].marketsby event expansion are divided by 100 when above 1, so those rows are on the 0–1 scale.
1 as cents and divide by 100 client-side. price is best-effort enrichment, not a quote — use the market/orderbook endpoints for tradeable prices.
Errors
Errors are returned as{ "detail": "<message>" }, with two exceptions noted in the table: 429 uses an error key, and 422 uses FastAPI’s standard validation body. The Code column holds the exact message string the service returns; — means the body has no fixed message.
Gotcha: The table above is not exhaustive. When search is served through the upstream proxy, the upstream’s status code and body are returned verbatim, so a proxied request can surface statuses not listed here. Handle unexpected statuses rather than switching on this list alone.
classifiedGroups, absorbedMarketIds, and correlations are fail-open and are simply absent when their backing lookup errors.
