How to match the same question across venues

No two venues share an identifier, so lining one outcome up on Kalshi and Polymarket is a judgement. What to match on, and what to write down.

There is no identifier that two venues share, so every match is somebody's judgement. Read the two rule texts rather than the two titles, and match on the named settlement source, the close moment in UTC, the fallback clause and which side is YES. Rented matchers exist — Predictefy groups equivalent markets into clusters, Adjacent publishes one rate with the spread between its sources — and each is a judgement too.

The short way

Open both records and read the rules, not the titles.

On Polymarket the record comes from the Gamma API, free and with no key: it carries the conditionId for the market, a token id for each outcome, the start and end dates, and the resolution rules text. On Kalshi the same question arrives through the Kalshi API as a tree — a series, its events, and the markets under them — where the series record carries a settlement_sources list with the name and URL of the source plus links to the contract terms and the product certification filed with the CFTC. Both surfaces answer unauthenticated reads, so the matching work costs attention rather than money.

Then build the match out of four things, none of which is the title: the source each rule text names, the close moment in UTC, the fallback when the source does not publish, and which side of the question each venue calls YES. If all four agree, you have a pair. If one disagrees, you have a decision to record rather than a pair to file.

Record it as a row with the reason attached, because in six months nobody remembers why two markets were grouped.

# A match is a record with a reason attached, not a join key.
- id: <your own id for the question>
  kalshi:
    market: <ticker, from the series then event then market tree>
  polymarket:
    condition_id: <conditionId, from a Gamma market record>
    yes_token: <the outcome token id for the side you are calling YES>
  yes_means: <one sentence, in your words, that is true of both legs>
  settlement_source: <the source each rule text names, written out twice>
  closes: <both close moments, in UTC, not in the venue's local wording>
  accepted_difference: <what you decided could not change the outcome>
  matched_on: 2026-09-21

The accepted_difference field is the whole point. A table of matched questions with no record of what was accepted is a table nobody can audit, including you.

What the options are

The Polymarket Gamma API is the read half of Polymarket and takes no credentials at all. The two identifiers everything else on that venue is keyed by come out of here — the condition id and the outcome token ids — and the field worth more attention than it gets is the rules text, where the real source and the edge cases live. One structural detail matters for matching specifically: markets are grouped as events with child markets, one event holding many tradeable questions, each with its own pair of outcome tokens. An event is not a question, and matching at the event level quietly groups things that resolve separately.

The Kalshi API is the most conventional surface in this category and reads like a futures venue. The series is where the matching evidence sits: the exchange resolves its own markets under the rules of each series, and the series record exposes the named settlement sources with their URLs, the contract terms, and the product certification filed with the CFTC. Two practical notes. REST market data answers unauthenticated requests, so a candidate pair can be read without an account on either side. And archived markets move to a parallel set of historical endpoints behind a cutoff that keeps advancing — a matcher written only against the live endpoints loses its tail.

Predictefy rents you the matching. Its documentation describes a matching engine of embedding similarity plus LLM validation that groups equivalent markets into clusters across sixteen venue ids, with a /v1/discrepancies layer reporting price gaps between cluster members. What makes it worth citing is the honesty of the field it hands back: each cluster member carries a similarity score that the docs describe as the raw embedding similarity and say is deliberately not called confidence, because it is not a calibrated probability that the markets are equivalent. Resolution equivalence — that two markets truly settle on the same terms — is a separate gate, and the discrepancy endpoints do not apply it. Nothing reads without a key, the free plan is non-commercial with a seven-day history window, and a fresh AI cross-match is the most expensive call on the meter at 25 credits.

