Available, development access
Markets
List and search, detail, and observation history, as built in the v1 source.
List and search marketsPermalink to this section
/v1/marketsReturn markets across covered platforms, filtered by title substring, platform and category.
Query parameters
| Name | Type | Description |
|---|---|---|
q | string | Title substring match. This is a plain substring filter on the market title, not semantic or natural-language matching, so a full question will usually return nothing. |
platform | string | Comma-separated list of platforms to include, for example kalshi,polymarket. |
category | string | Restrict results to a single category. |
status | string | open or all, defaulting to open. This is a legacy query name: open means in_current_feed is true, that is, the market was present in the most recent Riddle feed refresh. It is not a venue trading status. |
limit | integer | Results per page. Defaults to 50, maximum 200. |
cursor | string | Opaque cursor taken from page.next_cursor on the previous response. Do not parse it. |
sort | string | market_id is the only supported sort field. Defaults to market_id. |
order | string | asc or desc. Defaults to asc. |
curl -G "$RIDDLE_API_BASE/v1/markets" \
--data-urlencode "q=shutdown" \
--data-urlencode "platform=kalshi,polymarket" \
--data-urlencode "sort=market_id" \
--data-urlencode "order=asc" \
--data-urlencode "limit=50" \
-H "x-api-key: $RIDDLE_API_KEY"Response envelopePermalink to this section
Top-level fields
| Name | Type | Description |
|---|---|---|
data | array | The markets on this page, ordered by market_id in the requested direction. |
page.limit | integer | The page size applied to this response. |
page.next_cursor | string | null | Cursor for the next page, or null when there are no further results. Returned on list only, not on detail or history. |
request_id | string | Identifier for this response. Log it and quote it when reporting a problem. |
Market fieldsPermalink to this section
Fields on a market record
| Name | Type | Description |
|---|---|---|
market_id | string | Platform-native market identifier. Combined with platform, this addresses the market. |
platform | string | The platform the market is listed on. |
title | string | Market title as published by the platform. |
category | string | null | Category assigned to the market. |
event_ticker | string | null | Platform event grouping identifier, where the platform publishes one. |
market_ticker | string | null | Platform market ticker, where the platform publishes one. |
yes_probability | number | null | Indicative probability of the yes side as a fraction between 0 and 1. Null when unavailable. |
no_probability | number | null | Indicative probability of the no side as a fraction between 0 and 1. Null when unavailable. |
price_change_24h | number | null | Change in the yes probability over the change window, in the same 0 to 1 units. |
price_change_window_hours | number | null | Length in hours of the window that price_change_24h was measured over. |
volume_24h_usd | number | null | Volume over 24 hours in US dollars where the platform publishes a documented window figure. Polymarket only today; null on Kalshi and Limitless. Null means unavailable, not zero. |
volume_7d_usd | number | null | Volume over 7 days in US dollars where the platform publishes a documented window figure. Polymarket only today; null on Kalshi and Limitless. Null means unavailable, not zero. |
resolution_date | string | null | Resolution date as published by the platform, where one is available. |
venue_url | string | null | Link to the market on the platform, for verification by a human. Nullable, and emitted only when the URL validates against the expected HTTPS venue host. Kalshi grouped event links carry the exact outcome ticker when that ticker has been verified. |
in_current_feed | boolean | Whether the market was present in the most recent Riddle feed refresh. |
last_seen_in_feed_at | timestamp | null | When the market was last seen in the feed. |
observed_at | timestamp | null | Nullable. When Riddle materialised the values on this record, which is a Riddle-side time and not a venue timestamp. Read it with the probabilities, never without, and handle the null case. |
Retrieve one marketPermalink to this section
/v1/markets/{platform}/{market_id}Return one market record, addressed by its platform and platform-native market id.
Path parameters
| Name | Type | Description |
|---|---|---|
platformrequired | string | The platform the market is listed on. |
market_idrequired | string | The platform-native market id, as returned by list and search. URL-encode it. |
curl "$RIDDLE_API_BASE/v1/markets/kalshi/$MARKET_ID" \
-H "x-api-key: $RIDDLE_API_KEY"Response envelope
| Name | Type | Description |
|---|---|---|
data | object | A single market record, not an array. Detail returns no page object and no cursor. |
request_id | string | Identifier for this response. |
Observation historyPermalink to this section
/v1/markets/{platform}/{market_id}/historyReturn Riddle observations of a market over a requested window.
Query parameters
| Name | Type | Description |
|---|---|---|
days | integer | Length of the requested window in days. Defaults to 30, maximum 365. This is the window you are asking for, not a guarantee that observations exist across it. |
limit | integer | Maximum observations returned, up to 2000. |
Observation fields
| Name | Type | Description |
|---|---|---|
observed_at | timestamp | null | Nullable. When Riddle materialised the observation. It is a Riddle-side time, not a venue timestamp. |
yes_probability | number | Indicative probability of the yes side at that observation, as a fraction between 0 and 1. Never null on a returned point: observations that are invalid or of an unapproved type are omitted from the series rather than returned with a null value. |
indicative_basis | string | null | What the indicative probability was derived from for that observation. |
price_semantics_version | integer | null | Version of the price semantics in force when the observation was recorded, so older points are interpreted under the rules that produced them. |
Response envelope
| Name | Type | Description |
|---|---|---|
data | array | Observations in time order. History is not paginated and returns no page object or cursor. |
meta.platform | string | The platform the requested market is listed on. |
meta.market_id | string | The platform-native market id that was requested. |
meta.days | integer | The window applied to this response, in days. |
meta.window.requested_days | integer | The window length you asked for, echoed back after defaulting and clamping. |
meta.window.since | timestamp | ISO start of the requested window, computed at request time. It bounds what was looked at, not what exists. |
meta.window.until | timestamp | ISO end of the requested window, computed at request time. |
meta.returned_points | integer | Number of observations in the data array. |
meta.first_observation_at | timestamp | null | Observation time of the first returned point, or null when the series is empty. |
meta.last_observation_at | timestamp | null | Observation time of the last returned point, or null when the series is empty. |
meta.rows_examined | integer | How many stored rows were examined to build this response. |
meta.excluded_invalid_points | integer | Invalid or unapproved observations dropped from the examined slice. It counts only the rows examined for this request and says nothing about data outside the window or beyond the point limit. |
meta.limits.requested_limit | integer | The limit you asked for on this request. |
meta.limits.effective_point_limit | integer | The limit actually applied after clamping. |
meta.limits.max_points | integer | Hard ceiling on points in one history response. Currently 2000. |
meta.truncated | boolean | Whether the series was cut short by the point limit rather than by the window. |
meta.completeness | string | Always partial_by_construction. History is a Riddle observation series, so it is partial by design. |
meta.completeness_note | string | Prose restating that the series carries no claim of full venue retention or of complete coverage across the requested window. |
meta.series_type | string | Always riddle_observations, marking the series as Riddle observations rather than venue market data. |
request_id | string | Identifier for this response. |
{
"data": [
{
"observed_at": "2026-09-20T00:00:00Z",
"yes_probability": 0.28,
"indicative_basis": "midpoint",
"price_semantics_version": 2
},
{
"observed_at": "2026-09-21T00:00:00Z",
"yes_probability": 0.30,
"indicative_basis": "midpoint",
"price_semantics_version": 2
}
],
"meta": {
"platform": "kalshi",
"market_id": "$MARKET_ID",
"days": 30,
"window": {
"requested_days": 30,
"since": "2026-08-22T00:00:00Z",
"until": "2026-09-21T00:00:00Z"
},
"returned_points": 2,
"first_observation_at": "2026-09-20T00:00:00Z",
"last_observation_at": "2026-09-21T00:00:00Z",
"rows_examined": 2,
"excluded_invalid_points": 0,
"limits": {
"requested_limit": 500,
"effective_point_limit": 500,
"max_points": 2000
},
"truncated": false,
"completeness": "partial_by_construction",
"completeness_note": "Riddle observations only. No claim of full venue retention across the requested window.",
"series_type": "riddle_observations"
},
"request_id": "req_2c90fe11"
}Read the meta block before you analyse the series. The window fields tell you what was looked at, returned_points and the observation bounds tell you what came back, and truncated with excluded_invalid_points tells you whether the slice you are holding is the whole of what was examined. A window you asked for is not evidence that observations exist across it, so treat these series as partial by construction rather than as backtest coverage.
resp = session.get(
f"{BASE}/v1/markets/{platform}/{market_id}/history",
params={"days": 90, "limit": 2000},
timeout=20,
)
payload = resp.json()
meta = payload["meta"]
if meta["truncated"]:
print("series hit the point limit", meta["limits"]["effective_point_limit"])
if meta["excluded_invalid_points"]:
print("dropped from the examined slice:", meta["excluded_invalid_points"])
if meta["returned_points"] == 0:
raise SystemExit("no observations in this window")
print("requested", meta["window"]["since"], "to", meta["window"]["until"])
print("observed", meta["first_observation_at"], "to", meta["last_observation_at"])
# Analyse against the observed bounds, not the requested window.
points = payload["data"]Research snapshotPermalink to this section
/v1/markets/{platform}/{market_id}/researchReturn a stored research snapshot for one exactly addressed market.
Research uses the same x-api-key header and the same account quota as the rest of v1. It is addressed the same way as detail and history: the exact platform and the exact platform-native market id. There is no title matching and no inference that a market on one venue is the same contract as a similarly worded market on another.
- Market identity: the platform and platform-native market id the snapshot was built for.
- The resolution rule text Riddle has stored, with its provenance and a flag when the stored text was truncated.
- The resolution date associated with the market, where one is stored.
- Independently retrieved yes and no quotes: bid and ask with the size shown against them at retrieval.
- Timestamps for the source and for Riddle's receipt of it.
- The price semantics version and the model version that produced the snapshot.
- A staleness indication for the snapshot as a whole.
Where a quote, a size or a rule text is not available, the field is null or is reported as explicitly unavailable. Null means unavailable, never zero and never a quote of zero size.
Snapshots carry a five-minute freshness classification. That threshold is a research convention for deciding whether a stored observation is recent enough to reason about, not a latency guarantee and not a statement about tradability.
curl "$RIDDLE_API_BASE/v1/markets/kalshi/KXPRESPERSON-28-JVAN/research" \
-H "x-api-key: $RIDDLE_API_KEY"Contract research workspacePermalink to this section
An authenticated workspace at https://app.getriddle.ai/research sits alongside this endpoint. It compares two exactly addressed market identities, works through conditional binary settlement scenarios, and exports the working as JSON or CSV so a result can be reproduced.
- Payout is a fixed, disclosed assumption of $1 per winning unit. It is not configurable.
- Fees and slippage are the totals you enter yourself, per leg.
- USD and USDC are treated at parity; no conversion or basis is modelled.