The Prediction Markets
Guide

Compare one event across Kalshi and Polymarket

Build a side-by-side table: for each answer, the Kalshi price, the Polymarket price, how fresh each is, and the gap. Two API calls.

Steps

  1. Find the event with /events?q=… and take its slug.
  2. Fetch it with /events/{slug}.
  3. Group outcomes from every market by match.group_id. Outcomes with match: null have no partner on the other venue.
  4. For each group with both venues, show each quote.probability and quote.freshness.
  5. Use spreads[] (joined on match.key) for the gap. Check comparison and match.contract_compatibility before calling two prices the same bet.

Code

Side-by-side table
import os
from collections import defaultdict

import requests

BASE = "https://api.thepredictionmarkets.com/api/public/v1"
HEADERS = {"X-API-Key": os.environ.get("TPM_API_KEY", "tpm_demo_key_authorized_2026")}


def get(path, **params):
    resp = requests.get(BASE + path, params=params, headers=HEADERS, timeout=20)
    resp.raise_for_status()
    return resp.json()["data"]


slug = get("/events", q="democratic nominee 2028", limit=1)[0]["slug"]
event = get(f"/events/{slug}")
print(event["title"], "| comparison:", event["comparison"])

rows = defaultdict(dict)  # group_id -> {"title", "key", "kalshi", "polymarket"}
for market in event["markets"]:
    for outcome in market["outcomes"] or []:
        match = outcome.get("match") or {}
        if not match.get("group_id"):
            continue  # no partner on the other venue
        row = rows[match["group_id"]]
        row.update(title=outcome["title"], key=match["key"])
        row[market["venue"]] = outcome["quote"]

gaps = {s["outcome_key"]: s["gap"] for s in event["spreads"]}


def fmt(q):
    return f"{q['probability']:.3f} ({q['freshness']})" if q["probability"] is not None else "-"


for row in rows.values():
    if "kalshi" in row and "polymarket" in row:
        print(f"{row['title']:<28} kalshi {fmt(row['kalshi']):<16} polymarket {fmt(row['polymarket']):<16} gap {gaps.get(row['key'])}")

Things to watch

  • Venues often list different numbers of outcomes. Show unmatched ones separately rather than dropping them silently.
  • Skip or grey out quotes whose freshness is stale or unknown.
  • comparison: "comparable" means the venues ask a similar but not identical question. Show the resolution.summary and each market's rules (from /markets/{venue}/{market_id}).
  • A positive gap is gross: before fees and depth. It is a lead, not a guaranteed profit.

Build with TPM

Get your free API key

One key gives you Kalshi and Polymarket events, prices with freshness, price history, cross-venue gaps and the live quotes WebSocket.