MarketMaster Data API

Build on prediction market data

Live edges, whale trades, and cross-platform arbitrage — over REST or real-time WebSocket streaming. Free tier included. No wallet connect required.

REST API

Six endpoints: edges, markets, arbitrage, whales, and status. Simple GET requests, JSON responses, CORS-enabled for browser apps.

📡

WebSocket Streaming

Real-time push over wss:// — new edges and whale trades delivered the moment they’re detected. No polling.

🧰

Official SDKs

Python and JavaScript clients with auth, retries, and type hints pre-packaged. Or bring your own HTTP client — it’s just JSON.

# Fetch top mispriced markets across both venues
curl "https://api.marketmaster.live/v1/edges?limit=5" \
  -H "x-api-key: mmk_live_your_key_here"

# Response
{
  "count": 5,
  "edges": [
    {
      "platform": "kalshi",
      "market_title": "Will a Democrat win in 2028?",
      "price": 0.43, "fair_value": 0.51, "edge_pct": 8.0
    }
  ]
}
// Subscribe to live edge updates
const ws = new WebSocket(
  "wss://api.marketmaster.live/api/v1/stream?api_key=mmk_live_..."
);

ws.onopen = () => {
  ws.send(JSON.stringify({ action: "subscribe", channel: "edges" }));
  ws.send(JSON.stringify({ action: "subscribe", channel: "whales" }));
};

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  // { type: "event", channel: "edges", data: { count: 34, ts: ... } }
  console.log(`[${msg.channel}]`, msg.data);
};
import requests

client = requests.Session()
client.headers["x-api-key"] = "mmk_live_your_key_here"

# Top edges, Kalshi only
edges = client.get(
    "https://api.marketmaster.live/v1/edges",
    params={"platform": "kalshi", "limit": 10}
).json()["edges"]

# Arbitrage opportunities (gross of fees)
arbs = client.get(
    "https://api.marketmaster.live/v1/arbitrage",
    params={"min_spread": 3}
).json()["opportunities"]

Introduction

The MarketMaster API gives you programmatic access to our cross-platform prediction market engine: edges (mispriced markets ranked by edge size), markets (normalized snapshots across venues), a single-market lookup, cross-platform arbitrage spreads, the whale trade feed, and a real-time WebSocket stream. Every REST endpoint is a simple GET that returns JSON.

The API is read-only. It never places trades, moves funds, or exposes any individual user’s data — only public market data.
REST · JSON WebSocket streaming API-key auth CORS-enabled v1

Quickstart

1. Generate a key on your dashboard (Developer API card). Copy it — it’s shown only once.
2. Send it in the x-api-key header.
3. Call an endpoint.

curl
curl "https://api.marketmaster.live/v1/edges?limit=5" \
  -H "x-api-key: mmk_live_your_key_here" \
  -H "User-Agent: my-app/1.0"

SDKs & MCP

Official clients with auth, retries, rate-limit handling and a proper User-Agent built in. Or skip code entirely and plug the data into Claude or Cursor via MCP.

JavaScript / TypeScript
npm install @marketmaster/sdk
import { MarketMaster } from "@marketmaster/sdk";

const mm = new MarketMaster({ apiKey: process.env.MM_API_KEY });
const { edges } = await mm.edges({ platform: "kalshi", min_edge: 5 });
Python
pip install marketmaster
from marketmaster import MarketMaster

mm = MarketMaster(api_key="mmk_live_your_key")
edges = mm.edges(platform="kalshi", min_edge=5)["edges"]
MCP — Claude Desktop / Cursor

Six read-only tools (mm_edges, mm_markets, mm_market, mm_arbitrage, mm_whales, mm_status) via npx — no install. Add to claude_desktop_config.json (or ~/.cursor/mcp.json):

{
  "mcpServers": {
    "marketmaster": {
      "command": "npx",
      "args": ["-y", "@marketmaster/mcp"],
      "env": { "MARKETMASTER_API_KEY": "mmk_live_your_key" }
    }
  }
}

Authentication

Authenticate every request with your API key in the x-api-key header. Keys look like mmk_live_… and are managed from your dashboard. A key is shown once at creation; store it in an environment variable, never commit it to source control, and rotate it from the dashboard if it’s ever exposed.

Treat your key like a password. Requests without a valid key return 401.
Always send a descriptive User-Agent header. Requests from default library agents (e.g. python-urllib) can be rejected by our edge bot protection. Our official SDKs set one automatically.

Conventions

Base URL

