OrcaLayer API ยท v1

Public REST API

Programmatic access to Polymarket whale analytics, ISW territory data, and leaderboards. Available on Premium ($19.99/mo).

Client libraries & tools

Official clients wrap this API โ€” or call the plain REST endpoints below from any language.

Quickstart

  1. Sign up and upgrade to Premium
  2. Go to Settings โ†’ API Keys and click Generate Key
  3. Copy the key (format sk_orca_XXXXXX) โ€” it's shown only once
  4. Make authenticated requests:
curl
curl -H "Authorization: Bearer sk_orca_XXXXXX" \
  "https://orcalayer.com/api/public/v1/whales/leaderboard?limit=10"

Authentication

For /api/public/v1/* endpoints, send your API key either as a Bearer token in the Authorization header or as an x-api-key header โ€” both work. Premium endpoints under /api/v2/* (whale-alerts, whales/recent-trades, whale-flips, wallet trades, market whale-trades) require the x-api-key header specifically. Public read endpoints (wallet profiles, market search, leaderboard) need no key.

authorization header
# /api/public/v1/* โ€” either header works:
Authorization: Bearer sk_orca_XXXXXX
x-api-key: sk_orca_XXXXXX

# premium /api/v2/* โ€” use x-api-key:
x-api-key: sk_orca_XXXXXX
  • Missing key โ†’ 401 Unauthorized
  • Revoked / invalid key โ†’ 401
  • Valid key but plan โ‰  premium โ†’ 403 Forbidden
  • Rate limit exceeded โ†’ 429 Too Many Requests with Retry-After: 60

Wins / Losses / Win Rate / Profit Factor โ€” data semantics

As of 2026-05-06, 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 a per-market resolved metric (1 unit per market; NegRisk multi-market events counted once) for these high-volume wallets.
  • profit_factor uses the 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's lifetime PnL (from their leaderboard API) when synced โ€” the 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:

response shape for contract addresses
{
  "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 600 requests/minute (sliding window 60s; your exact cap is reported in the X-RateLimit-Limit header). Anonymous public read endpoints are Cloudflare-throttled per IP (Premium keys have no daily cap; normal browsing on this site is unaffected). Custom enterprise tier available on request. Usage headers on every response:

response headers
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599

Batch 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 support@orcalayer.com with your use case.

Two layers of data

Most endpoints return netted, human-readable data โ€” start there. One advanced endpoint exposes the raw on-chain event stream for builders who want to do their own aggregation.

Layer 1 โ€” Positions & performance (use this for most things)

  • โ€ข /wallet/{addr}/overview โ€” P&L, win rate, volume, profit factor
  • โ€ข /wallet/{addr}/positions ยท /wallet/{addr}/closed โ€” open & closed positions
  • โ€ข /whale/{addr}/backtest โ€” resolved positions for backtesting (avg entry, net cost, P&L, won/lost)
  • โ€ข /whales/leaderboard, /whales/recent-trades

Netted, NegRisk-corrected, farmer-filtered. Each row is a real economic position.

Layer 2 โ€” Raw trade events (advanced)

/wallet/{addr}/trades returns raw fills. Each row carries an entry_type โ€” the settlement mechanics of the CLOB match, not trader intent:

  • โ€ข MINT โ€” the order matched an opposite-side buyer: no seller was in the book, so the protocol minted a new YES+NO pair and split it between the two counterparties (each side receives one leg)
  • โ€ข COMPLEMENTARY โ€” the order matched an existing holder: tokens simply changed hands
  • โ€ข MERGE โ€” two sells matched: the pair was burned back to USDC
  • โ€ข null โ€” legacy V1 orderbook trade (before the 2026-04-28 cutover)

~80% of all fills are MINT โ€” the label depends on who happened to be on the other side at that millisecond. A MINT fill does not mean the wallet deliberately split collateral (deliberate on-chain splits are not orderbook fills and never appear here). side is the direction of the CTF operation, not an exchange buy/sell. Full market-wide history is solid from ~Oct 2025; older data is progressively thinner. For copy-trade backtesting prefer Layer 1.

Endpoints

GEThttps://orcalayer.com/api/public/v1/whales/leaderboard?limit=10&sort=pnl

Top whales ranked by the chosen metric. Params: limit (1-100), sort (pnl | win_rate | volume | trades), min_profit_factor, min_markets, min_wr, min_pnl, max_avg_entry, category, filter (smart | all).

example response (truncated)
{
  "whales": [
    {
      "wallet": "0x56687bf447db6ffa42ffe2204a05edaa20f55839",
      "name": "Theo4",
      "total_pnl": 22053933.75,        // Polymarket lifetime PnL when synced
      "win_rate": 81.8,                 // from closed-positions cache 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
}
GEThttps://orcalayer.com/api/public/v1/wallet/{address}/overview

Full 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.

example response (truncated)
{
  "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,             // 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,                         // from closed-positions cache; per-market fallback for heavy wallets
    "losses": 4,
    "win_rate": 81.8,
    "total_pnl": 22053933.75,           // Polymarket lifetime PnL (their leaderboard/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
}
GEThttps://orcalayer.com/api/public/v1/wallet/{address}/positions?limit=50

Open positions for the wallet with current P&L. Returns the full open-position set in one response โ€” the limit/offset params are accepted but not currently enforced, and order is not guaranteed, so sort client-side (e.g. by current_value).

example response (truncated)
{
  "positions": [
    {
      "question": "Spread: Colombia (-1.5)",
      "condition_id": "0xba9f8cf5...",
      "market_id": "2099029",
      "slug": "fifwc-col-gha-2026-07-03-spread-home-1pt5",
      "side": "token2",                   // token1 = YES leg, token2 = NO leg
      "outcome": "No",
      "tokens": 155629.0,                 // shares held
      "avg_entry": 0.64,                  // average entry price
      "current_price": 0.625,
      "current_value": 97268.12,
      "cost": 99602.56,
      "initial_value": 99602.56,
      "pnl": -2334.43,
      "pnl_pct": -2.3,
      "category": "SPORTS",
      "last_trade_ts": 1751558400
    }
  ],
  "source": "merged",                     // provenance: "merged" = live overlay + cache
  "is_estimated": false,                  // true when values are DB-estimated (overlay not fresh)
  "count": 1
}
GEThttps://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 2026-05-06 โ€” sorts by share count (matches Polymarket UI Top Holders order).

example response (truncated)
{
  "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": 321, "no": 474, "total": 795, "yes_pct": 40.4 },  // smart-whale head-count per side; yes_pct = consensus lean (0-100)

  // 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 (added 2026-05-06)
  "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": ...,
  ...
}
GEThttps://orcalayer.com/api/public/v1/whale-flips?days=1&limit=20

Smart whales that reversed direction (sign flip) on a market in the last 24h. Compares today vs an N-days-ago snapshot. $200K+ swing required to qualify. Historical N up to 14 days. Strong drama signal โ€” the whale changed conviction.

example response (truncated)
{
  "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
}
GEThttps://orcalayer.com/api/public/v1/conviction-clusters?min_whales=3&limit=10

Conviction 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.

example response (truncated)
{
  "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
}
GEThttps://orcalayer.com/api/public/v1/isw/status

ISW 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).

example response (truncated)
{
  "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-07-01T19: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..."
        }
      ]
    }
  }
}
GEThttps://orcalayer.com/api/public/v1/wallet/{addr}/trades

Premium: full raw trade feed for a wallet across all markets. Rows are raw fills โ€” entry_type is the settlement mechanics of the match, NOT trader intent: MINT (matched an opposite-side buyer โ€” the protocol minted a new pair and split it between the two counterparties), MERGE (two sells matched โ€” pair burned back to USDC), COMPLEMENTARY (matched an existing holder โ€” tokens changed hands), or null for legacy V1 orderbook trades before the 2026-04-28 indexer cutover. ~80% of fills are MINT; it does not mean the wallet deliberately split collateral. 'side' is the direction of that CTF operation (maker/taker direction), NOT a plain orderbook buy/sell. Params: since (unix ts lower bound, default = last 30 days; pass since=1 for full archive), before (unix ts upper bound โ€” use for date-range / cursor paging), limit (default 100, max 500), offset. Auth: Premium key (Bearer or x-api-key). Note: history before ~Oct 2025 is sparse (pre-Alchemy backfill).

example response (truncated)
Authorization: Bearer sk_orca_XXXXXX

GET /api/public/v1/wallet/0xabc.../trades?since=1&limit=50

{
  "count": 50,
  "has_more": true,
  "trades": [
    {
      "condition_id": "0x6889...",
      "market_id": "2680257",
      "side": "BUY",
      "outcome": "No",
      "size": 10146.1,
      "price": 0.48,
      "usd_amount": 4870.13,
      "timestamp": 1782673162,
      "transaction_hash": "0x5be0...",
      "entry_type": "MINT",
      "title": "Will Canada win on 2026-06-28?",
      "slug": "fifwc-rsa-can-2026-06-28-can"
    }
  ]
}
GEThttps://orcalayer.com/api/public/v1/wallet/{addr}/market-trades

Premium: historical trades for wallet+market+time slice (same CTF-event semantics as the full feed). 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).

example response (truncated)
Authorization: Bearer sk_orca_XXXXXX

GET /api/public/v1/wallet/0xabc.../market-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..."
    }
  ]
}
GEThttps://orcalayer.com/api/public/v1/market/{condition_id}/whale-trades

Premium: 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).

example response (truncated)
Authorization: Bearer sk_orca_XXXXXX

GET /api/public/v1/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"
    }
  ]
}
GEThttps://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.

example response (truncated)
Authorization: Bearer sk_orca_XXXXXX

GET /api/public/v1/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
    }
  }
}
GEThttps://orcalayer.com/api/public/v1/isw/events

ISW 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').

example response (truncated)
{
  "events": [
    {
      "id": 749,
      "city": "Kindrashivka",
      "event_type": "CAPTURED",
      "severity": "CRITICAL",
      "details": "Kindrashivka (Any territory) captured",
      "timestamp": "2026-07-01 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
}
GEThttps://orcalayer.com/api/public/v1/isw/premium/status

Premium-tier mirror of /isw/status (Bearer auth). Same payload.

example response (truncated)
Authorization: Bearer sk_orca_XXXXXX
GEThttps://orcalayer.com/api/public/v1/isw/premium/events

Premium-tier mirror of /isw/events (Bearer auth). Same query params and payload.

example response (truncated)
Authorization: Bearer sk_orca_XXXXXX
GEThttps://orcalayer.com/api/analytics/smart-money-flow

Free, no key. Smart-money volume flow by category over the last 24h vs the prior window โ€” where the whales are rotating capital.

example response (truncated)
GET /api/analytics/smart-money-flow

{
  "flow": [
    {
      "category": "GEOPOLITICS",
      "markets": 818,
      "whales_now": 224105,
      "volume_24h": 2007809.06,
      "volume_prev": 1557691.04,
      "volume_delta_pct": 28.9,
      "wallets_24h": 43
    }
  ]
}
GEThttps://orcalayer.com/api/analytics/whale-clusters

Free, no key. Markets where multiple whales are stacked on the same side right now โ€” combined capital and the wallets behind it.

example response (truncated)
GET /api/analytics/whale-clusters

{
  "clusters": [
    {
      "market_id": "1897306",
      "question": "Will Korea Republic win on 2026-06-24?",
      "category": "SPORTS",
      "side": "token2",
      "whale_count": 8,
      "combined_usd": 59852.27,
      "wallets": [{ "wallet": "0x0346...", "name": "GoalLineGhost" }]
    }
  ]
}
GEThttps://orcalayer.com/api/analytics/top-movers

Free, no key. Wallets with the biggest PnL swing in the recent window, with a sparkline of their trajectory.

example response (truncated)
GET /api/analytics/top-movers

{
  "winners": [
    {
      "wallet": "0xa5ea...",
      "display_name": "bossoskil1",
      "pnl_now": -3672577.64,
      "delta": 5987768.15,
      "win_rate": 48.2,
      "closed_count": 1752,
      "sparkline": [-9660345, -3401876, -3672577]
    }
  ]
}
GEThttps://orcalayer.com/api/analytics/whale-volume-24h

Free, no key. Hourly YES/NO whale volume and trade counts over the last 24h โ€” a heartbeat of smart-money activity.

example response (truncated)
GET /api/analytics/whale-volume-24h

{
  "series": [
    { "hour": "2026-06-22T17:00:00Z", "yes_volume": 2843611.57, "no_volume": 4400535.67, "trades": 1555 }
  ]
}
GEThttps://orcalayer.com/api/v2/wallet/{addr}/similar-traders

Free, no key. Wallets that trade like this one โ€” same categories, comparable profile โ€” for discovery and copy-candidate research.

example response (truncated)
GET /api/v2/wallet/0xabc.../similar-traders

{
  "wallet": "0xabc...",
  "primary_category": "CRYPTO",
  "similar": [
    {
      "wallet": "0x55be...",
      "name": "coinman2",
      "profit_factor": 1.19,
      "win_rate": 57.5,
      "shared_categories": ["CRYPTO"],
      "overlap_label": "Crypto"
    }
  ]
}
GEThttps://orcalayer.com/api/v2/conviction-clusters

Premium (X-API-Key). Events where a cohort of profitable whales agrees on one side โ€” 'where the smart money is convinced', with each whale's win-rate and PnL.

example response (truncated)
X-API-Key: sk_orca_XXXXXX

GET /api/v2/conviction-clusters

{
  "clusters": [
    {
      "event_slug": "presidential-election-winner-2028",
      "side": "NO",
      "whale_count": 20,
      "market_count": 3,
      "total_capital_usd": 5756923.05,
      "whales": [{ "wr": 60.5, "pnl": 667926, "name": "11122", "wallet": "0x011f..." }]
    }
  ]
}
GEThttps://orcalayer.com/api/v2/wallet/{addr}/ai-analysis

Premium (X-API-Key). LLM-generated dossier on a wallet โ€” trading style, edges, risks. Async: the first call queues generation, poll again after ~60s for the finished report.

example response (truncated)
X-API-Key: sk_orca_XXXXXX

GET /api/v2/wallet/0xabc.../ai-analysis

# first call queues it:
{ "status": "pending", "retry_after_seconds": 60 }

# poll again after ~60s for the finished dossier
GEThttps://orcalayer.com/api/v2/whale/{addr}/backtest

Premium (X-API-Key). Per-position resolved history for a whale โ€” entry price, net cost, realized PnL, won/lost โ€” to backtest a copy strategy.

example response (truncated)
X-API-Key: sk_orca_XXXXXX

GET /api/v2/whale/0xabc.../backtest

{
  "wallet": "0xabc...",
  "positions": [
    {
      "market_id": "2132768",
      "question": "Will Bitcoin reach $150,000 in May?",
      "side": "NO",
      "avg_entry_price": 0.9971,
      "net_cost": 606386.01,
      "pnl": 870.29,
      "won": true,
      "category": "CRYPTO"
    }
  ]
}
GEThttps://orcalayer.com/api/v2/whale/{addr}/history

Premium (X-API-Key). Whale summary (PnL, win-rate, profit-factor) plus full resolved-market history.

example response (truncated)
X-API-Key: sk_orca_XXXXXX

GET /api/v2/whale/0xabc.../history

{
  "whale": {
    "wallet": "0xabc...",
    "name": "LucasMeow",
    "total_pnl": 376215.57,
    "win_rate": 97.5,
    "profit_factor": 174.91,
    "resolved_markets": 161
  },
  "history": [{ "market_id": "2410563", "question": "Will Bitcoin reach $87,500 in June?", "category": "CRYPTO", "side": "NO" }]
}
GEThttps://orcalayer.com/api/v2/whale-alerts/big-positions

Premium (X-API-Key). Largest open whale positions right now โ€” size, entry vs current price, cost basis and live value.

example response (truncated)
X-API-Key: sk_orca_XXXXXX

GET /api/v2/whale-alerts/big-positions

{
  "items": [
    {
      "wallet": "0x1887...",
      "wallet_name": "tubeyou",
      "market_title": "Will Manchester City FC win on 2026-05-19?",
      "side": "Yes",
      "size": 8269545.6,
      "avg_price": 0.6092,
      "current_price": 0.305,
      "cost_basis": 5037807.0
    }
  ]
}
GEThttps://orcalayer.com/api/v2/whale-alerts/summary

Premium (X-API-Key). Rolling stat-strip over a window (default 1440 min): total alerts, total volume, biggest single move, most active whale.

example response (truncated)
X-API-Key: sk_orca_XXXXXX

GET /api/v2/whale-alerts/summary?minutes=1440

{
  "window_minutes": 1440,
  "total_alerts": 13016,
  "total_volume": 57294668.26,
  "biggest_single": { "usd_amount": 423955.91, "question": "Argentina vs. Austria: O/U 2.5" },
  "most_active_whale": { "name": "swisstony", "trades": 4169 }
}
POSThttps://orcalayer.com/api/v2/isw/webhook/register

Premium (X-API-Key). Subscribe a URL to receive ISW Ukraine territory events as they publish โ€” push instead of poll. Body: url (required), cities ('all' or comma-separated). Unregister: DELETE /api/v2/isw/webhook/unregister with the same body.

example response (truncated)
X-API-Key: sk_orca_XXXXXX
Content-Type: application/json

POST /api/v2/isw/webhook/register
{ "url": "https://your-server.com/hook", "cities": "all" }

{ "status": "registered", "url": "https://your-server.com/hook", "cities": "all" }

Real-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.

GET ยท SSEhttps://orcalayer.com/api/public/v1/live/trades

Streams one event: trade frame per fill. Keep the connection open; the server sends : keepalive comments every 15s when the upstream is quiet.

NEW (22.07.2026): every event now carries settlement_type โ€”"MINT" | "MERGE" | "COMPLEMENTARY" | null โ€” derived live by grouping fills of the same transactionHash in a fixed 700ms buffer (settlement_hold_ms), ~60โ€“120s before the trade appears on-chain. Shadow-verified against on-chain OrdersMatched over 205,920 events: MINT/MERGE accuracy 100.000% (54,470 tx, zero mislabels), coverage 91.8%. Semantics: the label is tx-level (all fills of one match share it);COMPLEMENTARY means the tx contains a complementary component (pure-COMPL txs don't exist in V2 matching); null is an honest refusal (lone fill at the buffer edge, complex multi-match batch) โ€” never read it as "not a mint". Settlement mechanics, not trader intent.

curl
curl -N -H "Authorization: Bearer sk_orca_XXXXXX" \
  https://orcalayer.com/api/public/v1/live/trades

Event payload (V2 โ€” recommended)

event: trade
{
  "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...",
  "relay_received_at_ms": 1784634290778,  // when our relay got it (before hold)
  "settlement_type": "MINT",              // MINT | MERGE | COMPLEMENTARY | null
  "settlement_hold_ms": 700               // fixed derivation buffer
}

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:

FieldValuesMeaning
entry_typeMINT | MERGE | COMPLEMENTARY | nullSettlement mechanics of the CLOB match โ€” not trader intent. The label depends on who happened to be on the other side at that millisecond.
MINT = the order matched an opposite-side buyer: no seller was in the book, so the protocol minted a new YES+NO pair and split it between the two counterparties (each side receives one leg). ~80% of all fills. Side-signal: the order swept available liquidity โ€” an aggressive entry โ€” but it does NOT mean the wallet deliberately split collateral.
MERGE = two sells matched: the pair was burned back to USDC.
COMPLEMENTARY = the order matched an existing holder: tokens simply changed hands.
null = V1 legacy trade (pre-cutover 2026-04-28), not classified.
fee_usdnumberExchange 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

python (filter MINT trades only)
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())
        # settlement_type is derived live (tx-level, 100.000% verified on
        # MINT/MERGE). null = honest refusal, not "not a mint".
        usd = trade["size"] * trade["price"]
        if usd > 10_000 and trade.get("settlement_type") == "MINT":
            print(f"๐Ÿ‹ {trade['proxyWallet'][:10]}.. {trade['side']} {trade['outcome']} "
                  f"USD {usd:.0f} @ {trade['price']} [{trade['settlement_type']}]")
javascript (browser EventSource)
// 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

javascript (node / browser)
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);
python (requests)
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"])
python (polling whale consensus on a market)
import time, requests
headers = {"Authorization": "Bearer sk_orca_XXXXXX"}
while True:
    resp = requests.get("https://orcalayer.com/api/public/v1/market/1919417", headers=headers, timeout=15)
    resp.raise_for_status()
    whales = resp.json()["whales"]   # {"yes", "no", "total", "yes_pct"}
    print(f"smart-money YES={whales['yes_pct']:.1f}%  ({whales['yes']} yes / {whales['no']} no)")
    time.sleep(60)  # once per minute, well under 600/min limit

Error responses

StatusMeaning
401Missing, invalid, or revoked API key (on key-gated endpoints; public reads need no key)
403Key valid but user's plan is not Premium
404Endpoint not in public whitelist, or resource not found
429Rate limit exceeded โ€” wait Retry-After seconds
502Upstream data service temporarily unavailable

Ready to build?

Premium unlocks the full API, SSE live feed, and priority support.

Upgrade to Premium โ€” $19.99/mo

Questions? support@orcalayer.com