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.
| Value | Meaning | Suggested use |
|---|---|---|
live | Streaming from the venue's WebSocket and under 15 seconds old (or a quiet order book that a healthy feed confirms). | Show as live. |
fresh | Polled from the venue's REST API less than 10 minutes ago. | Show normally. |
aging | Between 10 minutes and 2 hours old, or the live feed is degraded. | Show with a timestamp. |
stale | Older than that. | Do not trade on it; grey it out. |
unknown | TPM 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.
{
"data": [
"…"
],
"pagination": {
"limit": 50,
"has_more": true,
"next_cursor": "eyJvIjo1MH0"
}
}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.
Related
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.