MarketMaster
Dữ liệu thị trường dự đoán Kalshi và Polymarket chỉ đọc: lợi thế EV, chênh lệch giá giữa các sàn, ảnh chụp nhanh thị trường và giao dịch cá voi.
Tài liệu
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 MCP server 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" }
}
}
}
MCP — hosted endpoint (nothing to install)
The same six tools are also served from a hosted Streamable HTTP MCP endpoint, for clients that connect to a URL rather than spawning a local process. Authenticate with the same key as a bearer token.
https://api.marketmaster.live/mcp
{
"mcpServers": {
"marketmaster": {
"url": "https://api.marketmaster.live/mcp",
"headers": { "Authorization": "Bearer mmk_live_your_key" }
}
}
}
Use npx above if you want the server running locally; use the hosted endpoint if your client only takes a URL, or you would rather not run a process. Both expose identical tools and count against the same quota.
MarketMaster is listed in the official MCP Registry as live.marketmaster/mcp and on Smithery. A machine-readable description of the server is published at https://api.marketmaster.live/.well-known/mcp/server-card.json.
Every MCP tool is annotated readOnlyHint: true. An agent connected to MarketMaster can read market data and nothing else — it cannot place a trade, move funds, or reach another user’s account.
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
| Format | All requests and responses are JSON. |
|---|---|
| Prices | Probabilities in 0.0–1.0 (e.g. 0.43 = 43¢ / 43% implied chance). |
| Timestamps | ISO 8601, UTC (e.g. 2026-06-20T21:25:12Z). |
| Venues | kalshi, polymarket. |
| CORS | Enabled for all origins — call directly from a browser app. |
| Pagination | List endpoints accept limit and offset; response includes has_more. |
| Caching | Data 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
Most popular
API Starter
$9.99/mo
120 req/min · 50,000 req/mo
- Everything in Free
- 2× rate limit
- WebSocket: 3 connections
- No consumer app required
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?
| Feature | MarketMaster | Competitors |
|---|---|---|
| Arbitrage feed | ✅ All tiers | Enterprise only |
| EV / edge rankings | ✅ All tiers | Enterprise only |
| Whale trade feed | ✅ All tiers | Enterprise only |
| WebSocket streaming | ✅ All tiers | Enterprise only |
| Entry price | Free forever | $49+/mo to start |
| Kalshi + Polymarket + more | ✅ Unified | Single-venue |
Rate limits
Limits are enforced per account across two windows: a per-minute rate and a monthly quota. Limits survive key rotation.
| Tier | Rate | Monthly quota | WS connections |
|---|---|---|---|
free | 60 req / min | 1,000 | 1 |
api_starter | 120 req / min | 50,000 | 3 |
pro (consumer) | 240 req / min | 200,000 | 5 |
api_pro | 300 req / min | 1,000,000 | 10 |
Rate-limit response headers
X-RateLimit-Limit-Minute | Your per-minute ceiling. |
|---|---|
X-RateLimit-Remaining-Minute | Requests left this minute. |
X-RateLimit-Limit-Month | Your monthly quota. |
X-RateLimit-Remaining-Month | Requests 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
| Parameter | Type | Description |
|---|---|---|
platform | string | Filter by venue: kalshi, polymarket. |
category | string | Filter by category, e.g. politics, sports, crypto. |
limit | integer | Max rows. Default 50, max 200. |
offset | integer | Rows to skip. Default 0. |
min_edge | number | Minimum edge in percentage points. |
Response: edges[]
| Field | Type | Description |
|---|---|---|
platform | string | Venue. |
market_id | string | Venue’s native market identifier. |
market_title | string | Market question. |
market_category | string | Category (e.g. politics). |
outcome | string | YES or NO. |
price | number | Live market price for the side named by outcome (0–1). On a NO row this is the NO price, not the YES price. |
fair_value | number | Model’s estimated fair probability for the side named by outcome (0–1). Every price field on a row — price, fair_value, ask — is quoted on that same side, so fair_value − price is always the edge in your favour. |
edge_pct | number | Edge in percentage points. Larger = more mispriced. |
confidence | number | Model confidence (0–1). |
match_group_id | integer | Cross-venue match group ID. Markets sharing this ID are the same real-world event on different venues. |
expires_at | string | When the market closes (ISO 8601). |
computed_at | string | When 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
| Parameter | Type | Description |
|---|---|---|
source | string | Filter by venue (alias: platform). |
category | string | Filter by category. |
limit | integer | Max rows. Default 100, max 500. |
offset | integer | Rows to skip. Default 0. |
Response: markets[]
| Field | Type | Description |
|---|---|---|
source | string | Venue. |
source_market_id | string | Venue’s native market id. |
title | string | Market question. |
category | string | Category. |
yes_price | number | Current YES price (0–1). |
no_price | number | Current NO price (0–1). |
volume_24h_usd | number | null | 24h traded volume in USD. |
close_time | string | null | Market close time (ISO 8601). |
last_trade_at | string | null | Last trade timestamp (ISO 8601). |
fetched_at | string | When 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
| Parameter | Type | Description |
|---|---|---|
source | string · required | Venue: kalshi, polymarket. |
id | string · required | Venue’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
| Parameter | Type | Description |
|---|---|---|
min_spread | number | Minimum spread in percentage points. |
limit | integer | Max rows. Default 50, max 200. |
offset | integer | Rows to skip. Default 0. |
Response: opportunities[]
| Field | Type | Description |
|---|---|---|
match_group_id | integer | Cross-venue match group. |
title | string | Market question. |
outcome | string | Outcome being compared (YES / NO). |
spread_pct | number | Dearest minus cheapest price, in percentage points. |
buy_yes | object | Cheapest venue: { platform, market_id, price }. |
sell_yes | object | Dearest venue: { platform, market_id, price }. |
computed_at | string | When 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
| Parameter | Type | Description |
|---|---|---|
source | string | Filter by venue. |
min | integer | Minimum trade size in USD. |
limit | integer | Max rows. Default 50, max 200. |
offset | integer | Rows to skip. Default 0. |
Response: trades[]
| Field | Type | Description |
|---|---|---|
source | string | Venue. |
source_market_id | string | Venue’s native market id. |
trader_name | string | null | Public trader handle, when available. |
side | string | Trade side (buy / sell). |
outcome | string | Outcome traded. |
size_usd | number | Notional size in USD. |
price | number | Trade price (0–1). |
title | string | Market question. |
trade_time | string | When the trade printed (ISO 8601). |
tx_hash | string | null | On-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
Returns your key’s tier, limits, and current usage. Use to monitor remaining quota.
{
"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:
| Environment | How to authenticate |
|---|---|
| Node.js / Python / server | Pass x-api-key: mmk_live_... in the WebSocket upgrade request headers. |
| Browser | Append ?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)
| Action | Payload | Effect |
|---|---|---|
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)
| Type | Example payload | When |
|---|---|---|
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
| Tier | Max concurrent connections | Max channel subscriptions / connection |
|---|---|---|
free | 1 | 5 |
api_starter | 3 | 20 |
pro (consumer) | 5 | 50 |
api_pro | 10 | 100 |
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." } }
| Status | Code | Meaning |
|---|---|---|
401 | missing_api_key | No x-api-key header sent. |
401 | invalid_api_key | Key is malformed, unknown, or revoked. |
400 | invalid_parameter | Query parameter invalid or required param missing. |
404 | not_found | No resource matched (e.g. unknown market id). |
426 | websocket_required | /api/v1/stream must be connected via WebSocket. |
429 | rate_limited | Per-minute rate exceeded — check Retry-After header. |
429 | quota_exceeded | Monthly quota exhausted — resets at month start. |
500 | auth_error | Temporary problem validating the key — retry. |
502 | upstream_error | Transient data load problem — retry. |
503 | streaming_unavailable | WebSocket 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.