https://api.marketmaster.live
FormatAll requests and responses are JSON.
PricesProbabilities in 0.0–1.0 (e.g. 0.43 = 43¢ / 43% implied chance).
TimestampsISO 8601, UTC (e.g. 2026-06-20T21:25:12Z).
Venueskalshi, polymarket.
CORSEnabled for all origins — call directly from a browser app.
PaginationList endpoints accept limit and offset; response includes has_more.
CachingData endpoints send Cache-Control: public, max-age=30.

Pricing

All endpoints — including arbitrage, edges, whale feed, and WebSocket streaming — are available on every tier.

Free
$0/mo
60 req/min · 1,000 req/mo
  • All 6 REST endpoints
  • Edges, arb & whale feed
  • WebSocket: 1 connection
  • 1 API key
Get started free
API Pro
$29.99/mo
300 req/min · 1,000,000 req/mo
  • Everything in Starter
  • 5× rate limit
  • WebSocket: 10 connections
  • For high-frequency pipelines

MarketMaster Pro ($12.99/mo consumer sub) also includes API access at 240 req/min · 200k req/mo, WebSocket: 5 connections, plus scanner, alerts & overlay.

Why MarketMaster vs alternatives?

FeatureMarketMasterCompetitors
Arbitrage feed✅ All tiersEnterprise only
EV / edge rankings✅ All tiersEnterprise only
Whale trade feed✅ All tiersEnterprise only
WebSocket streaming✅ All tiersEnterprise only
Entry priceFree forever$49+/mo to start
Kalshi + Polymarket + more✅ UnifiedSingle-venue

Rate limits

Limits are enforced per account across two windows: a per-minute rate and a monthly quota. Limits survive key rotation.

TierRateMonthly quotaWS connections
free60 req / min1,0001
api_starter120 req / min50,0003
pro (consumer)240 req / min200,0005
api_pro300 req / min1,000,00010

Rate-limit response headers

X-RateLimit-Limit-MinuteYour per-minute ceiling.
X-RateLimit-Remaining-MinuteRequests left this minute.
X-RateLimit-Limit-MonthYour monthly quota.
X-RateLimit-Remaining-MonthRequests left this month.

Exceeding the per-minute rate returns 429 rate_limited; exhausting the monthly quota returns 429 quota_exceeded. Both include a Retry-After header in seconds.

Edges

GET/v1/edges

The most mispriced markets right now — our model’s fair value vs. the live price, ranked by edge size. Returns one row per market, deduplicated to the latest reading.

Query parameters

ParameterTypeDescription
platformstringFilter by venue: kalshi, polymarket.
categorystringFilter by category, e.g. politics, sports, crypto.
limitintegerMax rows. Default 50, max 200.
offsetintegerRows to skip. Default 0.
min_edgenumberMinimum edge in percentage points.

Response: edges[]

FieldTypeDescription
platformstringVenue.
market_idstringVenue’s native market identifier.
market_titlestringMarket question.
market_categorystringCategory (e.g. politics).
outcomestringYES or NO.
pricenumberLive market price (0–1).
fair_valuenumberModel’s estimated fair probability (0–1).
edge_pctnumberEdge in percentage points. Larger = more mispriced.
confidencenumberModel confidence (0–1).
match_group_idintegerCross-venue match group ID. Markets sharing this ID are the same real-world event on different venues.
expires_atstringWhen the market closes (ISO 8601).
computed_atstringWhen this edge was computed (ISO 8601).

Example

curl
curl "https://api.marketmaster.live/v1/edges?platform=kalshi&limit=2" \
  -H "x-api-key: mmk_live_your_key_here"
JavaScript
const res = await fetch(
  "https://api.marketmaster.live/v1/edges?platform=kalshi&limit=2",
  { headers: { "x-api-key": process.env.MM_API_KEY } }
);
const { edges } = await res.json();
Python
import requests
r = requests.get(
    "https://api.marketmaster.live/v1/edges",
    params={"platform": "kalshi", "limit": 2},
    headers={"x-api-key": MM_API_KEY},
)
edges = r.json()["edges"]
Response
{
  "count": 2,
  "edges": [{
    "platform": "kalshi", "market_id": "PRES-2028-DEM",
    "market_title": "Will a Democrat win the 2028 election?",
    "outcome": "YES", "price": 0.43, "fair_value": 0.51,
    "edge_pct": 8.0, "confidence": 0.62,
    "expires_at": "2028-11-07T05:00:00Z", "computed_at": "2026-06-20T21:25:12Z"
  }],
  "generated_at": "2026-06-20T21:25:34Z"
}

Markets

GET/v1/markets

The latest snapshot of all tracked markets across both venues — title, category, YES/NO price, 24h volume, and close time. Sorted by 24h volume (most active first).

