Appearance
Kalshi Search API
Discover, cluster, and correlate markets across the live Kalshi prediction-market universe. These endpoints turn the open market set into a queryable database with three lenses — keyword search, embedding similarity, and time-series math — joinable in a single request.
Pair this surface with the Baskets API for server-side portfolio construction, with the Prediction Markets Agent for narrative analysis on individual events, and with the Events API for per-event snapshots and time series.
Like the Chat Completions and Responses endpoints, these are direct REST endpoints called relative to the Octagon API base URL:
text
https://api.octagonai.co/v1Conventions
| Convention | Details |
|---|---|
| Path style | Kebab-case (e.g. /behavioral-clusters, /markets-with-edge, /cluster-peers). |
| Field style | snake_case in query strings, request bodies, and response payloads. |
| Timestamps | RFC 3339 UTC (2026-08-19T00:00:00Z). |
| Prices | 0–1 fraction units (Kalshi cents are normalized server-side). |
| Probabilities | Octagon model/market probabilities (model_probability, market_probability) are percentages on a 0–100 scale; edge_pp is their difference in percentage points; confidence_score is 0–10. Note: the Baskets API sizing endpoints take probabilities as 0–1 fractions — divide by 100 when feeding values across. |
| Pagination | base64(JSON) cursor tokens. Pass next_cursor from a previous page back as cursor=.... |
Data refresh
Market, event, candle, cluster, and model-run data refresh nightly.
Search & Browse
GET /predictions/kalshi/markets
Structured + full-text search over the open Kalshi universe.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | No | Full-text query over title, subtitle, and ticker. Results are ranked by relevance when supplied. Minimum 3 characters. |
category | string | No | Matches event category or subcategory (exact). |
series_ticker | string | No | Filter by Kalshi series (exact match). |
series_prefix | string | No | LIKE '<prefix>%' filter on series_ticker — tree-style browsing (e.g. KXBTC matches KXBTCD, KXBTCY, KXBTCMAX100, …). |
event_ticker | string | No | Filter to markets in a single event. |
close_before | datetime | No | Only markets closing on or before this RFC 3339 timestamp. |
min_volume_24h | float | No | Floor on volume_24h. |
sort_by | string | No | volume_24h, close_time, or last_price. Defaults to rank when q is supplied, otherwise volume_24h. |
boost_category | string | No | Boosts relevance rank for markets in this category (case-insensitive), so a macro-flavored query like Brent crude prefers Financials over an MLB prop bet. Ignored when q is empty or sort_by is set. |
limit | integer | No | Page size. Default 50; min 1; max 200. |
cursor | string | No | Pagination cursor returned from a previous response. |
Response fields
Each market row in the data array includes:
| Field | Type | Description |
|---|---|---|
market_ticker | string | Ticker for this market. |
event_ticker | string | Parent event ticker. |
series_ticker | string | Parent series ticker. |
title | string | Market title. |
subtitle | string | null | Market subtitle. |
status | string | Market status (e.g. "open"). |
close_time | datetime | When the market closes. |
last_price | float | Last traded price (0–1). |
yes_bid / yes_ask | float | YES-side bid/ask (0–1). |
no_bid / no_ask | float | NO-side bid/ask (0–1). |
volume | number | Total volume. |
volume_24h | number | 24-hour volume. |
liquidity | number | Liquidity metric. |
open_interest | number | Open interest. |
category | string | Event category. |
event_name | string | Parent event name. |
The response envelope also includes next_cursor and has_more for pagination.
GET /predictions/kalshi/markets/similar
Semantic similarity search. Catches matches keyword search misses (e.g. "Will Bitcoin pierce six figures" ↔ "BTC above $100k") and is freely combinable with structured filters.
Query parameters — exactly one of anchor_ticker or q is required; supplying neither or both returns 400.
| Parameter | Type | Required | Description |
|---|---|---|---|
anchor_ticker | string | Cond. | Use the stored embedding of this market as the anchor. Zero added latency. Mutually exclusive with q. |
q | string | Cond. | Anchor by free-text query. Adds a short embedding step. Mutually exclusive with anchor_ticker. |
top_k | integer | No | Number of nearest neighbors. Default 25; min 1; max 100. |
category | string | No | Restrict to a category. |
min_volume_24h | float | No | Floor on volume_24h. |
close_before | datetime | No | Only markets closing before this RFC 3339 timestamp. |
Response fields
| Field | Type | Description |
|---|---|---|
anchor_ticker | string | null | The anchor ticker, when anchored by ticker. |
anchor_query | string | null | The free-text query, when anchored by q. |
data | array | Nearest neighbors: market_ticker, event_ticker, title, category, and distance. Lower distance = closer cosine similarity. |
GET /predictions/kalshi/series
Per-series rollup over the active universe — one row per series with active counts, volume, and dominant category.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
series_prefix | string | No | LIKE '<prefix>%' filter on series_ticker (e.g. KXBTC matches KXBTCD, KXBTCY, KXBTCMAX100, …). |
category | string | No | Filter against the dominant category for each series. |
min_volume_24h | float | No | Floor on total_volume_24h. |
sort_by | string | No | total_volume_24h (default), market_count, or active_count. |
limit | integer | No | Default 50; min 1; max 200. |
cursor | string | No | Pagination cursor. |
Response fields
Each series row in the data array includes:
| Field | Type | Description |
|---|---|---|
series_ticker | string | Series ticker. |
series_title | string | Series title. |
market_count | integer | Total markets in the series. |
active_count | integer | Active markets in the series. |
total_volume_24h | float | Combined 24-hour volume across the series. |
dominant_category | string | Most common category across the series. |
categories | array | All categories present in the series. |
last_seen_at | datetime | When the series was last observed in the sync. |
Performance
Expect 1–3 s on the first call; later calls are served from the nightly rollup.
GET /predictions/kalshi/series/{series_ticker}/events
List events inside a single series.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | No | Optional free-text filter within the series (title / subtitle). |
limit | integer | No | Default 50; min 1; max 200. |
cursor | string | No | Pagination cursor. |
Response fields
The envelope echoes series_ticker; each event row in the data array includes:
| Field | Type | Description |
|---|---|---|
event_ticker | string | Event ticker. |
title | string | Event title. |
category | string | Event category. |
close_time | datetime | When the event closes. |
market_count | integer | Total markets within the event. |
active_market_count | integer | Active markets within the event. |
total_volume_24h | float | Combined 24-hour volume. |
GET /predictions/kalshi/events/{event_ticker}/markets
Walk an event's child markets — e.g. every strike in an IPO date-ladder, every state in an election, every band in a recession-by-date event.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
min_volume_24h | float | No | Floor on volume_24h. |
limit | integer | No | Default 100; min 1; max 500. |
cursor | string | No | Pagination cursor. |
Response fields
The envelope echoes event_ticker; each market row in the data array uses the same market fields as GET /predictions/kalshi/markets.
Clusters
GET /predictions/kalshi/clusters
Browse the current run of thematic (embedding-based) clusters.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Default 200; min 1; max 500. |
sample_titles | integer | No | Number of sample titles per cluster. Default 4; min 0; max 20. |
label_contains | string | No | Case-insensitive substring filter on the cluster label. |
cursor | string | No | Pagination cursor. |
Response fields
Each cluster in the data array includes:
| Field | Type | Description |
|---|---|---|
cluster_id | integer | Cluster identifier under the current run. |
label | string | LLM-generated cluster label. |
description | string | LLM-generated cluster description. |
size | integer | Number of markets in the cluster. |
sample_titles | array | Representative market titles. |
created_at | datetime | When the cluster run was created. |
GET /predictions/kalshi/clusters/{cluster_id}/markets
Markets in one thematic cluster, ranked ascending by distance to the cluster centroid. Same response shape and cursor pagination as GET /predictions/kalshi/markets.
GET /predictions/kalshi/behavioral-clusters
Same shape as /predictions/kalshi/clusters but over the behavioral cluster run, which groups markets by their 30-day daily return patterns. Each row additionally exposes mean_daily_return and daily_volatility. Covers markets with ≥14 days of daily candles.
Query parameters — limit, sample_titles, and cursor, as on GET /predictions/kalshi/clusters. label_contains is not supported here.
GET /predictions/kalshi/behavioral-clusters/{cluster_id}/markets
Members of one behavioral cluster, same response shape as the thematic version.
GET /predictions/kalshi/markets/{market_ticker}/clusters
Return the thematic and behavioral cluster IDs a single market belongs to under the current runs. Useful for driving "no more than N legs from the same cluster" basket constraints client-side when not using POST /predictions/kalshi/baskets/build.
Response fields
| Field | Type | Description |
|---|---|---|
market_ticker | string | The requested market. |
thematic | object | null | Thematic assignment: cluster_id, label, description, size. |
behavioral | object | null | Behavioral assignment: cluster_id, label, size, mean_daily_return, daily_volatility. |
Either field can be null if the market isn't in a current-run assignment for that clustering kind.
GET /predictions/kalshi/markets/{market_ticker}/cluster-peers
Peer markets in the anchor's cluster, in one call.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | No | thematic (default) or behavioral. Selects the clustering to use. |
limit | integer | No | Number of peers, excluding the anchor. Default 50; min 1; max 200. |
Response fields
| Field | Type | Description |
|---|---|---|
market_ticker | string | The anchor market. |
kind | string | The clustering used (thematic or behavioral). |
cluster | object | The anchor's cluster: cluster_id, label, description, size. |
data | array | Peer markets: market_ticker, title, and distance to the anchor. |
Correlations & Model Edge
POST /predictions/kalshi/markets/correlations
Pairwise Pearson correlation matrix over close-price series for N markets. Side-aware: pass per-ticker sides to mix YES and NO legs (the sign is flipped for each NO leg, since corr(YES_A, NO_B) = -corr(YES_A, YES_B)).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
market_tickers | array of strings | Yes | 2–100 distinct market tickers. |
sides | array of strings | No | Per-ticker side — "yes" or "no", same length as market_tickers. Defaults to all yes. Filtered to the present tickers in the response. |
window_days | integer | No | Lookback window. Default 30; min 1; max 730. |
interval | string | No | "1h" or "1d". Auto-picked when omitted: 1d when window_days ≥ 90, else 1h. 1h reads hourly candles, 1d reads daily. |
include_cell_detail | boolean | No | When true, include a cells_detail array with overlap counts and a reason code per pair. Default false. |
Response fields
| Field | Type | Description |
|---|---|---|
tickers | array | The tickers included in the matrix, in matrix order. |
sides | array | The effective per-ticker sides. |
matrix | array of arrays | Pairwise Pearson correlations, already side-flipped server-side. Cells with fewer than 3 paired observations or constant series are null. |
ranked_pairs | array | The upper-triangle of the matrix sorted ascending by correlation — the most-uncorrelated pairs come first. Each entry: ticker_a, ticker_b, correlation. |
cells_detail | array | null | Present only with include_cell_detail: true. Each entry: ticker_a, ticker_b, correlation, overlap_count, and reason — "ok", "insufficient_overlap", or "zero_variance". |
window_days | integer | The lookback window used. |
interval | string | The candle interval used. |
missing | array | Markets without candle data in the window — dropped from the matrix. |
POST /predictions/kalshi/markets/edge
Per-ticker Octagon model-edge lookup. Accepts a mix of market and event tickers — markets are resolved to their parent event server-side. Returns stored model output from the most recent scoring run.
Typical use: pull model priors for a known list of tickers, then feed model_probability into POST /predictions/kalshi/baskets/size for Kelly sizing.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
tickers | array of strings | Yes | 1–100 tickers. Mix of market_ticker and event_ticker values is allowed. |
run_id | string (UUID) | No | Specific scoring run. Defaults to the most recent run. |
Response fields
The envelope includes the resolved run_id and its captured_at; each row in the data array includes:
| Field | Type | Description |
|---|---|---|
input_ticker | string | The ticker as supplied in the request. |
market_ticker | string | null | Resolved market ticker. null when the input was an event ticker (no per-market resolution applies). |
event_ticker | string | Resolved parent event ticker. |
title | string | Event title. |
series_category | string | Event category. |
model_probability | float | null | Octagon model probability (0–100 percentage scale). |
market_probability | float | null | Market-implied probability (0–100 percentage scale). |
edge_pp | float | null | Model edge in percentage points (model_probability - market_probability). |
expected_return | float | null | Expected return if the model is correct. |
confidence_score | float | Octagon's confidence in the model output (0–10 scale). |
total_volume | float | Total trading volume. |
total_open_interest | float | Total open interest. |
status | string | "scored" when the parent event was scored in this run, otherwise "unscored". |
captured_at | datetime | Snapshot capture time. |
GET /predictions/kalshi/markets-with-edge
Edge-vs-consensus ranking against the latest completed scoring run, the same data exposed by the Events API. One-call recipe for "10 most-traded politics markets ranked by edge vs consensus".
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
run_id | string (UUID) | No | Defaults to the most recent scoring run. |
category | string | No | Filter on series_category (case-insensitive). |
edge_pp_min | number | No | Lower bound on edge_pp (model probability minus market probability, in percentage points). |
edge_pp_max | number | No | Upper bound on edge_pp. |
expected_return_min | number | No | Floor on expected_return. |
total_volume_min | number | No | Floor on total_volume. |
model_probability_min | number | No | Floor on model_probability, on the same 0–100 scale (e.g. 60 for 60%). |
sort_by | string | No | One of edge_pp (default), expected_return, total_volume, model_probability. Descending. |
limit | integer | No | Default 50; min 1; max 200. |
cursor | string | No | Pagination cursor. |
Response fields
The envelope includes the resolved run_id, its captured_at, and the effective sort_by; each row in the data array includes:
| Field | Type | Description |
|---|---|---|
event_ticker | string | Event ticker. |
title | string | Event title. |
series_category | string | Event category. |
model_probability | float | Octagon model probability (0–100 percentage scale). |
market_probability | float | Market-implied probability (0–100 percentage scale). |
edge_pp | float | Model edge in percentage points (model_probability - market_probability). |
expected_return | float | Expected return if the model is correct. |
confidence_score | float | Octagon's confidence in the model output (0–10 scale). |
total_volume | float | Total trading volume. |
total_open_interest | float | Total open interest. |
The envelope also includes next_cursor and has_more for pagination. If filters reject every row but the run exists, captured_at still reflects the run's snapshot age.
Use Cases
- Keyword search — markets matching "BTC 100k" via
GET /predictions/kalshi/markets?q=.... - Semantic search — markets similar to a known ticker (
anchor_ticker) or a free-text description (q) viaGET /predictions/kalshi/markets/similar. - Browse the universe tree — series rollups (
GET /predictions/kalshi/series), events within a series (GET /predictions/kalshi/series/{series_ticker}/events), and every strike/state/band within an event (GET /predictions/kalshi/events/{event_ticker}/markets). - Browse themes — the clustered map of the universe (
GET /predictions/kalshi/clusters), drill into a theme (GET /predictions/kalshi/clusters/{cluster_id}/markets), or jump straight to a market's theme-mates (GET /predictions/kalshi/markets/{market_ticker}/cluster-peers). - Find uncorrelated bets —
POST /predictions/kalshi/markets/correlationsand read offranked_pairs(most-uncorrelated pairs first), mixing YES and NO legs viasides. - Rank by model edge —
GET /predictions/kalshi/markets-with-edgefor edge-vs-consensus rankings, orPOST /predictions/kalshi/markets/edgeto pull model priors for a known ticker list. - True top-N by volume —
GET /predictions/kalshi/markets?sort_by=volume_24hacross the entire universe, no client reranking.
Errors
| Status | Cause |
|---|---|
400 | Bad query parameters — unknown name, invalid kind / sort_by, anchor_ticker + q both supplied, and similar. |
401 | Missing or invalid Authorization header. |
422 | Request body validation failure — a missing or wrongly-typed field, or a value outside its documented range. |
502 | Upstream failure — typically an embedding or candle-data error. |
503 | Kalshi search is unavailable, or q is shorter than 3 characters. |
All errors return a JSON body with a detail field describing the cause.
Related docs
- Baskets API — server-side basket building, Kelly sizing, backtests, and validation over this universe.
- Prediction Markets Agent — the agent that produces narrative reports on individual events.
- Events API — latest snapshot of every analyzed event plus per-event history. Exposes the same scoring runs as
/predictions/kalshi/markets-with-edge.