Public REST API
Programmatic access to Polymarket whale analytics, ISW territory data, and leaderboards. Available on Premium ($19.99/mo).
Quickstart
- Sign up and upgrade to Premium
- Go to Settings → API Keys and click Generate Key
- Copy the key (format
sk_orca_XXXXXX) — it's shown only once - Make authenticated requests:
curl -H "Authorization: Bearer sk_orca_XXXXXX" \
"https://orcalayer.com/api/public/v1/whales/leaderboard?limit=10"Authentication
Send your API key as a Bearer token in the Authorization header. Alternative: x-api-key header.
Authorization: Bearer sk_orca_XXXXXX- Missing key →
401 Unauthorized - Revoked / invalid key →
401 - Valid key but plan ≠ premium →
403 Forbidden - Rate limit exceeded →
429 Too Many RequestswithRetry-After: 60
Wins / Losses / Win Rate / Profit Factor — data semantics
As of 6.05.2026 (Phase B5 take 2), wins, losses, win_rate, and profit_factor are computed from Polymarket data-api.polymarket.com/closed-positions data when our cache is complete (≥95% of expected closed positions, measured via Polymarket data-api.polymarket.com/traded API).
- Cache complete → matches Polymarket UI counters (most wallets).
- Cache truncated (top whales with 1M+ trade events) → falls back to our v5 markets-resolved metric (1 unit per market, NegRisk multi-market events count once). v5 ≈ Prediction Folio for high-volume wallets.
profit_factoruses cache PnL ratio (looser guard — ratios stable even when cache truncated). Capped at 99.99x; real may be higher with rare losses.total_pnlalways uses Polymarket lb_api lifetime PnL when synced (authoritative source).
Contract addresses (not user wallets)
Polymarket router/exchange contract addresses are excluded from wallet endpoints (they aggregate ALL trades through the router, not a single user). Querying these returns an explicit error response shape rather than fake whale stats:
{
"error": "This address is a Polymarket smart contract, not a user wallet",
"is_contract": true,
"contract_type": "NegRisk CTF V2 Exchange", // or CTF Exchange V1/V2, NegRisk CTF V1
"address": "0xe2222d279d744050d28e00520010520000310f59"
}Currently 4 addresses excluded: 0x4bfb41d5... (CTF Exchange V1), 0xc5d563a3... (NegRisk CTF V1), 0xe1111800... (CTF Exchange V2), 0xe2222d27... (NegRisk CTF V2).
Rate limits
Each Premium API key is limited to 300 requests/minute (sliding window 60s). Anonymous (no-key) public endpoints: 200/min per IP; wallet overview/positions endpoints are additionally capped at 300 requests/day per IP for anonymous external clients (Premium keys have no daily cap; normal browsing on this site is unaffected). Custom enterprise tier available on request. Usage headers on every response:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287Batch endpoints: POST /api/public/v1/wallets/overview (max 50 wallets) and POST /api/public/v1/wallets/positions (max 20, heavier payloads) with body {"wallets": ["0x...", ...]} return per-wallet statuses (ok / computing / not_found) in one call. Batch requests are read-only and idempotent — identical requests return the same data and never mutate state, so retrying after a timeout or network error is always safe. One batch consumes N (= wallet count) of the per-key budget; the X-RateLimit-Consumed header reports it. Wallets in computing status are queued automatically and typically resolve within minutes; for an immediate result fetch the single-wallet overview endpoint.
Need higher limits? Email [email protected] with your use case.
Endpoints
https://orcalayer.com/api/public/v1/whales/leaderboard?limit=10&sort=pnl&period=allTop whales ranked by P&L / Win Rate / Profit Factor. Params: limit (1-100), sort (pnl|wr|pf|volume), period (24h|7d|30d|all), min_profit_factor.
{
"whales": [
{
"wallet": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
"name": "Theo4",
"total_pnl": 22053933.75, // overlay-aware: lb_api truth when synced
"win_rate": 81.8, // Phase B5 take 2: cache breakdown when complete
"profit_factor": 99.99, // capped at 99.99 (real ratio may be higher)
"resolved_markets": 22,
"wins": 18,
"losses": 4,
"total_volume": 52213734.76,
"total_trades": 39067,
"main_category": "POLITICS",
"avg_entry_price": 0.5523,
"last_trade_ts": 1731476951,
"active_count": 0,
"market_win_rate": 85.7, // per-market basis (1 unit per market)
"market_wins": 12,
"market_losses": 2,
"mint_volume_30d": 0.0,
"merge_volume_30d": 0.0
}
],
"total": 193696,
"sort": "pnl",
"offset": 0
}https://orcalayer.com/api/public/v1/wallet/{address}/overviewFull profile + aggregate stats for any Polygon wallet (0x… or Polymarket username). Primary stats are served from a per-wallet cache (see as_of field, refreshed within ~5 min of trading activity). On a cold cache for a very heavy wallet the API may return HTTP 202 + Retry-After: 30 — retry once and the cache will be warm. Under heavy load, secondary counters (active_count, closed_count, W/L breakdowns) may be omitted (null) with top-level degraded: true — primary stats are always served. view=union and view=signer are computed live and may be slower.
{
"profile": {
"address": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
"name": "Theo4",
"pseudonym": "Ironclad-Tenement",
"proxy_wallet": "0x56687bf447db6ffa42ffe2204a05edaa20f55839"
},
"overview": {
"total_trades": 39067,
"total_markets": 14,
"total_volume": 52213734.76,
"last_trade": 1731476951,
"profit_factor": 99.99, // overlay-aware (cache PnL ratio when complete)
"active_count": 0, // currently held positions
"closed_count": 22, // matches stats.wins + stats.losses
"median_hold_days": 21.5, // typical days a position is held (last 90d window)
"hold_positions_count": 12 // sample size that produced median (3+ required)
},
"stats": {
"resolved": 22,
"wins": 18, // Phase B5: cache breakdown when complete, sw v5 fallback
"losses": 4,
"win_rate": 81.8,
"total_pnl": 22053933.75, // lb_api truth (Polymarket /profit endpoint)
"profit_factor": 99.99,
"is_smart": true,
"profitable_streak": 1,
"market_win_rate": 85.7,
"market_wins": 12,
"market_losses": 2,
"unrealized_pnl": 0,
"pnl_24h": null
},
"rankings": {
"rank_pnl": 1,
"rank_winrate": 48344,
"rank_volume": 125,
"rank_profit_factor": 7,
"total_traders": 1298302,
"is_smart": true
},
"categories": {
"SPORTS": 0.0, "GEOPOLITICS": 0.0, "CRYPTO": 0.0,
"POLITICS": 85.7, "ECONOMICS": 0.0, "TECH": 0.0
},
"as_of": 1765538538, // unix ts of the cached snapshot
"degraded": false // true = secondary counters omitted under load
}https://orcalayer.com/api/public/v1/wallet/{address}/positions?limit=50Open positions for the wallet with current P&L.
{
"positions": [
{
"market_id": "2099029",
"condition_id": "0xabc...",
"outcome": "Yes",
"shares": 246169,
"avg_price": 0.119,
"current_price": 0.085,
"cost": 29368.0,
"unrealized_pnl": -8369.0,
"title": "US x Iran permanent peace deal by May 15, 2026?"
}
],
"source": "db_estimate", // or "polymarket_inline" when overlay synced
"is_estimated": true, // false when overlay-fresh
"count": 1
}https://orcalayer.com/api/public/v1/market/{market_id}Market details + smart whale consensus + 3 trader rankings (Smart / Top Size / Top Shares). Each ranking has list of top 10 + aggregate metrics (count, invested, avg WR, agg P&L). yes_team_shares / no_team_shares added 6.05.2026 — sorts by share count (matches Polymarket UI Top Holders order).
{
"market": {
"id": "2099029",
"question": "US x Iran permanent peace deal by May 15, 2026?",
"price_yes": 0.085,
"price_no": 0.915,
"category": "GEOPOLITICS",
"volume": 65397286.0
},
"whales": { "yes_count": 321, "no_count": 474, "score": 0.60 },
// Smart Money tab — only smart whales, sorted by USD invested
"yes_team": [...], "no_team": [...],
"yes_team_total_count": 321, "no_team_total_count": 474,
"yes_team_total_invested": 217400, "no_team_total_invested": 1730000,
"yes_team_avg_wr": 67.0, "yes_team_agg_pnl": -117300, "yes_team_agg_pnl_pct": -23.8,
// Top Size tab — all traders, sorted by USD invested
"yes_team_size": [...], "no_team_size": [...],
"yes_team_size_total_count": 2391, "yes_team_size_avg_wr": 36.5,
"yes_team_size_agg_pnl": -117300, ...
// Top Shares tab — all traders, sorted by share count (NEW 6.05.2026)
"yes_team_shares": [
{ "wallet": "0xc851...", "name": "betwick",
"side": "YES", "shares": 246169, "avg_price": 0.119,
"cost": 29368, "pnl": 3234, "pnl_pct": 11.0,
"win_rate": 60.0, "global_pnl": 285000, "is_smart": false }
],
"no_team_shares": [...],
"yes_team_shares_avg_wr": 35.2, "no_team_shares_avg_wr": 47.1,
"yes_team_shares_agg_pnl": ...,
...
}https://orcalayer.com/api/public/v1/whale-flips?days=1&limit=20Smart whales що reversed direction (sign flip) на market у last 24h. Compares today vs N-days-ago snapshot. $200K+ swing required to qualify. Historical N up to 14 days. Strong drama signal — whale changed conviction.
{
"flips": [
{
"wallet": "0x...",
"display_name": "Brokie",
"market_id": "1808970",
"old_side": "YES",
"new_side": "NO",
"old_size_usd": 500000,
"new_size_usd": 700000,
"swing_usd": 1200000,
"win_rate": 75.7,
"lifetime_pnl": 1900000
}
],
"count": 1,
"lookback_days": 1
}https://orcalayer.com/api/public/v1/conviction-clusters?min_whales=3&limit=10Conviction clusters — events where 3+ smart whales aligned same side across 2+ related markets. Aggregate signal stronger than single-market position. Returns event group, side (YES/NO), whale count, market count, combined capital.
{
"clusters": [
{
"event_slug": "presidential-election-winner-2028",
"side": "NO",
"whale_count": 13,
"market_count": 3,
"total_capital_usd": 5458963,
"whales": [{"name": "Kickstand7", "wr": 63.5, "pnl": 1402041}, ...],
"markets": [{"market_id": "123", "question": "..."}]
}
],
"count": 1
}https://orcalayer.com/api/public/v1/isw/statusISW Territory Monitor — all tracked cities with per-market subscriptions. Each city has 1+ subscriptions (single landmark, any-territory polygon, or capture-all polygon). Each subscription has its own threat level computed from polygon intersection with combined ISW shading (control + advance + gains_24h).
{
"summary": {
"total_cities": 51,
"total_subscriptions": 62,
"captured_subscriptions": 2,
"threats_by_subscription": { "CRITICAL": 2, "HIGH": 8, ... }
},
"cities": {
"Kupiansk-Vuzlovyi": {
"coordinates": { "lon": 37.6439, "lat": 49.6605 },
"worst_threat": "SAFE",
"last_check": "2026-04-26T19:50:30",
"subscriptions": [
{
"id": 5,
"label": "Any territory",
"check_type": "enter",
"geometry": [[37.644, 49.673], ...], // closed-ring polygon
"threat": "SAFE",
"coverage": 0.0, // 0..1 polygon area shaded
"closest_distance_m": 3666, // distance to nearest shading
"market_slug_pattern": "will-russia-enter-kupiansk-vuzlovyi-by-*",
"description_excerpt": "Russia captures any territory of Kupiansk-Vuzlovyi...",
"market": { "id": "...", "question": "...", "price_yes": 1.0, "volume": 10652 }
},
{
"id": 6,
"label": "Railroad station",
"check_type": "single",
"geometry": [[37.644326, 49.654359]], // single point
"buffer_advance_m": 250,
"threat": "SAFE",
"point_shaded": false,
"closest_distance_m": 5646,
"market_slug_pattern": "will-russia-capture-kupiansk-vuzlovyi-by-*",
"description_excerpt": "Russia captures the Kupyansjk-Vuzlovij railroad station..."
}
]
}
}
}https://orcalayer.com/api/public/v1/wallet/{addr}/tradesPremium: historical trades for wallet+market+time slice. Required: market (condition_id), before (unix ts). Optional: after (default 0 = from start), limit (default 100, max 500). Auth: Bearer Premium key. Performance note: omitting after on busy wallets does a full scan (~25s); for fast queries pass a narrow time window (e.g. last 7 days).
Authorization: Bearer sk_orca_XXXXXX
GET /api/v2/wallet/0xabc.../trades?market=0xcond...&before=1777000000&after=1776900000&limit=50
{
"wallet": "0xabc...",
"market": "0xcond...",
"count": 12,
"trades": [
{
"id": 588001234,
"timestamp": 1776999500,
"wallet_role": "maker",
"direction": "BUY",
"side": "YES",
"price": 0.245,
"usd_amount": 612.5,
"token_amount": 2500,
"entry_type": "MINT",
"tx_hash": "0xab09...",
"counterparty": "0xdef..."
}
]
}https://orcalayer.com/api/public/v1/market/{condition_id}/whale-tradesPremium: whale-class trades on a market (either maker or taker is in smart_whales). Required: before (unix ts). Optional: min_usd (default 500 — also echoed in response), limit (default 100, max 500). Auth: Bearer Premium key. Performance: ~10-15s on busy markets (resolves condition_id → market_id internally, 20s statement timeout protects against runaway).
Authorization: Bearer sk_orca_XXXXXX
GET /api/v2/market/0xcond.../whale-trades?before=1777000000&min_usd=1000&limit=50
{
"market": "0xcond...",
"count": 23,
"trades": [
{
"timestamp": 1776999000,
"maker": "0xabc...",
"taker": "0xdef...",
"maker_is_whale": true,
"taker_is_whale": false,
"price": 0.245,
"usd_amount": 1200,
"side": "YES",
"entry_type": "MINT"
}
]
}https://orcalayer.com/api/public/v1/wallet/{addr}/market-summary/{condition_id}Premium: wallet position snapshot on a market. Without 'at': current state from wallet_market_stats (per-side YES/NO rows). With 'at=<unix_ts>': reconstructs aggregate from trades up to that timestamp. Auth: Bearer Premium key.
Authorization: Bearer sk_orca_XXXXXX
GET /api/v2/wallet/0xabc.../market-summary/0xcond...?at=1776999000
{
"wallet": "0xabc...",
"condition_id": "0xcond...",
"at": 1776999000,
"found": true,
"sides": {
"YES": {
"buy_tokens": 2500, "buy_cost_usd": 612.5,
"sell_tokens": 0, "sell_cost_usd": 0,
"net_tokens": 2500, "net_cost_usd": 612.5,
"trade_count": 1,
"avg_entry_price": 0.245,
"first_trade_ts": 1776999500, "last_trade_ts": 1776999500
}
}
}https://orcalayer.com/api/public/v1/isw/eventsISW threat events stream — CAPTURED, THREAT_CRITICAL, THREAT_HIGH, etc. Per-subscription. Query params: hours (default 24, max 720), limit (default 50, max 200), city, subscription_id, check_type ('single'/'enter'/'entirety'), min_severity ('SAFE'..'CRITICAL').
{
"events": [
{
"id": 749,
"city": "Kindrashivka",
"event_type": "CAPTURED",
"severity": "CRITICAL",
"details": "Kindrashivka (Any territory) captured",
"timestamp": "2026-04-26 19:49:26.631036",
"subscription_id": 51,
"subscription_label": "Any territory",
"check_type": "enter",
"market_slug_pattern": "will-russia-enter-kindrashivka-by-*",
"coverage": 0.058,
"closest_distance_m": 0
}
],
"count": 1
}https://orcalayer.com/api/public/v1/isw/premium/statusPremium-tier mirror of /isw/status (Bearer auth). Same payload.
Authorization: Bearer sk_orca_XXXXXXhttps://orcalayer.com/api/public/v1/isw/premium/eventsPremium-tier mirror of /isw/events (Bearer auth). Same query params and payload.
Authorization: Bearer sk_orca_XXXXXXReal-time trade stream (SSE)
Server-Sent Events stream of every Polymarket trade event. Our relay forwards events sub-second from Polymarket's on-chain confirmation. Polymarket V2 batches trade submissions on-chain every ~60–120 seconds (gas optimization) — this is the structural settlement window inherited by all on-chain data sources, including ours. Use it for copy-trading bots, live dashboards, or any consumer that would otherwise poll.
https://orcalayer.com/api/public/v1/live/tradesStreams one event: trade frame per on-chain OrderFilled event (V1 + V2). Keep the connection open; the server sends : keepalive comments every 15s when the upstream is quiet.
curl -N -H "Authorization: Bearer sk_orca_XXXXXX" \
https://orcalayer.com/api/public/v1/live/tradesEvent payload (V2 — recommended)
{
"asset": "54709499356...", // ERC-1155 CTF token id
"conditionId": "0xb4766e...",
"eventSlug": "btc-updown-5m-...",
"proxyWallet": "0xd9013df8...", // = maker address
"side": "BUY", // maker side
"price": 0.66, // 0.0 .. 1.0
"size": 4.06, // shares
"outcome": "Up",
"pseudonym": "Dear-Colonial",
"bio": "", // whale's social metadata
"profileImage": "",
"title": "Bitcoin Up or Down - ...",
"timestamp": 1776759129,
"transactionHash": "0xab09c4..."
}Enriched fields (from our indexer)
When you query historical trade data via /wallet/{address} endpoints, each row includes these enriched fields derived from V2 OrdersMatched events:
| Field | Values | Meaning |
|---|---|---|
| entry_type | MINT | MERGE | COMPLEMENTARY | null | MINT = primary market entry. Whale split USDC into a fresh YES+NO share pair (1 USDC → 1 YES + 1 NO) and kept one side. Strongest smart-money signal — capital deployment, not flipping inventory from another holder. MERGE = full exit. Whale combined a YES+NO pair back into USDC (1 YES + 1 NO → 1 USDC). Pre-resolution unwind or profit-take signal. COMPLEMENTARY = NegRisk multi-market auto-spawn. When a whale buys YES on one market in a NegRisk event group (e.g. "X wins 2028 nomination" with 12 candidates), the NegRisk adapter contract auto-creates offsetting NO positions on sister markets. These rows are the auto-spawned legs, routed through adapter contracts 0xe111180000d2663c0091e4f400237545b87b996b and0xe2222d279d744050d28e00520010520000310f59. Useful signal for "this whale is committing across the whole event group" — not one-market noise.null = V1 legacy trade (pre-cutover 28.04.2026), not classified. |
| fee_usd | number | Exchange fee in USDC. Subtract from usd_amount for exact net P&L. |
Limits
- Max 5 concurrent SSE connections per API key
- Each SSE connection counts as 1 against the 600 req/min limit (not per event)
- If upstream drops, the server auto-reconnects up to 5× with exponential backoff before sending
event: error
Examples
import json, requests
KEY = "sk_orca_XXXXXX"
with requests.get(
"https://orcalayer.com/api/public/v1/live/trades",
headers={"Authorization": f"Bearer {KEY}"},
stream=True,
timeout=None,
) as r:
for line in r.iter_lines(decode_unicode=True):
if not line or not line.startswith("data:"):
continue
trade = json.loads(line[5:].strip())
# entry_type is enriched on historical rows; live SSE delivers the raw event.
# Large-wallet MINTs are the strongest smart-money signals:
usd = trade["size"] * trade["price"]
if usd > 10_000:
print(f"🐋 {trade['proxyWallet'][:10]}.. {trade['side']} {trade['outcome']} "
f"USD {usd:.0f} @ {trade['price']}")
// EventSource does NOT support custom headers — pass the key as query param
// via a thin proxy, OR use fetch() with ReadableStream:
const resp = await fetch("https://orcalayer.com/api/public/v1/live/trades", {
headers: { Authorization: "Bearer sk_orca_XXXXXX" }
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buf = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
const events = buf.split("
");
buf = events.pop() || "";
for (const e of events) {
const data = e.split("
").find(l => l.startsWith("data:"));
if (data) console.log(JSON.parse(data.slice(5)));
}
}Code examples
const resp = await fetch("https://orcalayer.com/api/public/v1/whales/leaderboard?limit=5", {
headers: { Authorization: "Bearer " + process.env.ORCA_KEY }
});
const data = await resp.json();
console.log(data.whales);import os, requests
headers = {"Authorization": f"Bearer {os.environ['ORCA_KEY']}"}
r = requests.get("https://orcalayer.com/api/public/v1/whales/leaderboard", params={"limit": 5}, headers=headers)
r.raise_for_status()
for w in r.json()["whales"]:
print(w["name"], w["total_pnl"])import time, requests
headers = {"Authorization": "Bearer sk_orca_XXXXXX"}
while True:
r = requests.get("https://orcalayer.com/api/public/v1/market/1919417", headers=headers).json()
score = r["consensus"]["score"]
print(f"whale_score={score:.2f}")
time.sleep(60) # once per minute, well under 1000/min limitError responses
| Status | Meaning |
|---|---|
| 401 | Missing, invalid, or revoked API key |
| 403 | Key valid but user's plan is not Premium |
| 404 | Endpoint not in public whitelist, or resource not found |
| 429 | Rate limit exceeded — wait Retry-After seconds |
| 502 | Upstream data service temporarily unavailable |
Ready to build?
Premium unlocks the full API, WebSocket feed, and priority support.
Upgrade to Premium — $19.99/moQuestions? [email protected]