Skip to content

Market object

The canonical market object returned by GET /v2/markets and GET /v2/pairs/{market_id}, and the real-time stream envelope.

The API serves one canonical market object across REST and the stream. GET /v2/markets returns a list of them; GET /v2/pairs/{market_id} returns one with the full detail blocks attached. There is no longer a separate lean-tick shape. The same object carries identity, prices, and settlement, with the price and settlement blocks only populated where the request asked for them.

  • GET /v2/markets: the catalog, one row per matched game. Unfiltered, prices are null. signal=arb returns the same rows priced from the live snapshot, filtered to confirmed arbs.
  • GET /v2/pairs/{market_id}: one market by id, with include=ids,depth,settlement applied by default: venue handles, confidence, per-outcome book detail, and settlement disclosure.

The market object

{
  "id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2",
  "slug": null,
  "sport": "baseball",
  "league": "MLB",
  "title": "Nyy vs Bos",
  "home": null,
  "away": null,
  "start_time": "2026-07-01T23:05:00+00:00",
  "status": "open",
  "match": "high_confidence",
  "signal": "arb",
  "spread_pts": 2.0,
  "potential_arb_pct": 1.83,
  "coverage": { "kalshi": true, "polymarket": true },
  "outcomes": [
    { "key": "NYY", "name": "NYY", "kalshi": 0.52, "polymarket": 0.54, "cheaper": "kalshi" },
    { "key": "BOS", "name": "BOS", "kalshi": 0.50, "polymarket": 0.46, "cheaper": "polymarket" }
  ],
  "result": null,
  "updated_at": "2026-07-01T22:55:00+00:00",
  "priced_at": "2026-07-01T22:55:00+00:00",
  "links": { "kalshi": "https://kalshi.com/markets/KXMLBGAME-26JUL01NYYBOS", "polymarket": null }
}

Object fields

FieldTypeMeaning
idstringThe stable canonical id, dino_<uuid>. Use this to call GET /v2/pairs/{market_id}.
slugstring|nullA clean-name identifier. Not yet populated; always null today.
sportstringThe broad sport key.
leaguestring|nullThe competition within the sport, such as MLB. Null when the venue series does not name one.
titlestring|nullA best-effort title built from the matched team identifiers.
home / awaystring|nullNot yet populated; always null today.
start_timestring|nullISO-8601 UTC kickoff.
statusstringopen, live, closed, or settled. See Status.
matchstringThe match-confidence label. Currently always high_confidence for served rows.
signalstring"arb" or "spread". See Signal.
spread_ptsnumber|nullMax per-outcome cross-venue gap, in percentage points. Null unless every outcome is priced on both venues.
potential_arb_pctnumber|nullThe gross ask-to-ask return, before venue fees. Populated only when signal is "arb". An observation of the snapshot at priced_at.
coverageobject{ kalshi, polymarket }, each true only if every outcome has a price on that venue.
outcomesarrayPer-outcome pricing. See Outcomes. Empty when the row is unpriced.
resultobject|nullSettlement verdict, populated only once status is settled.
updated_atstring|nullWhen the matcher last re-analysed this pair's identity and settlement terms.
priced_atstring|nullA freshness signal. On REST it's currently the same value as updated_at — there's no separate live-price timestamp stored per row yet, so treat it as "last write to this pair," not "last price tick." On the WebSocket markets:* stream, both frame types (market and price) carry the real capture instant. Null only if the row has no updated_at either.
linksobject{ kalshi, polymarket } venue URLs. polymarket is null until a stable link form is available.

Outcomes

Each entry in outcomes is one market outcome (e.g. a team) with its prices on both venues:

FieldTypeMeaning
key / namestringThe outcome label, such as a team name.
kalshinumber|nullThe ask price on Kalshi, 0 to 1. Null if not available; never 0 for absent.
polymarketnumber|nullThe ask price on Polymarket, 0 to 1. Null if not available.
cheaperstring|nullThe venue with the lower ask for this outcome, "kalshi" or "polymarket". Null if either side is unpriced.

With include=depth, each outcome also carries kalshi_book and polymarket_book, sibling objects with ask, depth, and the venue ticker or token id. These never replace the scalar kalshi / polymarket price fields.

Status

ValueMeaning
openActively matched and tracked (covers both active and review-band pairs internally).
liveReserved for in-progress games. Not yet served; no row returns live today.
closedRetracted with no settlement verdict on file.
settledRetracted with a settlement verdict on file; result is populated.

Only open-status rows carry a signal other than spread. A non-open row always forces signal to spread and potential_arb_pct to null, regardless of any prices attached.

Signal

signal labels the settlement relationship the matcher found for the market, re-derived on every request from the current row and prices. It is never trusted from a cached label.

ValueMeaning
"arb"Earned. All of: status is open with 2+ confirmations, settlement parity confirmed, no high or critical severity risk in the pair's complete internal risk set, and every outcome priced on both venues. potential_arb_pct is populated.
"spread"Everything that isn't a confirmed arb. Includes a correctly matched game with disclosed settlement risk, an unpriced catalog row, or a review-band candidate. Read settlement.risks (via include=settlement or /v2/pairs/{market_id}) before treating a spread row as tradeable at all.

The served settlement.risks never include cancellation (cancel_*) events; those are filtered server-side, while the signal gates still evaluate the complete internal set. An arb row can still carry a bounded non-cancellation entry that does not break normal-resolution parity, so arb does not imply an empty risks[]. Always read the settlement block before acting.

How these scores are produced, including the matching sweep, the fuzzy cap, and the automated promotion gate, is covered in Matching & confidence.

Over the WebSocket

The market object rides the markets:* channels as flat frames: a market frame (the full canonical object, on a state change) and a price frame (a compact delta keyed on the outcome key, on a price move). Both frame types are documented on the WebSocket stream page.

Bootstrapping from REST

GET /v2/markets does not carry a stream-offset field. There is no as_of_offset in v2. To start with a complete picture and then stay live: read /v2/markets?signal=arb for the open arbs right now, then connect the WebSocket stream. Messages that arrive after you connect are gapless from that point forward; there's no snapshot-to-offset stitching in v2, so treat the REST read and the stream connect as two independent views. The WebSocket stream page covers connecting and disconnect recovery.