Skip to content
Get an API key

Compare one market across every venue

“Will the Fed cut in January?” trades on several venues at once, under different titles, different tickers, and occasionally different resolution rules. This recipe lines them up.

compareMarketPrices exists for exactly this. Give it a market on any venue and it returns the cross-venue comparison directly — no cluster lookup needed.

Terminal window
curl -s "$PREDICTEFY_API_URL/api/kalshi/compareMarketPrices?marketId=MARKET_ID&live=true" \
-H "Authorization: Bearer pk_live_YOUR_KEY"

It takes marketId or slug, plus live, limit, offset and page. Each row is one venue:

{
"success": true,
"data": [
{
"market": { "...": "the normalized market record for this venue" },
"venue": "kalshi",
"clusterId": "clr_9f2a7c41",
"similarity": 0.94,
"reasoning": null,
"bestBid": 0.412,
"bestAsk": 0.418,
"indicativeYesPrice": 0.415,
"indicativeNoPrice": 0.585,
"label": "indicative price discrepancy",
"asOf": "2026-08-12T14:31:04.882Z"
}
],
"meta": { "live": true, "liveUnavailable": false }
}

Two pairs of price fields, and the difference matters:

  • bestBid / bestAsk come from the live book overlay. They are never a stored snapshot — if the overlay could not run, they are null rather than stale.
  • indicativeYesPrice / indicativeNoPrice are the catalog’s view, which may be a stored value.

Check meta.liveUnavailable before you present anything as live. When it is true, the live overlay was requested and did not succeed, and the row is telling you so instead of quietly serving you older numbers.

Use this when you want the grouping itself rather than one market’s neighbours.

Terminal window
# Browse clusters. Note: filters are category / hasDiscrepancy / sort — there is no
# text search on this route. Find the market first with fetchMarkets, then compare.
curl -s "$PREDICTEFY_API_URL/v1/clusters?hasDiscrepancy=true&limit=20" \
-H "Authorization: Bearer pk_live_YOUR_KEY"
# Expand one cluster into its members.
curl -s "$PREDICTEFY_API_URL/v1/clusters/CLUSTER_ID" \
-H "Authorization: Bearer pk_live_YOUR_KEY"

A cluster carries question, normalizedQuestion, category, venueCount, venues, hasDiscrepancy, aggAvgYes, aggSpread, maxSpread, similarity, closeDate and asOf. Expanding it adds members, each with marketPk, venue, yesPrice, noPrice, volume24h, liquidity and its own similarity.

/v1/discrepancies lists clusters that currently disagree on price. It filters on category and livenot by cluster id — and each row is a spread with its two ends:

{
"clusterId": "clr_9f2a7c41",
"question": "Will the Fed cut rates at the January 2026 meeting?",
"venues": ["kalshi", "polymarket"],
"spread": 0.023,
"low": { "venue": "kalshi", "canonicalMarketId": "kalshi:FED-26JAN-CUT", "yesPrice": 0.415 },
"high": { "venue": "polymarket", "canonicalMarketId": "polymarket:0x7d3f", "yesPrice": 0.438 },
"similarity": 0.94,
"matchProbability": 0.97,
"label": "indicative price discrepancy",
"asOf": "2026-08-12T14:31:04.901Z"
}

label has exactly one permitted value here — indicative price discrepancy. That is not hedging: this route makes no claim about depth, fees, or resolution equivalence, so it cannot call anything executable.

To ask whether one of these survives those checks, qualify it:

Terminal window
curl -s "$PREDICTEFY_API_URL/v1/discrepancies/CLUSTER_ID/qualification?size=1000" \
-H "Authorization: Bearer pk_live_YOUR_KEY"

That route takes size and an optional venues filter. See the scanner recipe for what qualification actually tests.

  • similarity is a match score, never a confidence. A 0.94 pair can still settle differently. matchProbability is a separate field and a separate claim.
  • Render asOf next to every price. Presenting a snapshot as live is the most common way a comparison UI misleads the person reading it.
  • Label the book type per row. Some venues serve reconstructed books — faithful as price, not executable as depth. Venue coverage records which venues have a real book; read it from there rather than hard-coding a list that will go stale.
  • Handle NOT_SUPPORTED per row, not per request. One venue that cannot serve a book should not blank the table. See Capability-honest data.
  • page.total may be null — that means not counted, not zero. Paginate on hasMore and nextCursor.

Cluster lookups and cross-venue comparisons are priced above catalog reads because each one walks stored relationship data. A fresh AI-assisted match, for a market with no existing cluster, costs considerably more than reusing one — so cache the cluster id, not the prices. Current weights are in Credits & billing.