Adjacent solves a narrower version of the same problem and publishes the result instead of the method. A reference rate takes the same underlying question priced on more than one venue, combines the source prices into one series, and reports the spread between them — volume-weighted, equally weighted, or by median. The rate list is GET /api/v1/rates, a single rate is GET /api/v1/rates/{rate_id}, and the constituent prices come back from GET /api/v1/rates/{rate_id}/prices with sources=true. The documentation's own caution is the useful part: a small spread means the sources are close, and a large one tells you to inspect the individual markets before treating the rate as a single signal. Coverage is Kalshi and Polymarket only, and on 19 September 2026 every market the free public tier returned was Kalshi.

Brier.fyi is the one published methodology for doing this properly, and it is the bar the rest of the page is measured against. It generates embeddings to find similar markets, refines with tags, keywords, duration overlap and other heuristics, and then has real people pick and approve every match. The stated tolerance is that markets may be grouped only if their differences would not have more than a one-percent chance of changing the resolution, and the site names its own hard cases: two markets resolving on 31 December against 1 January, or using two different news sources. The code is MIT and the matched set is readable — but the pipeline that fed it is paused, the site's content is under a non-commercial licence, and most of what it scored resolved in 2024 or 2025. Read it for the method, not for a current table.

Where this breaks

There is no shared identifier, so every match is somebody's judgement — including the rented ones. Kalshi's key is a ticker tree; Polymarket's is a condition id plus one token id per outcome on a chain. Nothing derives one from the other and no registry maps between them, which means every product on this page computed its matches from text. Predictefy says so about its own field in as many words, and Brier.fyi's answer is to put a person at the end of the pipeline. A match that arrives as a row in somebody's API is not more authoritative than one you made; it is the same kind of object with the provenance further away. Store whose judgement it was.

Fuzzy title matching fails exactly where it matters — on one clause. "Above 90" and "at least 90" differ by one degree of outcome and by almost nothing to an embedding. So do a threshold crossed by a date and one crossed in a year, a market on the winner of a contest and one on the winner of the first round, and a question naming one agency's release against another's. These pairs score high and resolve differently, which is the worst combination available: a matcher tuned to catch them also groups genuinely distinct markets, and one tuned to avoid them misses the mirrors it exists for. This is why the honest bar is a tolerance about the resolution rather than a similarity threshold.

"The same question" is usually false once you read both rule texts. The named source differs — one contract settles on a specific agency's first publication, the other on a named news outlet. The fallback differs: some contracts hold expiration until a corrected release, others fall back to a price determined by the exchange, and on Polymarket a UMA token-holder vote can return Unknown, in which case the market resolves 50/50 and every token on both sides redeems at 50 cents. Who decides differs too — the exchange under its own series rules on one side, a bonded proposal escalating to a vote on the other, with a disputed question running four to six days. Who decides how a prediction market resolves is the page to read before a pair goes into the table.

Time zones and cutoff moments are a second question hiding inside the first. Close time, expiration, and the last moment the source can still publish are three different timestamps, and venues word them in three different ways. A temperature market is the clearest case: the day it settles on runs midnight to midnight local standard time at one named station, regardless of when either venue stops trading, and the number is preliminary for months afterwards — what a temperature market actually settles on works through it. Normalise everything to UTC before comparing, and record the close moments separately from the resolution moment, because it is the second one that decides whether two contracts answer the same question.

One venue's YES is the other's NO. Polymarket's token ids are per outcome, so the side is an identifier rather than a flag; Kalshi's markets carry a yes and a no side with the question worded one way round. Two venues routinely mirror a question — "above" against "at or below", "wins" against "does not win" — and a match that lines up the markets without lining up the sides inverts a whole column silently. The same trap shows up inside a normalised feed: FinFeedAPI documents that its market ids are per outcome rather than per question, so anyone counting markets or joining across venues has to carry that from the first query.

A gap between two matched prices is more often a failed match than a finding. Fees are charged on different bases, collateral is dollars on one side and a stablecoin on the other, the exits cost and delay different amounts, and eligibility rules frequently stop one person holding both legs at all — why the same contract costs two different prices takes the gap apart piece by piece. Predictefy's own documentation draws the same line: its discrepancy endpoints indicate where to investigate, not what to do, and a row earns a stronger label only once live-depth, verified-fee, open-market and resolution-equivalence gates have each passed. Treat a persistent gap on a matched pair as evidence that the pair is wrong until the rule texts say otherwise.

