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-arcadeprovider 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 theprophetxprovider hint (or theprophetx:composite prefix) for event/detail lookups - Limitless uses kebab-case slugs and requires the
limitlessprovider hint (or thelimitless:composite prefix) for event/detail lookups; a matched NFL moneyline's slug is both itsmarket_idand itsevent_id, because a single market is its own event - Hyperliquid uses integer outcome ids and requires the
hyperliquidprovider hint (or thehyperliquid:composite prefix) for event/detail lookups; a matched NFL game winner's, spread's or total's id is both itsmarket_idand itsevent_id, and its outcome ids are spot coins such as#114580 - Pred uses
<parent_market_id>:<child_market_id>pairs of0x-hex ids and requires thepredprovider hint (or thepred:composite prefix) for event/detail lookups; a barepred:<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.
- Event identification — markets from different sources are grouped by the underlying real-world event (e.g., a specific NBA game on a specific date)
- Participant normalization — team names, player names, and roles are canonicalized across sources
- Sub-market grouping — within each event, markets are grouped by type (moneyline, spread, total, player prop), segment (full game, first half), and line value
- 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|winnerorspread|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.