Official Python SDK, MCP server for Claude Desktop, plus copy-paste Node.js and cURL examples.
Give Claude direct access to markets, OHLCV, leaderboard, trader browse/PnL, holders, trades, copy picks, and live order books. The MCP server calls the same REST API your apps use.
Published on npm: @falconai/mcp-server ↗ · Local dev: cd packages/falcon-mcp-server && npm install
{
"mcpServers": {
"falcon-polymarket": {
"command": "npx",
"args": ["-y", "@falconai/mcp-server"],
"env": {
"FALCON_API_KEY": "aif_your_key",
"FALCON_API_BASE": "https://api.falconai.pro/api/v1/external/polymarket"
}
}
}
}
| Tool | Description |
|---|---|
search_markets | List/filter markets |
get_market | Market detail + snapshot |
get_market_ohlcv | OHLCV candles (5m / 1h / 1d) |
get_leaderboard | Trader rankings (PnL, win rate, risk score) |
browse_traders | Ranked trader directory |
get_trader_stats | Full wallet profile |
get_trader_pnl | Daily PnL curve |
get_recent_trades | Trade feed (optional min_size) |
get_market_holders | Top holders per outcome |
get_market_oi | Open interest |
get_copy_picks | Topic copy-trade picks |
get_orderbook | Live L2 book |
get_api_usage | Quota / usage |
get_briefing | Agent heartbeat (copy picks, signals, paper state) |
Use Falcon for Polymarket research inside OpenClaw. Install the skill from ClawHub when available, or download falcon-openclaw-skill.zip from the agent hub.
openclaw skills install falcon-polymarket
# or copy SKILL.md to ~/.openclaw/skills/falcon-polymarket/
export FALCON_API_KEY=aif_your_key
For live signal execution, run falcon-polymarket-agent on the same machine (PyPI, zip, or Windows .exe). Wallet keys stay local. Install guide ↗ · Download page ↗
MCP package for OpenClaw/Cursor: @falconai/polymarket-mcp (see MCP section above) or npx @falconai/mcp-server.
Autonomous trading runtime that runs on your PC or VPS. Falcon sends signals; your machine applies risk rules and signs Polymarket CLOB orders. Private keys never leave your device.
Published on PyPI: falcon-polymarket-agent ↗ · npm bootstrap: npx @falconai/agent-client
# Preflight (API key + WebSocket + platform tether) falcon-polymarket-agent-check # Run client + dashboard at http://localhost:8080 falcon-polymarket-agent
Zip + Python, Windows .exe (no Python), and Docker are on the agent client download page ↗. Configuration reference: .env guide ↗
DRY_RUN=true first)Use the Python SDK or any HTTP client with your API key.
Published on PyPI: falcon-polymarket ↗ · Local dev: pip install -e packages/falcon-polymarket-python
from falcon_polymarket import FalconPolymarketClient with FalconPolymarketClient(api_key="aif_your_key") as client: board = client.get_leaderboard(limit=5, sort_by="total_realized_pnl") candles = client.get_ohlcv("0x...", interval="1h") holders = client.get_market_holders("0x...") usage = client.usage()
fetchEvery request must include your API key in the Authorization header. Generate keys at Developer Keys ↗.
// Node.js 18+ — built-in fetch const API_BASE = 'https://api.falconai.pro/api/v1/external/polymarket'; const API_KEY = 'aif_your_key_here'; // from /app/developer/keys const apiFetch = (path, params = {}) => { const url = new URL(API_BASE + path); Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v)); return fetch(url, { headers: { 'Authorization': `Bearer ${API_KEY}` } }) .then(r => r.json()); }; // Health check const health = await apiFetch('/health'); console.log(health); // { success: true, data: { status: 'ok' } }
# Python — pip install requests import requests API_BASE = 'https://api.falconai.pro/api/v1/external/polymarket' API_KEY = 'aif_your_key_here' session = requests.Session() session.headers['Authorization'] = f'Bearer {API_KEY}' # Health check resp = session.get(f'{API_BASE}/health') resp.raise_for_status() print(resp.json()) # {'success': True, 'data': {'status': 'ok'}}
# Health check curl https://api.falconai.pro/api/v1/external/polymarket/health \ -H "Authorization: Bearer aif_your_key_here"
List active markets, fetch a single market (id or slug), OHLCV candles, or paginate through all 35,000+ markets using cursor-based pagination.
// List top 10 active markets by volume const { data: markets } = await apiFetch('/markets', { status: 'active', sort: 'volume', limit: 10 }); // Single market detail (conditionId or slug) const { data: market } = await apiFetch(`/markets/${marketId}`); // OHLCV candles const { data: candles } = await apiFetch(`/markets/${marketId}/ohlcv`, { interval: '1h', limit: 168 }); // ── Cursor pagination ──────────────────────────────────────── async function getAllMarkets() { let cursor = null; const all = []; do { const resp = await apiFetch('/markets', { status: 'active', limit: 50, ...(cursor && { cursor }) }); all.push(...resp.data); cursor = resp.pagination?.has_more ? resp.pagination.next_cursor : null; } while (cursor); return all; }
# List top 10 active markets by volume markets = session.get( f'{API_BASE}/markets', params={'status': 'active', 'sort': 'volume', 'limit': 10} ).json()['data'] # Single market market = session.get(f'{API_BASE}/markets/{market_id}').json()['data'] # Hourly OHLCV candles = session.get( f'{API_BASE}/markets/{market_id}/ohlcv', params={'interval': '1h', 'limit': 168} ).json()['data'] # ── Cursor pagination ───────────────────────────────────────── def get_all_markets(): all_markets, cursor = [], None while True: params = {'status': 'active', 'limit': 50} if cursor: params['cursor'] = cursor resp = session.get(f'{API_BASE}/markets', params=params).json() all_markets.extend(resp['data']) if not resp['pagination']['has_more']: break cursor = resp['pagination']['next_cursor'] return all_markets
# List top 10 active markets sorted by volume curl "https://api.falconai.pro/api/v1/external/polymarket/markets?status=active&sort=volume&limit=10" \ -H "Authorization: Bearer aif_your_key" # Single market detail curl "https://api.falconai.pro/api/v1/external/polymarket/markets/0xabc123..." \ -H "Authorization: Bearer aif_your_key" # Hourly OHLCV curl "https://api.falconai.pro/api/v1/external/polymarket/markets/0xabc123.../ohlcv?interval=1h&limit=168" \ -H "Authorization: Bearer aif_your_key" # Categories + platform volume curl "https://api.falconai.pro/api/v1/external/polymarket/markets/categories" \ -H "Authorization: Bearer aif_your_key" curl "https://api.falconai.pro/api/v1/external/polymarket/volume/timeseries?range_days=30" \ -H "Authorization: Bearer aif_your_key" # Next page using cursor curl "https://api.falconai.pro/api/v1/external/polymarket/markets?status=active&limit=50&cursor=eyJ0cyI6..." \ -H "Authorization: Bearer aif_your_key"
Stream recent trades, filter by wallet or market, browse ranked traders, and fetch per-wallet PnL, holders, and open interest.
// Recent trades (global) const { data: trades } = await apiFetch('/trades', { limit: 50 }); // Trades for a specific wallet const { data: walletTrades } = await apiFetch('/trades', { wallet: '0xba4ac793e68eacb93b41566137f25757656a9fa6', limit: 100 }); // Trader profile (lifetime + 30-day stats) const { data: trader } = await apiFetch( '/traders/0xba4ac793e68eacb93b41566137f25757656a9fa6' ); console.log(trader.total_realized_pnl, trader.win_rate_pct); // Daily PnL curve + top holders (Tier 2) const { data: pnl } = await apiFetch('/traders/0xba4ac.../pnl', { days: 90 }); const { data: holders } = await apiFetch(`/markets/${marketId}/holders`);
# Recent trades (global) trades = session.get(f'{API_BASE}/trades', params={'limit': 50}).json()['data'] # Trades for a specific wallet wallet_trades = session.get(f'{API_BASE}/trades', params={ 'wallet': '0xba4ac793e68eacb93b41566137f25757656a9fa6', 'limit': 100 }).json()['data'] # Trader profile (30-day stats + realized PnL) trader = session.get( f"{API_BASE}/traders/0xba4ac793e68eacb93b41566137f25757656a9fa6" ).json()['data'] print(trader['total_realized_pnl'], trader['win_rate_pct']) # Daily PnL + holders (Tier 2) pnl = session.get(f'{API_BASE}/traders/{wallet}/pnl', params={'days': 90}).json()['data'] holders = session.get(f'{API_BASE}/markets/{market_id}/holders').json()['data']
# Recent trades curl "https://api.falconai.pro/api/v1/external/polymarket/trades?limit=50" \ -H "Authorization: Bearer aif_your_key" # Trades for a wallet curl "https://api.falconai.pro/api/v1/external/polymarket/trades?wallet=0xba4ac...&limit=100" \ -H "Authorization: Bearer aif_your_key" # Trader profile + daily PnL + holders curl "https://api.falconai.pro/api/v1/external/polymarket/traders/0xba4ac..." \ -H "Authorization: Bearer aif_your_key" curl "https://api.falconai.pro/api/v1/external/polymarket/traders/browse?sort_by=total_realized_pnl&limit=25" \ -H "Authorization: Bearer aif_your_key" curl "https://api.falconai.pro/api/v1/external/polymarket/markets/0xabc.../holders" \ -H "Authorization: Bearer aif_your_key"
Subscribe via REST with your API key at POST /api/v1/external/polymarket/webhooks, or use the Developer Webhooks UI. Events are verified with HMAC-SHA256.
curl -X POST https://api.falconai.pro/api/v1/external/polymarket/webhooks \ -H "Authorization: Bearer aif_your_key" \ -H "Content-Type: application/json" \ -d '{"url":"https://your-server.com/hook","events":["trade.executed","trader.large_trade"]}'
| Header | Description |
|---|---|
X-Falcon-Event | Event type, e.g. trade.executed |
X-Falcon-Delivery | Unique delivery ID |
X-Falcon-Timestamp | Unix seconds (use for replay-attack protection) |
X-Falcon-Signature | sha256=<hmac> — verify this before processing |
const crypto = require('crypto'); const express = require('express'); const app = express(); // Use raw body parser so we can compute the exact HMAC app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } })); const WEBHOOK_SECRET = process.env.FALCON_WEBHOOK_SECRET; // whsec_... app.post('/webhook', (req, res) => { const sig = req.headers['x-falcon-signature']; const ts = req.headers['x-falcon-timestamp']; // Reject events older than 5 minutes (replay-attack guard) if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) { return res.status(400).send('Timestamp too old'); } const msg = `${ts}.${req.rawBody}`; const expected = 'sha256=' + crypto .createHmac('sha256', WEBHOOK_SECRET) .update(msg).digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) { return res.status(401).send('Bad signature'); } // Signature is valid — handle the event const event = req.body; console.log(`Received ${event.event} for market ${event.market_id}`); // Always respond 2xx quickly — retry logic depends on it res.status(200).send('OK'); });
# Flask webhook receiver — pip install flask import hmac, hashlib, time from flask import Flask, request, abort app = Flask(__name__) SECRET = 'whsec_your_secret' # set from env @app.post('/webhook') def receive_webhook(): sig = request.headers.get('X-Falcon-Signature', '') ts = request.headers.get('X-Falcon-Timestamp', '0') # Replay-attack guard — reject events older than 5 minutes if abs(time.time() - int(ts)) > 300: abort(400, 'Timestamp too old') raw_body = request.get_data() msg = ts.encode() + b'.' + raw_body expected = 'sha256=' + hmac.new( SECRET.encode(), msg, hashlib.sha256 ).hexdigest() if not hmac.compare_digest(sig, expected): abort(401, 'Bad signature') event = request.get_json() print(f"Received {event['event']} for {event.get('market_id','?')}") return 'OK', 200
# HMAC-SHA256 algorithm (language-agnostic) # # 1. Concatenate: message = timestamp + "." + raw_body_string # 2. Compute HMAC: digest = HMAC-SHA256(key=webhook_secret, data=message) # 3. Format: expected = "sha256=" + hex(digest) # 4. Compare: use constant-time comparison to avoid timing attacks # # Reject if: sig != expected OR abs(now - timestamp) > 300 seconds # # Example values: timestamp = "1743600000" raw_body = '{"event":"trade.executed","market_id":"0xabc..."}' secret = "whsec_8c027e334813cbafe38895ca0281f992..." message = timestamp + "." + raw_body # → "1743600000.{"event":"trade.executed",...}" signature = "sha256=" + HMAC_SHA256_HEX(key=secret, data=message) # → "sha256=a3f7e2c8d1..."
// trade.executed { "event": "trade.executed", "trade_id": "0x1a2b3c...", "market_id": "0xabc123...", "market_title": "Will BTC exceed $100k by Dec 2025?", "wallet": "0xba4ac793...", "side": "buy", "outcome": "YES", "price": 0.72, "size_usdc": 250.0, "timestamp": "2026-04-03T15:00:00.000Z" } // market.price_signal { "event": "market.price_signal", "market_id": "0xabc123...", "market_title": "Will XRP dip to $0.60 by December 31, 2026?", "yes_price": 0.44, "prev_price": 0.28, "change_pct": 57.1, "direction": "up", "timestamp": "2026-04-03T15:05:00.000Z" } // market.resolved { "event": "market.resolved", "market_id": "0xdef456...", "market_title": "Will the Fed cut rates in March 2026?", "resolved_outcome": "YES", "final_yes_price": 1.0, "final_no_price": 0.0, "timestamp": "2026-04-03T16:00:00.000Z" } // copy.executed (account-scoped — autonomous copy) { "event": "copy.executed", "user_id": 42, "copy_subscription_id": 7, "leader_wallet": "0xba4ac793...", "leader_trade_id": "0x1a2b3c...", "market_id": "0xabc123...", "side": "buy", "outcome": "YES", "child_size_usdc": 25.0, "leader_size_usdc": 250.0, "clob_order_id": "0xorder...", "trade_intent_id": 901, "status": "submitted", "dry_run": false, "timestamp": "2026-04-03T15:01:00.000Z" } // risk.blocked (account-scoped) { "event": "risk.blocked", "user_id": 42, "copy_subscription_id": 7, "leader_wallet": "0xba4ac793...", "market_id": "0xabc123...", "reason": "max_daily_notional", "leader_size_usdc": 250.0, "trade_intent_id": 902, "timestamp": "2026-04-03T15:01:00.000Z" } // strategy.signal (account-scoped — Strategy Studio) { "event": "strategy.signal", "signal_id": "strat_1a2b3c4d", "signal_type": "strategy_lab", "strategy_id": 12, "strategy_name": "YES dip buyer", "market_id": "0xabc123...", "market_title": "Will BTC exceed $100k by Dec 2026?", "side": "buy", "entry_price": 0.42, "confidence": 0.75, "edge": 0.08, "emitted_at": "2026-04-03T15:02:00.000Z" }
All errors share the same envelope. Check success === false and the code field.
async function safeApiFetch(path, params = {}) { const resp = await apiFetch(path, params); if (!resp.success) { const { code, error } = resp; if (code === 'UNAUTHORIZED') throw new Error('Invalid API key'); if (code === 'FORBIDDEN') throw new Error('Tier too low for this endpoint'); if (code === 'NOT_FOUND') throw new Error(`Not found: ${path}`); if (code === 'PAYMENT_REQUIRED') throw new Error('Insufficient credits'); throw new Error(`API error [${code}]: ${error}`); } return resp; }
def safe_get(path, **params): resp = session.get(f'{API_BASE}{path}', params=params).json() if not resp.get('success'): code = resp.get('code', 'UNKNOWN') msg = resp.get('error', 'Unknown error') error_map = { 'UNAUTHORIZED': 'Invalid API key', 'FORBIDDEN': 'Tier too low for this endpoint', 'NOT_FOUND': f'Not found: {path}', 'PAYMENT_REQUIRED': 'Insufficient credits', } raise Exception(error_map.get(code, f'API error [{code}]: {msg}')) return resp