PredictorSDK
Concepts

Matching Markets

How PredictorSDK matches equivalent markets across prediction market platforms.

The core abstraction in PredictorSDK is the matching market. When multiple prediction market platforms list the same real-world event, PredictorSDK groups them into a single canonical event with matched sub-markets.

The problem

Each prediction market platform has its own way of representing markets:

  • Kalshi uses ticker symbols like KXNBAGAME-26OCT20OKCSAS
  • Polymarket uses Gamma market IDs/slugs for markets, CLOB token IDs for selections (position IDs on its Protocol V2 markets), and condition IDs in wallet positions
  • SX.Bet uses on-chain market IDs
  • Predict uses numeric IDs like 77071
  • AlphaArcade uses ULIDs and requires the alpha-arcade provider hint (?provider= or the composite prefix) for event/detail lookups
  • ProphetX uses integer event ids and <event_id>:<market_id> market keys (a matched player prop's key ends in its line, <event_id>:<market_id>:<line>, because the pair alone resolves to whichever line ProphetX currently favours), and requires the prophetx provider hint (or the prophetx: composite prefix) for event/detail lookups
  • Limitless uses kebab-case slugs and requires the limitless provider hint (or the limitless: composite prefix) for event/detail lookups; a matched NFL moneyline's slug is both its market_id and its event_id, because a single market is its own event
  • Hyperliquid uses integer outcome ids and requires the hyperliquid provider hint (or the hyperliquid: composite prefix) for event/detail lookups; a matched NFL game winner's, spread's or total's id is both its market_id and its event_id, and its outcome ids are spot coins such as #114580
  • Pred uses <parent_market_id>:<child_market_id> pairs of 0x-hex ids and requires the pred provider hint (or the pred: composite prefix) for event/detail lookups; a bare pred:<parent> addresses the parent as a group

The same game (Thunder at Spurs) appears differently on each platform. PredictorSDK resolves this by matching them into a single canonical event.

How matching works

Player props distinguish exact prop identity from settlement equivalence. Strict matching is the default; opt into player_prop_match=same_prop with include_submarkets=true for a source-by-source rule matrix that also includes different or unverified rules. See player-prop matching for coverage, examples, and the complete options matrix.

  1. Event identification — markets from different sources are grouped by the underlying real-world event (e.g., a specific NBA game on a specific date)
  2. Participant normalization — team names, player names, and roles are canonicalized across sources
  3. Sub-market grouping — within each event, markets are grouped by type (moneyline, spread, total, player prop), segment (full game, first half), and line value
  4. Outcome mapping — source-native selections (yes/no, team names, over/under) are mapped to canonical outcome keys

Stable identifiers

Every matched entity gets a stable canonical identifier:

  • Event ID: nba-okc-sas-2026-10-20 (league-away-home-date)
  • Sub-market key: moneyline|full|winner or spread|full|spread:okc|8.5
  • Outcome key: winner:sas, cover:okc, over, under

These identifiers are deterministic and consistent across API calls.

Every response lists matched events in data, soonest scheduled start first. A game stays listed through the day after its scheduled date, so the first events can be games that have already been played (see Include settled events). Each event lists its submarkets, and each submarket lists the venue markets matched to it in source_markets[], with the same fields on every venue: provider, the venue's parent event_id, the market's market_id and market_name, and outcome IDs mapped to canonical outcome keys. A list page covers events whose full-game moneyline is matched, and each carries only that submarket, so a page stays small; add include_submarkets=true for every matched submarket, and for events matched only on spreads, totals or props.

To narrow the list, filter it by league, scheduled_date or participant (see Filtering). To retrieve known events without paging through it, call GET /v1/matching-markets/sports/lookup and pass canonical keys as event_id, or a venue market's market_id, market_slug or event_id as source_id={provider}:{id} (for example kalshi:KXNBAGAME-26OCT20OKCSAS). On Polymarket, Predict and AlphaArcade an outcome token works too, and on Polymarket so does a condition ID. Both parameters repeat and combine, up to 100 unique identifiers in all, and matching is case-insensitive. A lookup returns each event it found once, with every submarket, and lookups reports each identifier as you sent it with the events it found, none if it found nothing.

Upgrading from SDK 0.22 or earlier? Lookups moved to their own route, GET /v1/matching-markets/sports/lookup, and event_id and source_id are no longer read by the list, which answers 400 naming the new route. Every event now carries scheduled_date and scheduled_start. The same release renames the platform override to provider and makes the SDKs' paginated calls return pages you can iterate (see the Pagination guide). Upgrading from SDK 0.20 or earlier? Events moved from the canonical_events map to the data array, and a lookup reports what each identifier found in lookups instead of keying events by identifier. From 0.18 or earlier, the per-venue markets map and the per-venue lookup parameters (kalshi_event_ticker, polymarket_market_slug, …) are gone too: send source_id={provider}:{id}. A retired parameter answers 400 naming its replacement. The changelog maps each old field to its new home.

The canonical event_id lookup and the event_id on each source market are different things. The lookup takes PredictorSDK's stable cross-provider key; the source-market field is native to one venue and names that venue's parent event, for GET /v1/events/{event_id}. The market itself is market_id, for GET /v1/markets/{market_id}. Send the source market's provider as either endpoint's provider query parameter every time — it is required for Predict market IDs, AlphaArcade ULIDs, ProphetX IDs, Pred IDs, Limitless slugs and Hyperliquid IDs. Without a provider, shared identifier shapes are probed across eligible providers, and an identifier that resolves on more than one returns 409 Conflict with candidates instead of silently choosing the wrong one. One venue's event_id has a shorter life than the row that carries it: SX Bet lists a fixture only while it trades, so for a game that has been played its event_id answers 404 on GET /v1/events/{event_id} while its market_id still resolves. See Which identifier goes where.

Identity, not odds

PredictorSDK's matching surface focuses on market identity and matching, not odds or liquidity data. It tells you which markets exist, where they exist, and how they map to each other. Use the exact provider-native market/outcome references with market detail or the venue directly for current data.

On this page