PredictorSDK
Concepts

Events and Markets

Understanding events, sub-markets, and how canonical matching works internally.

PredictorSDK matches prediction markets across sources into canonical events. This page explains the internal data model that powers matching. The public API returns a simplified view -- see the API Reference for the exact response shape.

Events

An event represents a single real-world occurrence, like an NBA game. Internally, each event gets a stable canonical identifier.

For team sports the ID follows the pattern <league>-<away>-<home>-<YYYY-MM-DD>, e.g. nba-okc-sas-2026-10-20.

Sub-markets

Each event contains sub-markets. A sub-market is a specific bet type within the event, identified by a canonical key.

spread|full|spread:hou|8.5

Key fields in the internal model:

FieldDescription
keyCanonical sub-market identifier within the event. A second-half or fourth-quarter line whose venues count overtime ends its detail with :incl_ot, as in spread|half:2|spread:hou:incl_ot|8.5
market_typemoneyline, spread, total (the game's total points), team_total (one team's own points), or player_prop. Read it as text: a value you do not know yet is a new kind of market
segmentfull, half:1, half:2, or quarter:1 to quarter:4. A half or quarter means regulation play, except a second half or fourth quarter whose venues count overtime, which says so in key, rules.settlement and display_name
lineNumeric line value when applicable. Omitted on moneylines, signed for spreads, positive for totals and props
subjectOptional market subject: the team of a spread or team total, the player of a prop. Omitted for event-owned game lines such as moneyline and total
rulesOptional settlement metadata when it materially affects matching

Team totals

A team total is one team's own total, such as the Eagles over 24.5 points (market_type team_total). It is not the game's total: its subject is the team, its metric is points, its line is a positive threshold, and its key names the team, team_total|full|team_total:phi:incl_ot|24.5, so the Eagles' line and the Jaguars' at the same number are two sub-markets. Its display_name names the team (Philadelphia Eagles Total 24.5, 1H Philadelphia Eagles Total 13.5) and its outcomes are over and under for that team. A full-game team total counts overtime, as a full-game total does, and says so in its key and rules.settlement. It carries the same rule matrix as every game line.

Team totals match for the NFL, across Kalshi and Polymarket: the full game and the first half. Polymarket also lists a second-half team total, which counts overtime and stays one venue's line until Kalshi lists one. Other leagues' team totals are not matched yet.

For event-owned game lines, subject is omitted entirely. Moneyline rows also omit line, while full-game totals may include rules.settlement when overtime treatment affects matching. A second-half or fourth-quarter spread or total that counts overtime carries rules.settlement of incl_ot too; a regulation half or quarter has none.

Outcomes

Each sub-market defines canonical outcomes that source-native selections map into. For example, a spread market has cover and not_cover outcomes.

Source matching

The sports matching engine maps source-native markets from Kalshi, Polymarket, Predict, SX.Bet, AlphaArcade, ProphetX, Pred, Limitless, and Hyperliquid into these canonical structures. AlphaArcade participates for full-game moneylines in matcher-supported leagues when its structured start date agrees with any explicit date in its title or slug (MLB, WNBA, NHL and NFL have matched live), and for full-game spreads that are one market with one line (NHL puck lines have matched live). Predict participates for full-game moneylines in the NBA, NHL, MLB and NFL, and for NBA and NFL full-game spreads and totals when it lists them. Pred participates for NFL full-game moneylines, spreads and totals, and for NBA moneylines. Limitless participates for NFL full-game moneylines, each mirroring a Polymarket market for the same game. ProphetX participates for full-game moneylines in NFL, MLB, NBA, and NHL, resolving each side through the event's structured competitor list. Verified NFL player props also participate through same-prop comparisons with settlement-rule differences. Hyperliquid participates for NFL full-game moneylines, spreads and totals, each joining the event other venues list for the same two teams on the same day, because Hyperliquid does not say which team is home; its other markets are exposed in the market catalog and detail/event endpoints. The public API exposes matched sports results as canonical events whose submarkets list every matched venue market in one shape -- see Matching Markets for details on how matching works.

Public API response