Query parameters

ParameterTypeDescription
sourcestringFilter by venue (alias: platform).
categorystringFilter by category.
limitintegerMax rows. Default 100, max 500.
offsetintegerRows to skip. Default 0.

Response: markets[]

FieldTypeDescription
sourcestringVenue.
source_market_idstringVenue’s native market id.
titlestringMarket question.
categorystringCategory.
yes_pricenumberCurrent YES price (0–1).
no_pricenumberCurrent NO price (0–1).
volume_24h_usdnumber | null24h traded volume in USD.
close_timestring | nullMarket close time (ISO 8601).
last_trade_atstring | nullLast trade timestamp (ISO 8601).
fetched_atstringWhen we last snapshotted this market (ISO 8601).
curl
curl "https://api.marketmaster.live/v1/markets?category=politics&limit=50" \
  -H "x-api-key: mmk_live_your_key_here"

Market

GET/v1/market

A single market’s latest snapshot plus any live edges computed for it. Identify by source + id.

Query parameters

ParameterTypeDescription
sourcestring · requiredVenue: kalshi, polymarket.
idstring · requiredVenue’s native market id.

Returns market (same shape as markets[]) and edges[] (same shape as edges[], most recent first). Unknown id returns 404 not_found.

curl
curl "https://api.marketmaster.live/v1/market?source=kalshi&id=PRES-2028-DEM" \
  -H "x-api-key: mmk_live_your_key_here"

Arbitrage

GET/v1/arbitrage

Cross-platform price spreads for the same outcome on matched markets, ranked widest first.

Spreads are indicative and gross of fees, slippage, and bid/ask depth — not guaranteed arbitrage. Always confirm executable prices on the venue.

Query parameters

ParameterTypeDescription
min_spreadnumberMinimum spread in percentage points.
limitintegerMax rows. Default 50, max 200.
offsetintegerRows to skip. Default 0.

Response: opportunities[]

FieldTypeDescription
match_group_idintegerCross-venue match group.
titlestringMarket question.
outcomestringOutcome being compared (YES/NO).
spread_pctnumberDearest minus cheapest price, in percentage points.
buy_yesobjectCheapest venue: { platform, market_id, price }.
sell_yesobjectDearest venue: { platform, market_id, price }.
computed_atstringWhen these prices were computed (ISO 8601).
curl
curl "https://api.marketmaster.live/v1/arbitrage?min_spread=2&limit=10" \
  -H "x-api-key: mmk_live_your_key_here"

Whales

GET/v1/whales

Recent large real-money trades across venues, newest first.

Query parameters

ParameterTypeDescription
sourcestringFilter by venue.
minintegerMinimum trade size in USD.
limitintegerMax rows. Default 50, max 200.
offsetintegerRows to skip. Default 0.

Response: trades[]

FieldTypeDescription
sourcestringVenue.
source_market_idstringVenue’s native market id.
trader_namestring | nullPublic trader handle, when available.
sidestringTrade side (buy/sell).
outcomestringOutcome traded.
size_usdnumberNotional size in USD.
pricenumberTrade price (0–1).
titlestringMarket question.
trade_timestringWhen the trade printed (ISO 8601).
tx_hashstring | nullOn-chain tx hash for on-chain venues.
curl
curl "https://api.marketmaster.live/v1/whales?min=10000&limit=20" \
  -H "x-api-key: mmk_live_your_key_here"

Status

GET/v1/status

Returns your key’s tier, limits, and current usage. Use to monitor remaining quota.

Response
{
  "ok": true,
  "tier": "free",
  "limits": { "minute": 60, "month": 1000 },
  "usage":  { "minute": 3,  "month": 412 }
}

WebSocket Streaming

Connect once and receive pushed events the moment they’re ready — no polling. The stream pushes edge batches and whale trade notifications from our ingest pipeline as each batch is produced.

WSS /api/v1/stream Real-time

Connection URL

wss://api.marketmaster.live/api/v1/stream

Authentication

Your API key must be sent at connection time. Two options depending on environment:

EnvironmentHow to authenticate
Node.js / Python / serverPass x-api-key: mmk_live_... in the WebSocket upgrade request headers.
BrowserAppend ?api_key=mmk_live_... as a query parameter — browsers cannot set custom headers on WebSocket connections.
Never expose your API key in public client-side browser code. Use a short-lived token or a server-side relay for browser deployments.

Channels

