The Prediction Markets
Concepts

Conventions: prices, freshness and pagination

Every v1 endpoint follows the same rules. Learn them once here.

Base URL and versions

All REST endpoints live under https://api.thepredictionmarkets.com/api/public/v1 and use GET. Responses are JSON (UTF-8).

v1 only grows: new fields and endpoints may appear, so ignore fields you do not know. Nothing is renamed or removed. A breaking change would ship as /api/public/v2, with notice.

Prices are probabilities

Every price is a number from 0 to 1: the cost in dollars of a contract that pays $1, which is also the implied chance. 0.62 means 62%. Kalshi's cents and Polymarket's decimals are both converted to this scale.

Missing values are null

When a number is unknown it is null, never 0. A best_bid of null means there is no bid; 0 would mean a bid of zero. Check for null before doing math.

Timestamps

Timestamps are ISO 8601 strings in UTC, e.g. 2026-10-21T08:37:22Z. Ages such as age_seconds are whole seconds.

Freshness

Every quote says how current it is in quote.freshness. Use it to decide whether to show, grey out or skip a price.

ValueMeaningSuggested use
liveStreaming from the venue's WebSocket and under 15 seconds old (or a quiet order book that a healthy feed confirms).Show as live.
freshPolled from the venue's REST API less than 10 minutes ago.Show normally.
agingBetween 10 minutes and 2 hours old, or the live feed is degraded.Show with a timestamp.
staleOlder than that.Do not trade on it; grey it out.
unknownTPM has no timestamp for this price.Treat like stale.

Rows from /events carry a different object, freshness: {state, age_seconds, observed_at}, about the event listing rather than one quote. Its state is fresh, aging, stale or unknown.

Pagination

List endpoints return {data, pagination}. pagination is {limit, has_more, next_cursor}. To get the next page, send next_cursor as cursor with the same filters. Stop when has_more is false.

Response shape
{
  "data": [
    "…"
  ],
  "pagination": {
    "limit": 50,
    "has_more": true,
    "next_cursor": "eyJvIjo1MH0"
  }
}
Read every page
import os

import requests

BASE = "https://api.thepredictionmarkets.com/api/public/v1"
HEADERS = {"X-API-Key": os.environ["TPM_API_KEY"]}

params = {"category": "politics", "limit": 200}
rows = []
while True:
    body = requests.get(f"{BASE}/events", params=params, headers=HEADERS, timeout=20).json()
    rows += body["data"]
    if not body["pagination"]["has_more"]:
        break
    params["cursor"] = body["pagination"]["next_cursor"]

print(len(rows), "events")

Short lists that always fit on one page, such as /categories and /spreads, return pagination: null.

Request ids

Every response has an X-Request-ID header, and every error body repeats it as error.request_id. Include it when you contact support. You may send your own X-Request-ID (up to 128 printable characters) to trace a call end to end.

Errors and limits

Errors share one shape: {"error": {"code", "message", "request_id"}}. See errors for every code and rate limits for the headers.

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.