API reference
Idmon API — Documentation
Calibrated, real-time prediction-market intelligence — delivered as signed webhooks, a WebSocket stream, and a REST API. Base URL: https://api.idmon.io.
Get started
Quickstart
Three steps from zero to live data.
Get your key
Email [email protected] to request access. Tell us your use case and we'll confirm the right tier, provision your API key, and — for Operator — configure your webhook. Usually within one business day.
Request accessMake your first call (Developer)
Once you have a key, fetch recent signal alerts:
curl -s https://api.idmon.io/api/alerts/recent?limit=5 \
-H "Authorization: Bearer pmp_dev_your_key_here"
Example response:
[
{
"conditionId": "0xabc123...",
"alertType": "whale_trade",
"question": "Will the Fed cut rates in July 2026?",
"category": "economics",
"currentPrices": { "Yes": 0.62, "No": 0.38 },
"priceChangePct": 4.8,
"volume24h": 1240000,
"sentAt": "2026-06-19T10:14:22Z",
"message": "Whale bought $85K YES at 0.62"
}
]
Receive data
Operator: We configure signed webhooks to push signal embeds directly to your Discord channel or endpoint — within ~1.5 s of an alert. See Webhooks for the payload contract and signature verification.
Developer: Connect the WebSocket stream for a live push of every alert as it fires. See WebSocket for the connect URL, message types, and a Node.js example.
Authentication
All REST endpoints require your API key as a Bearer token in the Authorization header. Do not pass it as a query parameter for REST.
Authorization: Bearer <your-api-key>
WebSocket connections authenticate via a ?key= query parameter on the connect URL (bearer headers are not supported at the WS handshake — see WebSocket).
Key prefixes
Your key prefix tells you which tier you're on:
| Prefix | Tier |
|---|---|
pmp_op_ |
Operator |
pmp_dev_ |
Developer |
pmp_ent_ |
Enterprise |
401 Unauthorized. If you see a 401 mid-session your daily quota may have reset — check your usage against the rate limits table.
REST API
Base URL: https://api.idmon.io. Every /api/* path requires the Authorization: Bearer <key> header. Responses are JSON. Errors follow standard HTTP status codes.
Returns service status, uptime, your tier, rate-limit headroom, and server memory. Use this to confirm connectivity and inspect your plan limits.
curl -s https://api.idmon.io/api/health \
-H "Authorization: Bearer pmp_dev_your_key_here"
{
"status": "ok",
"tier": "developer",
"uptime": 432800,
"rateLimit": { "remaining": 9741, "limit": 10000, "resetsAt": "2026-06-20T00:00:00Z" },
"memoryMb": 182,
"ts": "2026-06-19T11:04:00Z"
}
Returns recent signal alerts, newest first. The quickstart example uses this endpoint.
| Query param | Type | Description |
|---|---|---|
| limitdefault 50 | integer | Maximum number of alerts to return. |
| type | string | Filter by alert type, e.g. whale_trade, volume_surge. |
curl -s "https://api.idmon.io/api/alerts/recent?limit=50&type=whale_trade" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"conditionId": "0xabc123...",
"alertType": "whale_trade",
"question": "Will the Fed cut rates in July 2026?",
"category": "economics",
"currentPrices": { "Yes": 0.62, "No": 0.38 },
"priceChangePct": 4.8,
"volume24h": 1240000,
"sentAt": "2026-06-19T10:14:22Z",
"message": "Whale bought $85K YES at 0.62"
}
]
Returns a histogram of alert counts by type over the requested window. Useful for building dashboards or spotting unusual activity.
| Query param | Type | Description |
|---|---|---|
| hoursdefault 24 | integer | Window size in hours to aggregate over. |
curl -s "https://api.idmon.io/api/alerts/counts?hours=24" \
-H "Authorization: Bearer pmp_dev_your_key_here"
{
"hours": 24,
"counts": {
"whale_trade": 18,
"volume_surge": 7,
"price_spike": 3
},
"since": "2026-06-18T11:00:00Z"
}
Recent large-trade events, each with side, size, and whale/mega-whale classification.
| Query param | Type | Description |
|---|---|---|
| limitdefault 20 | integer | Maximum number of events to return. |
curl -s "https://api.idmon.io/api/whales/recent?limit=20" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"side": "YES",
"sizeUsd": 150000,
"price": 0.71,
"question": "Will BTC close above $100K in June?",
"category": "crypto",
"timestamp": "2026-06-19T09:52:11Z",
"isMegaWhale": true
}
]
Aggregate whale statistics — total all-time, mega-whale count, and rolling 24-hour volume.
curl -s https://api.idmon.io/api/whales/stats \
-H "Authorization: Bearer pmp_dev_your_key_here"
{
"totalWhaleAlerts": 4120,
"megaWhaleAlerts": 312,
"whaleAlerts24h": 18
}
Top markets by 24-hour volume, with current outcome prices and liquidity. Optionally filter by category.
| Query param | Type | Description |
|---|---|---|
| limitdefault 20 | integer | Maximum number of markets to return. |
| category | string | Filter by category slug, e.g. politics, crypto, economics. |
curl -s "https://api.idmon.io/api/markets/top?limit=20&category=politics" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"conditionId": "0xdef456...",
"question": "Will Labour win the next UK general election?",
"category": "politics",
"endDate": "2029-01-31T00:00:00Z",
"volume24h": 3100000,
"liquidity": 820000,
"outcomePrices": { "Yes": 0.54, "No": 0.46 },
"url": "https://polymarket.com/event/..."
}
]
Top-performing wallets ranked by win rate, with trade counts, streaks, and total volume.
| Query param | Type | Description |
|---|---|---|
| ndefault 20 | integer | Number of wallets to return. |
curl -s "https://api.idmon.io/api/wallets/top?n=20" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"walletAddress": "0x1a2b3c...",
"username": "sharpshooter",
"correctTrades": 84,
"totalTrades": 102,
"winRate": 0.824,
"currentStreak": 7,
"totalVolumeUsd": 540000,
"polymarketUrl": "https://polymarket.com/profile/0x1a2b3c..."
}
]
Which whales actually win — not just who's big. Per-wallet win rate, Brier calibration (lower is better), profit factor, average hold time, breadth, and a smart/dumb classification. Sort by win rate, calibration, or profit factor. This is the accuracy layer trackers don't expose.
| Query param | Type | Description |
|---|---|---|
| limitdefault 30 | integer | Maximum wallets to return (max 100). |
| minTradesdefault 20 | integer | Minimum resolved trades for a wallet to qualify. |
| sortdefault winRate | string | One of winRate, brier (best-calibrated first), profitFactor. |
curl -s "https://api.idmon.io/api/whales/accuracy?limit=20&sort=brier" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"walletAddress": "0x1a2b3c...",
"username": "sharpshooter",
"classification": "smart",
"winRate": 0.667,
"brierScore": 0.18,
"profitFactor": 2.1,
"totalTrades": 120,
"avgHoldHours": 36.5,
"distinctCategories": 4,
"currentStreak": 5,
"bestCategory": "politics",
"polymarketUrl": "https://polymarket.com/profile/0x1a2b3c..."
}
]
Where the smart money is positioned: net YES flow from historically-accurate wallets (>60% win rate) vs low-accuracy wallets (<45%), per market. Returns the latest snapshot per market ranked by the smart–dumb delta, or pass conditionId for one market's time series.
| Query param | Type | Description |
|---|---|---|
| limitdefault 30 | integer | Maximum rows to return (max 100). |
| conditionId | string | Return the time series for a single market instead of the latest-per-market ranking. |
curl -s "https://api.idmon.io/api/smart-money?limit=20" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"conditionId": "0xabc123...",
"question": "Will the Fed cut rates in September?",
"category": "economics",
"smartMoneyNetYes": 142000,
"dumbMoneyNetYes": -38000,
"smartWalletCount": 11,
"dumbWalletCount": 6,
"smartDumbDelta": 180000,
"signal": "smart_bullish",
"computedAt": 1718900000000
}
]
Recent market resolutions with a calibrated, on-chain-anchored truth label — not just the winning outcome, but a calibrated P(label correct), which sources agreed, the on-chain settlement state, and dispute/review flags. Filter to contested resolutions with reviewOnly=true.
| Query param | Type | Description |
|---|---|---|
| limitdefault 50 | integer | Maximum resolutions to return (max 200). |
| category | string | Filter by category slug. |
| reviewOnly | boolean | Only return resolutions flagged as disputed or needing review. |
curl -s "https://api.idmon.io/api/resolutions?limit=50&reviewOnly=true" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"conditionId": "0xabc123...",
"question": "Will BTC close above $100k on Dec 31?",
"category": "crypto",
"winningOutcome": "YES",
"labelPosterior": 0.999,
"winningOutcomeSource": "onchain",
"settlementState": "finalized",
"nSourcesAgree": 4,
"nSourcesTotal": 4,
"disputeFlag": false,
"reviewFlag": false,
"ambiguityFlags": [],
"resolvedAt": 1735689600000
}
]
Full resolution truth for one market, including the per-source evidence trail — the on-chain payout, Gamma, CLOB and price-implied readings — each joined to that source's measured reliability. Returns 404 if the market hasn't resolved.
curl -s "https://api.idmon.io/api/resolutions/0xabc123..." \
-H "Authorization: Bearer pmp_dev_your_key_here"
{
"conditionId": "0xabc123...",
"question": "Will BTC close above $100k on Dec 31?",
"winningOutcome": "YES",
"labelPosterior": 0.999,
"winningOutcomeSource": "onchain",
"settlementState": "finalized",
"nSourcesAgree": 4,
"nSourcesTotal": 4,
"disputeFlag": false,
"reviewFlag": false,
"ambiguityFlags": [],
"resolvedAt": 1735689600000,
"evidence": [
{ "source": "onchain", "outcomeSide": "YES", "reliability": 0.999 },
{ "source": "gamma", "outcomeSide": "YES", "reliability": 0.95 }
]
}
Where a market disagrees with the outside world: the gap between the market price and the poll-implied (politics) or bookmaker-implied (sports) probability. Latest gap per market, signed (consensus − market) in percentage points.
| Query param | Type | Description |
|---|---|---|
| sourcedefault all | string | all, polls (politics), or odds (sports). |
| minGapPpdefault 0 | number | Minimum absolute gap, in percentage points. |
| category | string | Filter by category slug. |
| limitdefault 30 | integer | Max rows to return (max 100). |
curl -s "https://api.idmon.io/api/cross-market/divergence?source=polls&minGapPp=5" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"conditionId": "0xabc123...",
"question": "Will candidate X win?",
"category": "politics_geo",
"source": "polls",
"marketProb": 0.40,
"consensusProb": 0.50,
"gapPp": 10.0,
"direction": "consensus_higher",
"sampleSize": 12,
"emittedAt": 1718900000000,
"signalOutcome": null
}
]
Where Polymarket and Kalshi price the same market apart. Matched cross-venue pairs (passed a hardened 4-gate matcher) where the two venues disagree, ranked by spread — the cross-platform divergence signal.
| Query param | Type | Description |
|---|---|---|
| limitdefault 30 | integer | Max rows (max 100). |
| minSpreadPctdefault 0 | number | Minimum cross-venue spread, in percentage points. |
| platform | string | kalshi or manifold (the counter-venue). |
| verifiedOnly | boolean | Only human-confirmed-good matches. |
curl -s "https://api.idmon.io/api/cross-market/venue-divergence?platform=kalshi&minSpreadPct=8" \
-H "Authorization: Bearer pmp_dev_your_key_here"
[
{
"questionA": "Will Strait of Hormuz traffic return to normal by July 15?",
"questionB": "Strait of Hormuz traffic returns to normal by July 15?",
"platformA": "polymarket",
"platformB": "kalshi",
"priceA": 0.57,
"priceB": 0.40,
"spreadPp": 17.4,
"netEdgePp": 13.4,
"similarity": 0.83,
"matchQuality": "unlabeled",
"detectedAt": 1782125000000
}
]
A single-request dashboard summary — alert and whale counts over 24 hours, and the number of currently active markets.
curl -s https://api.idmon.io/api/overview \
-H "Authorization: Bearer pmp_dev_your_key_here"
{
"alertCount24h": 28,
"whaleCount24h": 18,
"activeMarkets": 3142,
"ts": "2026-06-19T11:04:00Z"
}
WebSocket
Available on Developer and Enterprise plans. Connect once to receive every alert as it fires — no polling, no batch lag.
Connect URL
wss://api.idmon.io/ws?key=<your-api-key>&types=volume_surge,whale_trade&minVolume=50000
| Param | Required | Description |
|---|---|---|
| key | Yes | Your API key (Developer or Enterprise prefix). |
| types | No | Comma-separated alert types to filter. Omit to receive all types. |
| minVolume | No | Minimum 24h market volume (USD) to filter alerts. |
Message types
{ "type": "connected", "message": "Authenticated. Stream active." }
{
"type": "alert",
"data": {
"alertType": "whale_trade",
"conditionId": "0xabc123...",
"question": "Will the Fed cut rates in July 2026?",
"category": "economics",
"currentPrices": { "Yes": 0.62, "No": 0.38 },
"priceChangePct": 4.8,
"volume24h": 1240000,
"message": "Whale bought $85K YES at 0.62",
"timestamp": "2026-06-19T10:14:22Z"
}
}
{ "type": "ping" }
ping frame every 30 seconds. Clients should ignore it — no pong response is required. If you lose the connection, reconnect with the same URL.
Close codes
Node.js example
import WebSocket from 'ws';
const KEY = process.env.IDMON_API_KEY;
const url = `wss://api.idmon.io/ws?key=${KEY}&types=whale_trade,volume_surge&minVolume=50000`;
function connect() {
const ws = new WebSocket(url);
ws.on('open', () => {
console.log('WebSocket open — waiting for alerts...');
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.type === 'ping') return; // ignore keepalive
if (msg.type === 'alert') {
const { alertType, question, message, timestamp } = msg.data;
console.log(`[${timestamp}] ${alertType}: ${question}`);
console.log(` ${message}`);
}
if (msg.type === 'connected') {
console.log('Authenticated:', msg.message);
}
});
ws.on('close', (code, reason) => {
if (code === 4001) { console.error('Unauthorized — check your API key'); return; }
if (code === 4003) { console.error('WebSocket not available on your plan'); return; }
if (code === 4008) { console.error('Connection limit reached — reduce concurrent connections'); return; }
// Reconnect on unexpected closure
console.warn(`Closed (${code}): ${reason}. Reconnecting in 5s...`);
setTimeout(connect, 5000);
});
ws.on('error', (err) => console.error('WS error:', err.message));
}
connect();
Webhooks
Available on Operator and above. We send signed HTTP POST requests to your endpoint within ~1.5 seconds of an alert — no polling required. Discord webhook URLs receive a Discord-formatted embeds payload automatically; all other URLs receive the generic JSON below.
Generic JSON payload
{
"schema": 1,
"event": "whale_trade",
"id": "evt_01j9abc...",
"ts": "2026-06-19T10:14:22Z",
"conditionId": "0xabc123...",
"data": {
"question": "Will the Fed cut rates in July 2026?",
"slug": "fed-rate-cut-july-2026",
"category": "economics",
"message": "Whale bought $85K YES at 0.62",
"currentPrices": { "Yes": 0.62, "No": 0.38 },
"previousPrices": { "Yes": 0.57, "No": 0.43 },
"priceChangePct": 4.8,
"volume24h": 1240000,
"liquidity": 310000,
"polymarketUrl": "https://polymarket.com/event/fed-rate-cut-july-2026"
}
}
Request headers
| Header | Value / purpose |
|---|---|
| Content-Type | application/json |
| X-PMP-Signature | sha256=<hex> — HMAC-SHA256 of the raw request body with your webhook secret. Verify this before processing. |
| X-PMP-Delivery-Id | Unique ID per delivery — use for idempotency (retries send the same ID). |
| User-Agent | PMP-Webhooks/1.0 |
Signature verification
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = b"your_webhook_secret_here"
@app.route("/webhook", methods=["POST"])
def webhook():
sig_header = request.headers.get("X-PMP-Signature", "")
if not sig_header.startswith("sha256="):
abort(400, "Missing signature")
received_sig = sig_header[len("sha256="):]
# Compute HMAC over the RAW body — do NOT re-serialise
raw_body = request.get_data()
expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected_sig, received_sig):
abort(401, "Invalid signature")
payload = request.get_json()
event = payload["event"]
print(f"Verified event: {event}")
return "", 200 # respond 2xx quickly — processing can be async
Retry behaviour
If your endpoint is unavailable or returns a non-2xx response:
- 5xx / timeout — exponential backoff, up to 4 retry attempts.
- 429 — backoff respects your
Retry-Afterresponse header. - Other 4xx — permanent failure; no retries. Respond 2xx quickly and handle processing asynchronously.
Use the X-PMP-Delivery-Id header to deduplicate retries on your side.
Rate limits
Exceeding any limit returns 429 Too Many Requests. Daily request counts reset at UTC midnight.
| Tier | REST | WebSocket | Requests / day | Req / second | Concurrent WS | Bulk history |
|---|---|---|---|---|---|---|
| Operator | — | — | Push only | — | — | — |
| Developer | ✓ | ✓ | 10,000 | 10 | 5 | — |
| Enterprise | ✓ | ✓ | Unlimited | 50 | 25 | ✓ |
Operator tier receives data exclusively via push (signed webhooks / Discord embeds) — no REST or WebSocket access. Enterprise bulk history includes market snapshots (~8K rows/min) and 5-minute OHLCV aggregates.
Support
Need help?
Email us at [email protected] — we respond within 48 hours. Enterprise plans include priority support.