edges
Pushed every ~10 min
New edge batch computed. Fetch /v1/edges on receipt for the updated list.
whales
Pushed every ~5 min
New whale trades ingested. Fetch /v1/whales on receipt for the latest fills.
prices/*
Reserved
Per-market price streaming — coming soon.
The stream sends a lightweight notification (count + timestamp) rather than the full payload. Pull the relevant REST endpoint on receipt to get the data — this keeps stream payloads small and lets you filter before fetching.

Client → server messages (send as JSON)

ActionPayloadEffect
subscribe{"action":"subscribe","channel":"edges"}Start receiving events for this channel.
unsubscribe{"action":"unsubscribe","channel":"edges"}Stop receiving events for this channel.
ping{"action":"ping"}Server replies with pong. Use to keep the connection alive.

Server → client messages (receive as JSON)

TypeExample payloadWhen
welcome{"type":"welcome","tier":"free","conn_id":"abc"}Immediately on connect.
subscribed{"type":"subscribed","channel":"edges"}After a successful subscribe.
event{"type":"event","channel":"edges","data":{"count":34,"ts":1751000000000}}New data available for a subscribed channel.
pong{"type":"pong"}Reply to your ping.
error{"type":"error","code":"too_many_connections","message":"..."}Auth failure, connection limit exceeded, or invalid message.

Connection limits by tier

TierMax concurrent connectionsMax channel subscriptions / connection
free15
api_starter320
pro (consumer)550
api_pro10100

Example — browser

JavaScript (browser)
// Pass key as query param; browsers can't set WebSocket headers
const ws = new WebSocket(
  `wss://api.marketmaster.live/api/v1/stream?api_key=${MM_API_KEY}`
);

ws.onopen = () => {
  ws.send(JSON.stringify({ action: "subscribe", channel: "edges"  }));
  ws.send(JSON.stringify({ action: "subscribe", channel: "whales" }));
};

ws.onmessage = async (e) => {
  const msg = JSON.parse(e.data);
  if (msg.type === "event" && msg.channel === "edges") {
    // New edge batch — pull fresh data
    const { edges } = await fetch("/v1/edges?limit=20", {
      headers: { "x-api-key": MM_API_KEY }
    }).then(r => r.json());
    renderEdges(edges);
  }
};

// Keep-alive ping every 30s
setInterval(() => ws.send(JSON.stringify({ action: "ping" })), 30_000);

Example — Node.js

JavaScript (Node.js / ws library)
import WebSocket from "ws";

const ws = new WebSocket("wss://api.marketmaster.live/api/v1/stream", {
  headers: { "x-api-key": process.env.MM_API_KEY },
});

ws.on("open", () => {
  ws.send(JSON.stringify({ action: "subscribe", channel: "edges"  }));
  ws.send(JSON.stringify({ action: "subscribe", channel: "whales" }));
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw);
  console.log(msg.type, msg.channel ?? "", msg.data ?? "");
});

Example — Python

Python (websockets library)
import asyncio, json, websockets

async def stream():
    uri = "wss://api.marketmaster.live/api/v1/stream"
    async with websockets.connect(uri, extra_headers={"x-api-key": MM_API_KEY}) as ws:
        await ws.send(json.dumps({"action": "subscribe", "channel": "edges"}))
        await ws.send(json.dumps({"action": "subscribe", "channel": "whales"}))
        async for raw in ws:
            msg = json.loads(raw)
            print(msg["type"], msg.get("channel"), msg.get("data"))

asyncio.run(stream())

Errors

Errors return the appropriate HTTP status with a JSON envelope:

{ "error": { "code": "rate_limited", "message": "Per-minute rate limit exceeded." } }
StatusCodeMeaning
401missing_api_keyNo x-api-key header sent.
401invalid_api_keyKey is malformed, unknown, or revoked.
400invalid_parameterQuery parameter invalid or required param missing.
404not_foundNo resource matched (e.g. unknown market id).
426websocket_required/api/v1/stream must be connected via WebSocket.
429rate_limitedPer-minute rate exceeded — check Retry-After header.
429quota_exceededMonthly quota exhausted — resets at month start.
500auth_errorTemporary problem validating the key — retry.
502upstream_errorTransient data load problem — retry.
503streaming_unavailableWebSocket streaming temporarily unavailable.

WebSocket error frames use the same code / message shape, delivered as a JSON message before the server closes the connection.

Versioning & changes

The API is versioned in the path (/v1/). We may add new fields to responses and new channels to the stream at any time — write clients that ignore unknown fields and message types. Breaking changes would ship under a new version path. This is an early release; endpoints, limits, and stream channels may still evolve.

Disclaimer

Edge and fair-value figures are model estimates for informational purposes only. Not trading advice, not a guarantee of profit. Data provided as-is without warranty. You are responsible for complying with the terms and applicable laws of any venue you trade on.