GET /v1/matching-markets/sports lists matched events in data, soonest scheduled start first (a game stays listed through the day after its scheduled date), and GET /v1/matching-markets/sports/lookup finds events by identifier and adds lookups, naming the events each identifier found. Each event carries its schedule (scheduled_date, the game's America/New_York day, and scheduled_start, its UTC start when a venue published an exact time), its canonical participants and submarkets, and each submarket lists the venue markets matched to it in source_markets[]. A list page covers events whose full-game moneyline is matched, and each carries only that submarket, so submarkets is never empty; include_submarkets=true adds spreads, totals, period lines and player props in the same shape, and events matched only on those. A lookup returns every submarket by default. Submarkets read in a fixed order: the full game, then each half and quarter, and within each, the moneyline, spreads, totals, team totals and props. Spread, team-total and player-prop submarkets also include the public subject that gives the signed line meaning, and event-owned moneyline and total submarkets omit it. The example is abridged: a submarket appears only when at least two venues list it, so every submarket in a real response has two or more source_markets entries, and the spread below shows just SX Bet's:

{
  "data": [
    {
      "event_id": "nfl-sea-den-2026-10-15",
      "sport": "football",
      "league": "nfl",
      "title": "Seattle Seahawks vs. Denver Broncos",
      "scheduled_start": "2026-10-16T00:15:00.000Z",
      "scheduled_date": "2026-10-15",
      "participants": [
        { "key": "sea", "name": "Seattle Seahawks", "role": "away" },
        { "key": "den", "name": "Denver Broncos", "role": "home" }
      ],
      "submarkets": [
        {
          "key": "moneyline|full|winner",
          "market_type": "moneyline",
          "segment": "full",
          "display_name": "Winner",
          "outcomes": [
            { "key": "winner:sea", "label": "Seattle Seahawks win", "side": "win" },
            { "key": "winner:den", "label": "Denver Broncos win", "side": "win" }
          ],
          "source_markets": [
            {
              "provider": "kalshi",
              "event_id": "KXNFLGAME-26OCT15SEADEN",
              "market_id": "KXNFLGAME-26OCT15SEADEN-DEN",
              "market_name": "Denver wins",
              "outcomes": [
                { "canonical_outcome_key": "winner:den", "label": "Yes", "outcome_id": "yes", "side": "yes" },
                { "canonical_outcome_key": "winner:sea", "label": "No", "outcome_id": "no", "side": "no" }
              ]
            },
            {
              "provider": "kalshi",
              "event_id": "KXNFLGAME-26OCT15SEADEN",
              "market_id": "KXNFLGAME-26OCT15SEADEN-SEA",
              "market_name": "Seattle wins",
              "outcomes": [
                { "canonical_outcome_key": "winner:sea", "label": "Yes", "outcome_id": "yes", "side": "yes" },
                { "canonical_outcome_key": "winner:den", "label": "No", "outcome_id": "no", "side": "no" }
              ]
            },
            {
              "provider": "polymarket",
              "event_id": "nfl-sea-den-2026-10-16",
              "market_id": "4212356",
              "market_name": "Seahawks vs. Broncos",
              "market_slug": "nfl-sea-den-2026-10-16",
              "outcomes": [
                { "canonical_outcome_key": "winner:sea", "label": "Seahawks", "outcome_id": "61085243734073593459419955931627362545950081431149347585770687210061098950896" },
                { "canonical_outcome_key": "winner:den", "label": "Broncos", "outcome_id": "78653414984017864835330593213700943479354040352698721042453780387223265497055" }
              ]
            }
          ]
        },
        {
          "key": "spread|full|spread:den|3.5",
          "market_type": "spread",
          "segment": "full",
          "display_name": "Denver Broncos -3.5",
          "line": -3.5,
          "subject": { "type": "participant", "key": "den", "name": "Denver Broncos" },
          "outcomes": [
            { "key": "cover:den", "label": "Denver Broncos covers -3.5", "side": "cover" },
            { "key": "not_cover:den", "label": "Denver Broncos does not cover -3.5", "side": "not_cover" }
          ],
          "source_markets": [
            {
              "provider": "sxbet",
              "event_id": "L19431555",
              "market_id": "0x4703010fbb76917d799c0df6c2886d52b863d04c1578157f431b9684b13bdaa0",
              "market_name": "Denver Broncos -3.5 vs Seattle Seahawks +3.5",
              "outcomes": [
                { "canonical_outcome_key": "cover:den", "label": "Denver Broncos -3.5", "outcome_id": "outcomeOne" },
                { "canonical_outcome_key": "not_cover:den", "label": "Seattle Seahawks +3.5", "outcome_id": "outcomeTwo" }
              ]
            }
          ]
        }
      ]
    }
  ]
}

A Kalshi or Pred outcome is a side of its market, so it is labelled Yes or No, as market detail names it; the market itself is market_name. An SX Bet market is named by its two outcome labels, as market detail titles it, because the venue's own market name is the game's team pair, shared by its moneyline and every spread and total.

Which identifier goes where

Every source market carries the same references, on every venue:

FieldWhat it namesWhere it goes
event_idthe venue's parent eventGET /v1/events/{event_id}
market_idthe market itself (market_slug too, on Polymarket)GET /v1/markets/{market_id}
outcomes[].outcome_idone side of that marketthe same outcomes[].outcome_id on market detail
event_id, market_id, market_slug, a Polymarket, Predict or AlphaArcade outcome_id, or a Polymarket condition IDsource_id={provider}:{id} on GET /v1/matching-markets/sports/lookup, to find the event again

Kalshi lists a game winner as one binary market per team ("Tampa Bay wins" and "Dallas wins" are separate markets) rather than one market with two outcomes, so a Kalshi moneyline appears once per team ticker, each with yes / no outcomes mapped to the right teams. Group by provider if you want one entry per venue.

event_id names a parent, not a market. Kalshi files a game's spread and total markets under events of their own (KXNFLSPREAD-…), and a parent ID is not a market ID even where it looks like one: a Kalshi event ticker answers 404 on market detail, and SX Bet and ProphetX event IDs answer 400. Polymarket's event_id above carries the UTC date of a Thursday-night kickoff while the canonical key uses the Eastern one, which is why identity is read from the venue's own fields rather than parsed from its slug.

An SX Bet event_id resolves only while the fixture is trading. SX Bet lists a fixture's markets through its active-markets route, which drops the fixture once it stops trading, and it has no keyless route that lists them by fixture ID afterwards: /markets/find takes market hashes only, and the trade tape holds nothing for a finished fixture. The default list keeps today's and yesterday's games, so the event_id of an SX Bet source market on a game that has been played answers 404 on GET /v1/events/{event_id} while the same source market's market_id still resolves on market detail, with status settled. Measured on 2026-10-10 across the 57 SX Bet fixtures on the default list: SX Bet listed markets for all 48 that had not started and for both that were in play, and none for the 7 it reported as finished, which are the 7 that answer 404. Kalshi, Polymarket and Predict event IDs resolved on 6 of 6 sampled events on 2026-10-08. To read a finished SX Bet game, send its market_id.

Always send the source market's provider with either call. It is the same lowercase ID the provider query parameter takes, so you can pass it through verbatim:

GET /v1/events/{event_id}?provider={provider}
GET /v1/markets/{market_id}?provider={provider}

The composite {provider}:{id} form that GET /v1/markets returns in data[].id is accepted by both endpoints too, and it is equally unambiguous: GET /v1/markets/predict:1607914 and GET /v1/events/predict:1607914 each go straight to Predict.

Omitting provider is safe only for Kalshi and SX Bet, whose identifier shapes route on their own. A Predict market ID is a bare number and a Polymarket identifier can be a bare number or a kebab-case slug — the shapes are shared, and the two ID spaces really do overlap. On market detail, 53 of 93 Predict market IDs sampled 2026-08-25 from /v1/matching-markets/sports also named a real Polymarket market, and on 2026-10-10 all 14 sampled open Predict moneyline IDs did.

When you omit provider on one of those shared shapes, the endpoint probes both providers and answers only if exactly one of them holds that identifier. If both do, it returns 409 Conflict listing the candidates rather than picking one. This is the answer of GET /v1/markets/1607914, a number that is both a Predict market and an unrelated Polymarket market:

{
  "code": "ambiguous_identifier",
  "message": "market_id \"1607914\" resolves on polymarket and predict, which are different markets. Retry with ?provider= naming the one you want, or use the composite {provider}:{id} form.",
  "status_code": 409,
  "param": "market_id",
  "candidates": ["polymarket", "predict"]
}

GET /v1/events/{event_id} probes the same way and answers the same way (param is event_id and the message says "different events"), but a Predict market ID collides there far less often: Polymarket numbers its events and its markets separately, and Predict's IDs fall in the range of Polymarket's market IDs. On the same day, all 14 sampled Predict IDs answered 409 on market detail and 200 on GET /v1/events/{event_id}, which returned the Predict event.

Passing provider on every request is the one rule that works for all nine providers. It is also faster and more available: the probe has to reach both providers to prove there is no collision, so it fails when either is having an outage, while a named provider depends only on that one. An AlphaArcade ULID, a Hyperliquid integer id, a Limitless slug, and a Pred 0x-hex id or pair are not inferred at all and always require provider (or the composite form).

See the TypeScript SDK Reference for the full type definitions.

outcome_id is a source-native selection reference, never a universal cross-provider outcome ID. It is the same string GET /v1/markets/{market_id} returns as outcomes[].outcome_id for the market this row names, on every provider. Polymarket, Predict and AlphaArcade publish a per-outcome token that is unique on that venue, so source_id finds an event from the token alone (source_id=polymarket:<token>). AlphaArcade's game lines reuse the token of the Polymarket market they mirror, so the same token can appear under both providers: key a cross-venue map by provider and token, never by token alone. SX Bet, Kalshi, ProphetX and Pred publish none, so their references are market-scoped and only mean something read together with market_id: SX Bet exposes one market hash plus two named positions (outcomeOne, outcomeTwo), Kalshi and Pred markets are binary (yes, no), and ProphetX numbers the sides of a market with small integers (4, 5) that repeat on every moneyline. These are not lookup identifiers: source_id answers 400 for Kalshi's and Pred's yes/no and SX Bet's outcomeOne/outcomeTwo, pointing you to the market's market_id.

This remains an identity endpoint. Use the returned IDs with market detail for current status, normalized quotes, timestamps, and liquidity. Consumers that require raw executable depth must obtain it from a venue or execution client; the matching response does not embed execution data.

On this page