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 arenull.signal=arbreturns the same rows priced from the live snapshot, filtered to confirmed arbs.GET /v2/pairs/{market_id}: one market by id, withinclude=ids,depth,settlementapplied 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
| Field | Type | Meaning |
|---|---|---|
id | string | The stable canonical id, dino_<uuid>. Use this to call GET /v2/pairs/{market_id}. |
slug | string|null | A clean-name identifier. Not yet populated; always null today. |
sport | string | The broad sport key. |
league | string|null | The competition within the sport, such as MLB. Null when the venue series does not name one. |
title | string|null | A best-effort title built from the matched team identifiers. |
home / away | string|null | Not yet populated; always null today. |
start_time | string|null | ISO-8601 UTC kickoff. |
status | string | open, live, closed, or settled. See Status. |
match | string | The match-confidence label. Currently always high_confidence for served rows. |
signal | string | "arb" or "spread". See Signal. |
spread_pts | number|null | Max per-outcome cross-venue gap, in percentage points. Null unless every outcome is priced on both venues. |
potential_arb_pct | number|null | The gross ask-to-ask return, before venue fees. Populated only when signal is "arb". An observation of the snapshot at priced_at. |
coverage | object | { kalshi, polymarket }, each true only if every outcome has a price on that venue. |
outcomes | array | Per-outcome pricing. See Outcomes. Empty when the row is unpriced. |
result | object|null | Settlement verdict, populated only once status is settled. |
updated_at | string|null | When the matcher last re-analysed this pair's identity and settlement terms. |
priced_at | string|null | A 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. |
links | object | { 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:
| Field | Type | Meaning |
|---|---|---|
key / name | string | The outcome label, such as a team name. |
kalshi | number|null | The ask price on Kalshi, 0 to 1. Null if not available; never 0 for absent. |
polymarket | number|null | The ask price on Polymarket, 0 to 1. Null if not available. |
cheaper | string|null | The 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
| Value | Meaning |
|---|---|
open | Actively matched and tracked (covers both active and review-band pairs internally). |
live | Reserved for in-progress games. Not yet served; no row returns live today. |
closed | Retracted with no settlement verdict on file. |
settled | Retracted 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.
| Value | Meaning |
|---|---|
"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.