If you outgrow this

If two venues are not enough, the rented versions are listed together: tools that cover more than one venue is the collection, and Adjacent vs DepthFeed vs Predexon is the head to head. Every one of them flattens a difference to put two venues in one schema, and on a matched question the difference being flattened is the one this page is about. PMXT offers cross-exchange market matching on its hosted tier under a wider venue list, with the caveat its card carries: the library stopped shipping in July 2026.

If what you want next is the history behind each leg rather than the pairing, how to get resolved market history as data is the companion page, and Predexon and DepthFeed are where a question about the order book at a point in time goes. Neither of them does the matching for you.

If what you want is whether the two prices were any good rather than whether they were the same question, Brier.fyi already did both the matching and the scoring — read how accurate a prediction market's price actually is alongside it, and note that what you are reading is an archive with an unusually good method attached.

The approaches, in order

Cards in the catalogue that do this, ordered editorially. Paid placement does not affect this order.

  1. 1.Polymarket Gamma API

    Free and keyless. Every market record carries its condition id, its outcome token ids and the resolution rules text — the rules are what you match on.

    Polymarket's read-only catalogue of events, markets, series and tags.

    FreeFree tier

  2. 2.Kalshi API

    Series, then events, then markets. The series record names the settlement source and links the contract terms; REST market data answers without an account.

    REST, WebSocket and FIX access to a CFTC-regulated event exchange.

    Free tier onlyFree tier

  3. 3.Predictefy

    A clusters endpoint that groups equivalent markets across sixteen venues, with a similarity score its own documentation refuses to call a probability.

    Sixteen venues behind one verb family, priced in credits, with nothing readable for free.

    $49/moFree tier

  4. 4.Adjacent

    Reference rates — one series built from the same question on more than one venue, published beside the spread between its sources rather than instead of it.

    Prediction-market indices and reference rates, with Kalshi on the tier you can test.

    $50/moFree tier

  5. 5.Brier.fyi

    An open-source set of hand-approved cross-platform matches with a published tolerance rule. Read the methodology; its pipeline is paused.

    Brier scores and letter grades for matched questions across four platforms.

    FreeFree tierOpen source

FAQ

Is there an identifier two venues share for the same question?

No. Kalshi keys on a ticker tree of series, event and market; Polymarket keys on a condition id plus one token id per outcome, on a chain. Neither is derivable from the other, and no registry maps between them. Anything that lines the two up computed the match, and the match is a judgement rather than a lookup.

Can I just match on the question titles?

Not safely. Two questions that differ by one clause — a date, a threshold, "at least" against "more than", a different named source — read almost identically to an embedding and resolve differently. Brier.fyi's published method runs embeddings first, then refines with tags, keywords and duration overlap, and then has a person approve every match.

What tolerance should I accept when two contracts nearly agree?

Brier.fyi publishes the only stated bar in this catalogue: two markets may be grouped only if their differences would not have more than a one-percent chance of changing the resolution. That is a judgement about a specific pair, not a threshold you can compute, which is why its matches are approved by people.

Does a price gap between two matched contracts mean one of them is wrong?

More often it means they are not the same contract. Different rule texts, different fallback clauses, fees charged on different bases, collateral in dollars against a stablecoin, and different close times all move one leg without moving the other. Read why the same contract costs two prices before treating a gap as a data error.

Sources

  1. Cross-venue intelligence Predictefy, read
  2. Reference rates Adjacent, read
  3. About Brier.fyi, read

The catalogue next door

This page names a handful of cards. The rest of them are in Prediction Market Data APIs, each filled in against the same schema, with the fields to narrow it yourself.

Last updated . Corrected in place: this is a reference page, not a dated post.