API Documentation

REST and WebSocket API for the PMDI prediction market data platform. Current version: v1

Authentication

All API requests (except /public/*) require a valid API key. Pass your key in the X-API-Key request header.

API keys are issued per buyer account and are tied to your subscription tier. Keys can be rotated at any time from the dashboard. Never embed your API key in client-side code.

# Include in every authenticated request X-API-Key: pmdi_live_xxxxxxxxxxxxxxxxxxxx

Keys beginning with pmdi_live_ are production keys. pmdi_test_ keys are sandbox-only and do not count against rate limits.

Base URL

https://api.pmdi.io/v1

All endpoint paths below are relative to this base URL. The API uses HTTPS exclusively. HTTP requests are redirected.

Rate Limits

Limits are enforced per API key on a rolling 60-second window. Exceeding the limit returns HTTP 429 with a Retry-After header indicating seconds until the window resets.

Tier Requests per minute Burst (5s) WebSocket connections
Trial 5 10 Not available
Pro 300 600 1 shared
Institutional Unlimited Unlimited 1 private endpoint

Response headers include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp).

Error Codes

HTTP Status Error Code Description
400invalid_paramsQuery parameters failed validation
401unauthorizedMissing or invalid API key
403forbiddenEndpoint not available on your tier
404not_foundMarket ID or resource not found
429rate_limitedRate limit exceeded. See Retry-After header
500server_errorInternal error. Logged to data_quality_log automatically
503unavailableIngestor or DB temporarily unavailable

Endpoint Reference

Markets

Query active and historical markets tracked by PMDI across Polymarket and Kalshi.

GET /markets List markets

Returns a paginated list of markets. By default returns active markets only.

ParameterTypeDescription
platformstringFilter by polymarket or kalshi
categorystringFilter by category (e.g. crypto, politics)
is_activebooleanDefault true
limitintegerMax 500, default 100
offsetintegerPagination offset, default 0
GET /markets/{market_id} Get market detail
Returns full metadata for a single market including resolution status, volume, and tags.

Price Ticks

Raw tick-level price data. Hypertable partitioned by time. Subject to historical depth limits by tier.

GET /markets/{market_id}/ticks Price tick series

Returns time-ordered price tick series for a given market.

ParameterTypeDescription
fromISO 8601Start of time range. Required.
toISO 8601End of time range. Defaults to now.
resolutionstringraw, 1m, 5m, 1h. Default raw.
limitintegerMax 10,000 per request

Signals

Computed probability velocity and anomaly signals. Requires Pro or Institutional tier.

GET /signals List recent signals

Returns signals sorted by computed timestamp descending.

ParameterTypeDescription
market_idstringFilter by specific market
anomaly_onlybooleanReturn only anomaly-flagged signals
windowintegerWindow seconds: 60, 300, 900, 3600
sinceISO 8601Return signals since timestamp
limitintegerMax 1,000
GET /markets/{market_id}/signals Signals for a market
Returns all probability velocity signals for the specified market, across all windows.

Correlations

Cross-asset correlation data between prediction market probabilities and asset prices. Pro and Institutional only.

GET /correlations Cross-asset correlations

Returns correlation records between market probabilities and tracked assets (BTC, ETH, 10Y Treasury, SPX).

ParameterTypeDescription
market_idstringRequired. Market to query.
assetstringFilter by asset: BTC, ETH, SPX, US10Y
window_daysinteger7, 30, or 90

Insights (Annotations)

Claude-annotated signal narratives enriched with news context. Pro and Institutional only.

GET /insights List annotated insights

Returns recent Claude-annotated insights sorted by confidence score descending.

ParameterTypeDescription
min_confidencefloatMinimum confidence score 0.0–1.0. Default 0.7
trigger_typestringvelocity_anomaly, correlation_spike, volume_surge
limitintegerMax 100
GET /insights/rss RSS feed (public)
Returns an RSS 2.0 feed of recent high-confidence insights. Public endpoint — no API key required.

WebSocket Feed

Real-time stream of price ticks, signals, and insights. Pro and Institutional only. Authenticate via query parameter on the initial connection.

WS wss://api.pmdi.io/v1/feed?key=YOUR_API_KEY

After connecting, send a JSON subscription message to select channels:

// Subscribe to specific channels { "type": "subscribe", "channels": ["ticks", "signals", "insights"], "market_ids": ["KXBTCD-25MAY30-T102000"] // optional filter }

The server sends a heartbeat every 30 seconds. If no message is received for 60 seconds, the connection should be considered stale and reconnected.

Public Stats

No authentication required.

GET /public/stats System stats
Returns classified_markets, ticks_today, signals_today, and uptime_percent. Updated every 60 seconds.

Python Examples

Fetch active markets

import httpx API_KEY = "pmdi_live_xxxxxxxxxxxxxxxxxxxx" BASE_URL = "https://api.pmdi.io/v1" HEADERS = {"X-API-Key": API_KEY} with httpx.Client() as client: resp = client.get( f"{BASE_URL}/markets", headers=HEADERS, params={"platform": "kalshi", "limit": 50}, ) resp.raise_for_status() markets = resp.json() print(f"Fetched {len(markets['items'])} markets")

Fetch recent anomaly signals

import httpx from datetime import datetime, timedelta, timezone # Signals from the last hour, anomalies only since = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat() with httpx.Client() as client: resp = client.get( f"{BASE_URL}/signals", headers=HEADERS, params={ "anomaly_only": True, "since": since, "window": 300, }, ) resp.raise_for_status() for signal in resp.json()["signals"]: print(signal["market_id"], signal["velocity_score"])

Subscribe to WebSocket feed (async)

import asyncio import json import websockets API_KEY = "pmdi_live_xxxxxxxxxxxxxxxxxxxx" WS_URL = f"wss://api.pmdi.io/v1/feed?key={API_KEY}" async def stream(): async with websockets.connect(WS_URL) as ws: # Subscribe to signals and insights await ws.send(json.dumps({ "type": "subscribe", "channels": ["signals", "insights"], })) async for message in ws: event = json.loads(message) if event["type"] == "signal": print("Signal:", event["market_id"], event["velocity_score"]) elif event["type"] == "insight": print("Insight:", event["narrative"][:80]) asyncio.run(stream())

cURL Examples

List markets

curl -s https://api.pmdi.io/v1/markets \ -H "X-API-Key: pmdi_live_xxxxxxxxxxxxxxxxxxxx" \ -G \ --data-urlencode "platform=polymarket" \ --data-urlencode "limit=20" \ | python3 -m json.tool

Fetch price ticks for a market

curl -s "https://api.pmdi.io/v1/markets/KXBTCD-25MAY30-T102000/ticks" \ -H "X-API-Key: pmdi_live_xxxxxxxxxxxxxxxxxxxx" \ -G \ --data-urlencode "from=2026-05-01T00:00:00Z" \ --data-urlencode "resolution=5m" \ --data-urlencode "limit=288"

Get latest high-confidence insights

curl -s "https://api.pmdi.io/v1/insights?min_confidence=0.85&limit=10" \ -H "X-API-Key: pmdi_live_xxxxxxxxxxxxxxxxxxxx"

Check public system stats (no auth)

curl -s https://api.pmdi.io/v1/public/stats | python3 -m json.tool