Available, development access

Markets

List and search, detail, and observation history, as built in the v1 source.

List and search marketsPermalink to this section

GET/v1/markets

Return markets across covered platforms, filtered by title substring, platform and category.

Query parameters

Query parameters
NameTypeDescription
qstringTitle 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.
platformstringComma-separated list of platforms to include, for example kalshi,polymarket.
categorystringRestrict results to a single category.
statusstringopen 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.
limitintegerResults per page. Defaults to 50, maximum 200.
cursorstringOpaque cursor taken from page.next_cursor on the previous response. Do not parse it.
sortstringmarket_id is the only supported sort field. Defaults to market_id.
orderstringasc 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"
Search titles on two platforms

Response envelopePermalink to this section

Top-level fields

Top-level fields
NameTypeDescription
dataarrayThe markets on this page, ordered by market_id in the requested direction.
page.limitintegerThe page size applied to this response.
page.next_cursorstring | nullCursor for the next page, or null when there are no further results. Returned on list only, not on detail or history.
request_idstringIdentifier for this response. Log it and quote it when reporting a problem.

Market fieldsPermalink to this section

Fields on a market record

Fields on a market record
NameTypeDescription
market_idstringPlatform-native market identifier. Combined with platform, this addresses the market.
platformstringThe platform the market is listed on.
titlestringMarket title as published by the platform.
categorystring | nullCategory assigned to the market.
event_tickerstring | nullPlatform event grouping identifier, where the platform publishes one.
market_tickerstring | nullPlatform market ticker, where the platform publishes one.
yes_probabilitynumber | nullIndicative probability of the yes side as a fraction between 0 and 1. Null when unavailable.
no_probabilitynumber | nullIndicative probability of the no side as a fraction between 0 and 1. Null when unavailable.
price_change_24hnumber | nullChange in the yes probability over the change window, in the same 0 to 1 units.
price_change_window_hoursnumber | nullLength in hours of the window that price_change_24h was measured over.
volume_24h_usdnumber | nullVolume 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_usdnumber | nullVolume 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_datestring | nullResolution date as published by the platform, where one is available.
venue_urlstring | nullLink 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_feedbooleanWhether the market was present in the most recent Riddle feed refresh.
last_seen_in_feed_attimestamp | nullWhen the market was last seen in the feed.
observed_attimestamp | nullNullable. 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

GET/v1/markets/{platform}/{market_id}

Return one market record, addressed by its platform and platform-native market id.

Path parameters

Path parameters
NameTypeDescription
platformrequiredstringThe platform the market is listed on.
market_idrequiredstringThe 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"
Fetch one market

Response envelope

Response envelope
NameTypeDescription
dataobjectA single market record, not an array. Detail returns no page object and no cursor.
request_idstringIdentifier for this response.

Observation historyPermalink to this section

GET/v1/markets/{platform}/{market_id}/history

Return Riddle observations of a market over a requested window.

Query parameters

Query parameters
NameTypeDescription
daysintegerLength 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.
limitintegerMaximum observations returned, up to 2000.

Observation fields

Observation fields
NameTypeDescription
observed_attimestamp | nullNullable. When Riddle materialised the observation. It is a Riddle-side time, not a venue timestamp.
yes_probabilitynumberIndicative 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_basisstring | nullWhat the indicative probability was derived from for that observation.
price_semantics_versioninteger | nullVersion of the price semantics in force when the observation was recorded, so older points are interpreted under the rules that produced them.

Response envelope

Response envelope
NameTypeDescription
dataarrayObservations in time order. History is not paginated and returns no page object or cursor.
meta.platformstringThe platform the requested market is listed on.
meta.market_idstringThe platform-native market id that was requested.
meta.daysintegerThe window applied to this response, in days.
meta.window.requested_daysintegerThe window length you asked for, echoed back after defaulting and clamping.
meta.window.sincetimestampISO start of the requested window, computed at request time. It bounds what was looked at, not what exists.
meta.window.untiltimestampISO end of the requested window, computed at request time.
meta.returned_pointsintegerNumber of observations in the data array.
meta.first_observation_attimestamp | nullObservation time of the first returned point, or null when the series is empty.
meta.last_observation_attimestamp | nullObservation time of the last returned point, or null when the series is empty.
meta.rows_examinedintegerHow many stored rows were examined to build this response.
meta.excluded_invalid_pointsintegerInvalid 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_limitintegerThe limit you asked for on this request.
meta.limits.effective_point_limitintegerThe limit actually applied after clamping.
meta.limits.max_pointsintegerHard ceiling on points in one history response. Currently 2000.
meta.truncatedbooleanWhether the series was cut short by the point limit rather than by the window.
meta.completenessstringAlways partial_by_construction. History is a Riddle observation series, so it is partial by design.
meta.completeness_notestringProse restating that the series carries no claim of full venue retention or of complete coverage across the requested window.
meta.series_typestringAlways riddle_observations, marking the series as Riddle observations rather than venue market data.
request_idstringIdentifier for this response.
Example 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"]
Check the meta block before analysing

Research snapshotPermalink to this section

GET/v1/markets/{platform}/{market_id}/research

Return 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"
Request a research snapshot

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.

Riddle is a research and information platform. It is not a broker, exchange or trading venue, it does not execute orders, and nothing in this documentation is financial, investment or trading advice. Kalshi, Polymarket and Limitless are named for identification only; Riddle is not affiliated with, endorsed by or sponsored by any of them.