API Reference v1

Build on Unhedged.

A REST API for prediction markets and Continuous Trading Markets (CTM). Public endpoints for market data; authenticated endpoints for placing bets, submitting predictions, and managing your balance.

Authentication

Pass your API key as a Bearer token in the Authorization header. Public endpoints (most market reads) work without a key.

Base URL
https://api.unhedged.gg
Key Prefix
ak_*
Authorization: Bearer ak_your_key_here

Quickstart

Place your first bet in three steps.

  1. 1 Create an API key

    Sign in and visit Account → API Keys. Default scopes cover reading and betting; opt-in for balance:withdraw only if you need it.

  2. 2 Find an active market
    curl https://api.unhedged.gg/api/v1/feed?status=ACTIVE&limit=5
  3. 3 Place a bet
    curl -X POST https://api.unhedged.gg/api/v1/bets \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{"marketId":"clx...","outcomeIndex":0,"amount":50}'

Scopes

Each API key has a fixed set of scopes. Endpoints that require a scope are marked below the endpoint title. Withdrawals are gated behind an explicit opt-in scope.

market:read Read market and CTM round data. Most market endpoints allow unauthenticated reads.
balance:read Read available, locked, deposited, withdrawn, and transaction amounts.
balance:withdraw Initiate withdrawals from available balance. Disabled by default.
bet:place Place bets on markets and submit CTM predictions.
bet:read Read your bets, predictions, and positions.
portfolio:read Read portfolio positions, equity snapshots, and history records.
user:read Read your own profile via GET /api/v1/users/me.
user:write Update only your trading preferences via PATCH /api/v1/users/me/preferences. Selected by default in Account → API Keys. Include it explicitly when creating keys through the API. Does not grant general profile editing.
order:place Place orders on ORDER_BOOK markets.
order:cancel Cancel your open matching-engine orders.
order:read Read your matching-engine order history.

Rate Limits

Limits are applied per IP. Endpoint-specific limits are listed on each endpoint card. When exceeded you'll receive 429 Too Many Requests — back off and retry.

Global default 600 req/min
Bets / authenticated reads 60 req/min
Bets per market per IP 15 req/min
CTM predict 80 req/min
Order book (place / cancel / read) 120 req/min · 60 per market
Price history 300 req/min
Withdrawals 5 req/min
API key creation 10 req/hour

Errors

Errors return a JSON body of the shape { "error": "ErrorType", "message": "..." }. Some 4xx responses include an additional code field for machine-readable handling.

400 Bad request — invalid params, malformed body, or insufficient balance
401 Unauthorized — missing or invalid API key
403 Forbidden — key lacks required scope, account suspended, or email not verified
404 Not found — market, round, or bet doesn't exist
409 Conflict — idempotency key reused with different payload
429 Rate limited — slow down. Backoff and retry after a few seconds
503 Service unavailable — feed or portfolio calculation capacity is full, or a feature is temporarily disabled. Capacity responses include a Retry-After header.

Portfolio capacity and retries

Full equity and equity history share a configurable limit on concurrent calculations per API instance. When capacity is full, the server rejects the read immediately with the response below. The equity summary uses a separate path. This capacity limit is separate from the per-user rate limits that return 429.

Portfolio overload response
HTTP/1.1 503 Service Unavailable
Retry-After: 1
Cache-Control: no-store
Content-Type: application/json

{
  "statusCode": 503,
  "error": "Service Unavailable",
  "message": "Portfolio calculations are busy. Please retry shortly."
}

Retry-After is an HTTP response header, not a JSON field. Its value here is a delay in seconds. Wait at least that long before retrying the same GET. If overload continues, increase the delay with exponential backoff and jitter, and bound the retry count. Avoid parallel retries for the same read. A retry is a new request and still counts toward the endpoint's rate limit; the header does not reserve capacity or guarantee success. Clients must implement this retry behavior. Keep failed financial reads unavailable rather than displaying zero or an old value.

Markets

GET /api/v1/feed PUBLIC
List Feed (Markets + Groups) Sorted stream of standalone markets and Kalshi-style market groups from the public markets page source. Each item has type: "market" or "binary_group". Grouped child markets appear inside their group, not as separate feed items. For category=esports the response also carries gameCounts: per-title totals across the whole filtered catalog (all pages), used by the esports game rail. No API key required. Anonymous responses may reuse a feed snapshot for one second. When feed capacity is full, returns 503 with Retry-After: 1.
Rate limit: 120 req/min per IP
Query Parameters
status string — ACTIVE | ENDED | RESOLVED | VOIDED
category string — Filter by category (max 50 chars)
tag string — Comma-separated tag filter (max 500 chars)
search string — Search query (2–200 chars)
sort string — ending_soonest | ending_latest | newest | biggest_pool | most_bets (default ending_soonest)
limit number — Results per page (1–100, default 20)
offset number — Pagination offset (default 0)
Example
curl https://api.unhedged.gg/api/v1/feed
Response
{
  "items": [
    {
      "type": "market",
      "market": {
        "id": "clx...",
        "slug": "will-btc-hit-100k-by-june",
        "question": "Will BTC hit $100k by June?",
        "status": "ACTIVE",
        "category": "Crypto",
        "totalPool": "1500.0000000000",
        "endTime": "2026-06-01T00:00:00.000Z",
        ...
      }
    },
    {
      "type": "binary_group",
      "group": {
        "id": "clg...",
        "slug": "who-wins-the-2028-election",
        "title": "Who will win the 2028 election?",
        "category": "Politics",
        "marketCount": 5,
        "earliestStartTime": "2028-11-07T00:00:00.000Z",
        "earliestEndTime": "2028-11-07T00:00:00.000Z",
        "aggregateVolume": "12500.0000000000",
        "markets": [
          { "id": "clm...", "groupLabel": "Candidate A", "status": "ACTIVE", "outcomes": [ ... ], "orderBookStats": { ... }, ... }
        ]
      }
    }
  ],
  "total": 87,
  "marketTotal": 62,
  "groupTotal": 25,
  "activeCount": 40,
  "endedCount": 12,
  "resolvedCount": 35,
  "gameCounts": { "mlbb": 0, "lol": 58, "cs2": 13, "dota2": 6, "valorant": 7 }
}
GET /api/v1/markets/:id PUBLIC
Get Market Details Returns one market with outcomes, pool sizes, odds, and status. No API key required.
Rate limit: 600 req/min
Example
curl https://api.unhedged.gg/api/v1/markets/<id>
Response
{
  "market": {
    "id": "clx...",
    "question": "Will BTC hit $100k by June?",
    "description": "Resolves Yes if...",
    "status": "ACTIVE",
    "category": "Crypto",
    "outcomes": [
      { "index": 0, "label": "Yes" },
      { "index": 1, "label": "No" }
    ],
    "totalPool": "1500.0000000000",
    "endTime": "2026-06-01T00:00:00.000Z",
    "createdAt": "2026-01-15T...",
    ...
  }
}
GET /api/v1/balance AUTH
Get Balance Returns available, locked, deposited, withdrawn, and total USD amounts. Balance amounts are read from the database on each request; responses use Cache-Control: no-store. Held CC balances use CC; ccPriceUsd is the USD price of one CC.
Scope: balance:read Rate limit: 60 req/min
Example
curl https://api.unhedged.gg/api/v1/balance \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "balance": {
    "available": "500.0000000000",
    "lockedWithdraws": "0.0000000000",
    "lockedBets": "150.0000000000",
    "lockedEngine": "0.0000000000",
    "total": "650.0000000000",
    "totalDeposited": "1000.0000000000",
    "totalWithdrawn": "200.0000000000",
    "ccAvailable": "25.0000000000",
    "ccLocked": "0.0000000000"
  },
  "withdrawalFee": "1.0000000000",
  "bettingFee": "0.0000000000",
  "ccPriceUsd": 0.015
}
POST /api/v1/bets AUTH
Place a Bet Creates a bet on one market outcome and deducts the amount from available balance.
Scope: bet:place Rate limit: 60 req/min globally · 15 req/min per market per IP
Request Body
marketId string * — Market ID to bet on
outcomeIndex number * — Index of the outcome (0-based)
amount number * — Amount in USD (min 0.0001)
idempotencyKey string — Optional dedup key to prevent double bets
Example
curl -X POST https://api.unhedged.gg/api/v1/bets \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "marketId": "clx...",
  "outcomeIndex": 0,
  "amount": 50
}'
Response
{
  "bet": {
    "id": "clx...",
    "marketId": "clx...",
    "outcomeIndex": 0,
    "amount": "50.0000000000",
    "status": "CONFIRMED",
    "timeWeight": "1.0000"
  },
  "balanceAfter": "450.0000000000",
  "fee": "0.0000000000"
}
GET /api/v1/bets AUTH
List Your Bets Returns your bets, optionally filtered by status or marketId.
Scope: bet:read Rate limit: 60 req/min
Query Parameters
status string — PENDING | CONFIRMED | WON | LOST | REFUNDED
marketId string — Filter by market
limit number — Results per page (1–100, default 20)
offset number — Pagination offset
Example
curl https://api.unhedged.gg/api/v1/bets \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "bets": [
    {
      "id": "clx...",
      "marketId": "clx...",
      "market": {
        "question": "Will BTC hit $100k?",
        "status": "ACTIVE",
        "outcomes": [...]
      },
      "outcomeIndex": 0,
      "amount": "50.0000000000",
      "status": "CONFIRMED",
      "createdAt": "2026-02-20T..."
    }
  ],
  "total": 5
}

Users

PATCH /api/v1/users/me/preferences AUTH
Update Trading Preferences Set your account-wide autoBurn preference, also available in Account Settings. Accepts login sessions and API keys with user:write (selected by default in Account → API Keys; include it explicitly when creating keys through the API). Only a boolean autoBurn is accepted; other fields are rejected. Enabled by default: the engine redeems complete available sets for $1 each. A set contains one share of every outcome (UP + DOWN in a binary market). Shares locked in resting orders are excluded. Set false to keep the shares until you sell them or the market settles. Saving does not immediately redeem existing sets: enabling applies at the next eligible fill or ask unlock. Disabling does not reverse earlier burns, and final market settlement is unchanged. The general PATCH /users/:userId profile endpoint remains unavailable to API keys.
Scope: user:write Rate limit: 60 req/min
Request Body
autoBurn boolean * — true: automatically redeem complete available sets; false: keep the shares. Applies across all markets.
Example
curl -X PATCH https://api.unhedged.gg/api/v1/users/me/preferences \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "autoBurn": false }'
Response
{ "autoBurn": false }
GET /api/v1/users/me AUTH
Get Current User Returns your profile (id, partyId, username, email, preferences). Accepts login sessions and API keys with user:read. The autoBurn preference defaults to true; change it in Account Settings or PATCH /api/v1/users/me/preferences.
Scope: user:read Rate limit: 60 req/min
Example
curl https://api.unhedged.gg/api/v1/users/me \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "id": "clx1abc2d0000abcdefghij",
  "partyId": "namespace::fingerprint",
  "username": "trader_jane",
  "email": "[email protected]",
  "authProvider": "email",
  "avatarUrl": "https://cdn.unhedged.gg/avatars/clx....webp",
  "role": "USER",
  "showInLeaderboard": true,
  "autoBurn": true,
  "emailVerified": true,
  "onboardingComplete": true,
  "referralCode": "ABCD2345",
  "createdAt": "2026-01-15T12:00:00.000Z"
}

Portfolio

GET /api/v1/portfolio/me AUTH
Get Portfolio Returns all-time summary counts, a preview of up to 50 active/ended positions, and the 10 most recent payouts. positionsTotal is the complete active/ended row count represented by /portfolio/me/positions; use that paginated endpoint for positions beyond the preview.
Scope: portfolio:read Rate limit: 1,000,000 req/min
Example
curl https://api.unhedged.gg/api/v1/portfolio/me \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "portfolio": {
    "summary": {
      "totalBets": 12,
      "totalWagered": "850.0000000000",
      "totalWins": 7,
      "totalLosses": 5,
      "pendingBets": 2,
      "pendingOrderBookPositions": 3,
      "pendingOrderBookCostBasis": "124.5000000000",
      "totalPayouts": "1200.0000000000",
      "totalProfit": "350.0000000000",
      "winRate": 58.33,
      "roi": 41.18
    },
    "positions": [...],
    "positionsTotal": 61,
    "recentPayouts": [...]
  }
}
GET /api/v1/portfolio/me/positions AUTH
Get Active Positions Returns unresolved market positions, including ORDER_BOOK positions. The default page size is 50 and the maximum is 500; use limit and offset to read beyond the first page. ORDER_BOOK positions carry full share-inventory mechanics — quantity, availableQuantity, lockedQuantity, averagePrice, markPrice, feesPaid, openOrderCount, and canSell — so you can read share counts here in one paginated call (use marketType=ORDER_BOOK) instead of polling /portfolio/me/shares per market. These fields are omitted for PARIMUTUEL positions. canSell reflects whether the market is open for trading (ACTIVE and before its cutoff) with shares available to sell.
Scope: portfolio:read Rate limit: 1,000,000 req/min
Query Parameters
marketId string — Optional filter to a single market.
marketType string — PARIMUTUEL | ORDER_BOOK
limit number — Results per page (default 50, 1–500).
offset number — Pagination offset (default 0).
Example
curl https://api.unhedged.gg/api/v1/portfolio/me/positions \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "positions": [
    {
      "marketId": "clx...",
      "marketType": "ORDER_BOOK",
      "outcomeIndex": 0,
      "outcomeName": "YES",
      "totalBetAmount": "64.5000000000",
      "betCount": 2,
      "currentValue": "71.2000000000",
      "unrealizedPnl": "6.7000000000",
      "unrealizedPnlPercent": 10.39,
      "quantity": 100,
      "availableQuantity": 97,
      "lockedQuantity": 3,
      "averagePrice": "0.6450000000",
      "markPrice": "0.7120000000",
      "feesPaid": "0.5000000000",
      "openOrderCount": 1,
      "canSell": true,
      "createdAt": "2026-04-12T15:33:21.000Z",
      "market": {
        "question": "Will BTC be up at 12:00 UTC?",
        "status": "ACTIVE",
        "outcomes": [
          { "index": 0, "label": "YES", "engineId": "out_0" },
          { "index": 1, "label": "NO", "engineId": "out_1" }
        ]
      }
    }
  ],
  "total": 1
}
GET /api/v1/portfolio/me/shares AUTH
Get Order-Book Shares Returns current ORDER_BOOK share inventory: every ACTIVE and ENDED share row, including zero balances. Pass marketId after a fill to refresh one order ticket (uncached, VOIDED excluded, cannot be combined with status, limit, or cursor). Pass status=RESOLVED for one page of resolved history: omit cursor on page 1, pass nextCursor as cursor until nextCursor is null. limit and cursor require status=RESOLVED. Collections are cached for 10 seconds; exact-market reads bypass the cache.
Scope: portfolio:read Rate limit: 60 req/min (120 req/min with ?marketId=)
Query Parameters
marketId string — Exact market (uncached). Cannot be combined with status, limit, or cursor.
status string — RESOLVED for history. Omit for current inventory.
limit number — History page size, 1–100. Default 100. Requires status=RESOLVED.
cursor string — Previous page nextCursor. Omit on page 1. Requires status=RESOLVED.
Example
curl https://api.unhedged.gg/api/v1/portfolio/me/shares \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "shares": [
    {
      "marketId": "clx...",
      "outcome": "YES",
      "available": 12,
      "locked": 3,
      "total": 15,
      "costAmount": "9.6750000000",
      "updatedAt": "2026-04-12T15:34:01.000Z",
      "market": {
        "question": "Will BTC be up at 12:00 UTC?",
        "status": "ACTIVE",
        "outcomes": [
          { "index": 0, "label": "YES", "engineId": "out_0" },
          { "index": 1, "label": "NO", "engineId": "out_1" }
        ]
      }
    }
  ],
  "nextCursor": null
}

// history pages carry nextCursor until the last page:
// GET /api/v1/portfolio/me/shares?status=RESOLVED&limit=100&cursor=<nextCursor>
GET /api/v1/portfolio/me/equity-summary AUTH
Get Equity Summary Returns cash, locked balances, and the marked value of open ORDER_BOOK positions from one database snapshot. Each request reads the database and returns Cache-Control: no-store. Use /api/v1/portfolio/me/equity for day and seven-day P/L.
Scope: portfolio:read Rate limit: 120 req/min per user
Example
curl https://api.unhedged.gg/api/v1/portfolio/me/equity-summary \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "equity": {
    "total": "1320.4500000000",
    "available": "1100.0000000000",
    "lockedWithdraws": "0.0000000000",
    "lockedBets": "95.9500000000",
    "lockedEngine": "60.0000000000",
    "unrealizedClob": "64.5000000000",
    "updatedAt": "2026-04-12T15:35:00.000Z",
    "asOf": "2026-04-12T15:35:00.000Z"
  }
}
GET /api/v1/portfolio/me/equity AUTH
Get Equity Snapshot Returns available cash, locked balances, open ORDER_BOOK mark-to-market value, and rolling P/L from one database snapshot. Each request reads the database and returns Cache-Control: no-store. The optional fresh parameter remains accepted; all reads are uncached. Use /api/v1/portfolio/me/equity-summary for header totals that do not need rolling P/L. When calculation capacity is full, returns HTTP 503 with Retry-After: 1 (seconds). Wait at least that long before retrying; see Errors for the response body and retry guidance.
Scope: portfolio:read Rate limit: 30 req/min per user
Query Parameters
fresh string — Optional compatibility parameter. All reads are uncached.
Example
curl https://api.unhedged.gg/api/v1/portfolio/me/equity \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "equity": {
    "total": "1320.4500000000",
    "available": "1100.0000000000",
    "lockedWithdraws": "0.0000000000",
    "lockedBets": "95.9500000000",
    "lockedEngine": "60.0000000000",
    "unrealizedClob": "64.5000000000",
    "dayChange": "18.2500000000",
    "dayChangePercent": 1.40,
    "sevenDayChange": "84.0000000000",
    "sevenDayChangePercent": 6.80,
    "dayPnl": "18.2500000000",
    "sevenDayPnl": "84.0000000000",
    "updatedAt": "2026-04-12T15:35:00.000Z",
    "asOf": "2026-04-12T15:35:00.000Z"
  }
}
GET /api/v1/portfolio/me/equity-history AUTH
Get Equity History Returns funding-neutral P/L chart points for the past day or week from one database snapshot. Each request reads the database and returns Cache-Control: no-store. The equity field in each point is cumulative P/L within the selected window. When calculation capacity is full, returns HTTP 503 with Retry-After: 1 (seconds). Wait at least that long before retrying; see Errors for the response body and retry guidance.
Scope: portfolio:read Rate limit: 30 req/min per user
Query Parameters
range string — 1D or 1W. Default 1D.
Example
curl https://api.unhedged.gg/api/v1/portfolio/me/equity-history \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "range": "1D",
  "points": [
    { "t": "2026-04-11T15:35:00.000Z", "equity": "0.0000000000" },
    { "t": "2026-04-11T16:35:00.000Z", "equity": "2.5000000000" }
  ]
}
GET /api/v1/portfolio/me/history AUTH
Get Position History Returns resolved and voided market positions from newest to oldest by market.resolutionTime. The default page size is 50 and the maximum is 500. ORDER_BOOK pagination is by market, so one market can return multiple outcome rows. Use status=RESOLVED|VOIDED and startTime/endTime to filter a resolution window, for example /api/v1/portfolio/me/history?marketType=ORDER_BOOK&status=RESOLVED&startTime=2026-04-01T00:00:00.000Z&endTime=2026-04-30T23:59:59.999Z.
Scope: portfolio:read Rate limit: 1,000,000 req/min
Query Parameters
marketId string — Optional filter to a single market.
marketType string — PARIMUTUEL | ORDER_BOOK
status string — RESOLVED | VOIDED
startTime string — Optional inclusive lower bound on market.resolutionTime. ISO 8601 date-time.
endTime string — Optional inclusive upper bound on market.resolutionTime. ISO 8601 date-time.
limit number — Results per page (default 50, 1–500).
offset number — Pagination offset (default 0).
Example
curl https://api.unhedged.gg/api/v1/portfolio/me/history \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "positions": [
    {
      "marketId": "clx...",
      "marketType": "ORDER_BOOK",
      "outcomeIndex": 1,
      "outcomeName": "NO",
      "betAmount": "50.0000000000",
      "payout": "92.0000000000",
      "sellProceeds": "0.0000000000",
      "sharesAtExpiry": 92,
      "costAmount": "50.0000000000",
      "realizedCost": "0.0000000000",
      "realizedPnl": "0.0000000000",
      "isWinner": true,
      "createdAt": "2026-04-10T12:00:00.000Z",
      "market": {
        "question": "Will BTC close above $100k?",
        "status": "RESOLVED",
        "winningOutcome": 1,
        "resolutionTime": "2026-04-12T15:35:00.000Z"
      }
    }
  ],
  "total": 1
}

CTM – Continuous Trading Markets

GET /api/v1/ctm/rounds PUBLIC
List CTM Rounds Returns CTM rounds filtered by status, asset, or round type. No API key required.
Rate limit: 600 req/min
Query Parameters
status string — OPEN | HEATMAP | LOCKED | RESOLVED | VOIDED (comma-separated)
asset string — Filter by asset (e.g. BTC, ETH)
roundType string — FLASH | BLITZ | SPRINT | DAILY | WEEKLY | EVENT
limit number — Results per page (1–100, default 20)
offset number — Pagination offset (default 0)
sort string — asc | desc
Example
curl https://api.unhedged.gg/api/v1/ctm/rounds
Response
{
  "rounds": [
    {
      "id": "clx...",
      "asset": "BTC",
      "roundType": "BLITZ",
      "status": "OPEN",
      "question": "Where will BTC be in 5 min?",
      "entryFeeBlind": "10.0000000000",
      "totalPool": "500.0000000000",
      "predictionCount": 12,
      "openStartsAt": "2026-04-05T12:00:00.000Z",
      "heatmapStartsAt": "2026-04-05T12:03:00.000Z",
      "lockedStartsAt": "2026-04-05T12:04:00.000Z",
      "resolvesAt": "2026-04-05T12:05:00.000Z",
      ...
    }
  ],
  "total": 58
}
GET /api/v1/ctm/rounds/:id PUBLIC
Get Round Details Returns one CTM round with phase times, fees, pool size, and settlement fields. No API key required.
Rate limit: 600 req/min
Example
curl https://api.unhedged.gg/api/v1/ctm/rounds/<id>
Response
{
  "round": {
    "id": "clx...",
    "asset": "BTC",
    "roundType": "BLITZ",
    "status": "OPEN",
    "question": "Where will BTC be in 5 min?",
    "description": "...",
    "entryFeeBlind": "10.0000000000",
    "heatmapFeeMultiplier": "2.0000000000",
    "platformFeeRate": "0.0500000000",
    "totalPool": "500.0000000000",
    "predictionCount": 12,
    "openStartsAt": "2026-04-05T12:00:00.000Z",
    "heatmapStartsAt": "2026-04-05T12:03:00.000Z",
    "lockedStartsAt": "2026-04-05T12:04:00.000Z",
    "resolvesAt": "2026-04-05T12:05:00.000Z",
    "settlementPrice": null,
    "resolvedAt": null,
    ...
  }
}
GET /api/v1/ctm/rounds/:id/heatmap PUBLIC
Get Heatmap Returns prediction counts by price bucket for one round. No API key required.
Rate limit: 600 req/min
Query Parameters
bucketCount number — Number of buckets (5–50, default 20)
Example
curl https://api.unhedged.gg/api/v1/ctm/rounds/<id>/heatmap
Response
{
  "heatmap": [
    { "min": 83000, "max": 83500, "count": 3 },
    { "min": 83500, "max": 84000, "count": 7 },
    ...
  ]
}
GET /api/v1/ctm/rounds/:id/results PUBLIC
Get Round Results Returns settlement price, payout totals, and top predictions for one resolved round. No API key required.
Rate limit: 600 req/min
Example
curl https://api.unhedged.gg/api/v1/ctm/rounds/<id>/results
Response
{
  "round": { ... },
  "result": {
    "settlementPrice": "84250.5000000000",
    "totalPool": "2000.0000000000",
    "platformFee": "100.0000000000",
    "payoutPool": "1900.0000000000",
    "totalPredictions": 45,
    "uniquePlayers": 32,
    "resolvedAt": "2026-04-05T12:05:00.000Z"
  },
  "topPredictions": [...]
}
GET /api/v1/ctm/rounds/:id/price-history PUBLIC
Get Price History Returns a round asset's price history, downsampled to at most 600 points. No API key required.
Rate limit: 300 req/min
Example
curl https://api.unhedged.gg/api/v1/ctm/rounds/<id>/price-history
Response
{
  "prices": [
    { "price": 84100.5, "bid": 84099.0, "ask": 84102.0, "timestamp": 1712318400000 },
    { "price": 84110.2, "bid": 84109.0, "ask": 84111.5, "timestamp": 1712318410000 },
    ...
  ],
  "asset": "BTC"
}
POST /api/v1/ctm/rounds/:id/predict AUTH
Place a Prediction Creates a price prediction for an open CTM round and deducts the entry fee from available balance.
Scope: bet:place Rate limit: 80 req/min
Request Body
predictedPrice number * — Your predicted settlement price (min 0)
idempotencyKey string — Optional dedup key to prevent double entries
Example
curl -X POST https://api.unhedged.gg/api/v1/ctm/rounds/<id>/predict \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "predictedPrice": 84250.50
}'
Response
{
  "prediction": {
    "id": "clx...",
    "roundId": "clx...",
    "phase": "BLIND",
    "predictedPrice": "84250.5000000000",
    "amount": "10.0000000000",
    "status": "CONFIRMED"
  },
  "balanceAfter": "490.0000000000",
  "fee": "10.0000000000",
  "phase": "BLIND"
}
GET /api/v1/ctm/my-predictions AUTH
List Your Predictions Returns your CTM predictions, optionally filtered by roundId.
Scope: bet:read Rate limit: 60 req/min
Query Parameters
roundId string — Filter by specific round
Example
curl https://api.unhedged.gg/api/v1/ctm/my-predictions \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "predictions": [
    {
      "id": "clx...",
      "roundId": "clx...",
      "phase": "BLIND",
      "predictedPrice": "84250.5000000000",
      "amount": "10.0000000000",
      "status": "CONFIRMED",
      "distance": "50.5000000000",
      "rank": 3,
      "payoutAmount": "25.0000000000",
      "submittedAt": "2026-04-05T12:01:00.000Z",
      "round": {
        "question": "Where will BTC be in 5 min?",
        "asset": "BTC",
        "status": "RESOLVED"
      }
    }
  ]
}
GET /api/v1/ctm/my-stats AUTH
Get Your CTM Stats Returns your CTM win rate, streaks, prediction count, and payout total.
Scope: bet:read Rate limit: 60 req/min
Example
curl https://api.unhedged.gg/api/v1/ctm/my-stats \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "stats": {
    "totalRounds": 85,
    "totalPredictions": 102,
    "totalWins": 28,
    "totalPayouts": "5600.0000000000",
    "bestRank": 1,
    "currentStreak": 3,
    "bestStreak": 7,
    "avgDistance": "125.3400000000"
  }
}

Order Book Markets

ORDER_BOOK markets use a central limit order book instead of pool betting. You quote prices for binary outcomes (e.g. YES / NO) on a continuous market between 0 and 1, where the price represents the implied probability.

Market discovery

Markets in /api/v1/feed carry a marketType field: ORDER_BOOK markets use the endpoints below. PARIMUTUEL are pool markets — use the /api/v1/bets endpoints.

Sides

bid = buy this outcome. ask = sell this outcome. Selling YES at p is economically equivalent to buying NO at 1 − p.

Prices

REST order decimals use strings with exactly 2 decimal places, e.g. "0.59". Contract counts use numbers, e.g. 100. Prices are strict (0, 1). Invalid decimals return code, field, and received.

Fees

ORDER_BOOK markets do not use platformFeeRate or per-market maker/taker fields. Set engineMarketType on create (default default; also sports_vip). The API returns feeSchedule (tiers from market_fee_type). Per fill, orders expose cum_fees and last_trade_fee. locked_fees is derived from the snapshotted maker_fee_rate, order price, and remaining quantity using the risk-shaped curve.

Settlement & balance lock

When you place an order, the maximum cost is moved from balance.available to balance.lockedEngine — visible immediately on GET /balance. On a fill, the matched portion is debited from lockedEngine and a position contract is created on the Canton ledger. On cancel, any unfilled portion returns to available. Trade matching is real-time off-chain; settlement is atomic on-chain in batches.

Client order IDs are not retry receipts

Optional clientOrderId on place (and clientOrderIds on cancel) tags open orders for your bots. Requests use camelCase; order objects expose client_order_id. The alias is unique among open orders for that balance in that market (shared across outcomes and API keys). After an order closes the same string may be reused. A timeout leaves an unknown outcome: do not place again with a new id, and do not assume reusing the same id is safe — if two contracts filled before the acknowledgement arrived, repeating the place can buy two more. Query GET /orders (follow every nextCursor) and trade history. Trade history has numeric order ids only; if the records do not resolve the outcome, stop rather than resubmit.

The API can replay a completed successful response for the same Idempotency-Key for up to 60 seconds on POST, PUT, and PATCH. It does not reserve a key while the first request is in flight, so simultaneous requests may both execute. DELETE cancellations are not covered by the cache. A fresh key is a new request, and an unresolved timeout remains ambiguous.

Market Gateway WebSocket

Order-book market data is served by the Rust market gateway, a separate WebSocket process from the REST API. Use REST /api/v1/orders for authenticated order placement and cancellation. Use the gateway for public ORDER_BOOK discovery, snapshots, live book updates, trade ticks, and execution quotes.

WebSocket URL
wss://market-gateway.unhedged.gg/ws
Protocol

JSON-RPC over standard WebSocket text frames. Requests include id, method, and optional params. Responses echo id with either result or error.

State Source

The gateway mirrors the matching engine's market-data stream in memory. It does not query PostgreSQL for live catalog or book state.

WebSocket Close

On gateway shutdown, clients receive a WebSocket close event with code 1012 and reason market gateway stopped; reconnect required. Reconnect, request a fresh snapshot, then resume sequenced deltas.

Connection Example
const ws = new WebSocket("wss://market-gateway.unhedged.gg/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({
    id: 1,
    method: "subscribe",
    params: {
      market_id: "clx...",
      best_bid_ask: true,
      snapshot_update: true,
      trade: true
    }
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.id) {
    // JSON-RPC response
    console.log(message.result ?? message.error);
    return;
  }

  // Unsolicited gateway event: best_bid_ask, snapshot_update, trade, etc.
  console.log(message);
};
Gateway Methods
list_markets List active matching-engine markets from the gateway catalog.
snapshot Return the gateway order-book mirror for one market.
subscribe Subscribe to best bid/ask, book diff, and trade streams for a market.
unsubscribe Remove one or more active subscriptions for a market.
query_trade Fetch historical public trade ticks for a market, capped at 500 trade IDs.
quote_execution Estimate immediate fill size and notional against the mirrored L2 book.
Gateway Errors
-32601 Method not found.
-32602 Invalid params.
-32004 Market or outcome does not exist in the active gateway catalog.
-32005 Gateway message rate limit exceeded.
Gateway Events
new_market

Lifecycle event when a market is added to the gateway catalog.

finish_market

Lifecycle event when a market leaves the gateway catalog.

best_bid_ask

Top-of-book update for one outcome.

snapshot_update

Incremental L2 book diff for one or more price levels.

trade

Unified public trade tick. Direct and cross-outcome fills use the same msg value.

Gateway trade_id is a JSON number on the wire. REST and private /ws order/trade IDs are strings.

Snapshot Request
{
  "id": 2,
  "method": "snapshot",
  "params": { "market_id": "clx..." }
}
Quote Request
{
  "id": 3,
  "method": "quote_execution",
  "params": {
    "market_id": "clx...",
    "outcome": "out_0",
    "side": "bid",
    "type": "Limit",
    "price": 0.50,
    "quantity": 10
  }
}
Quote Limitations

quote_execution is an immediate-fill estimate from the gateway mirror. It uses aggregated L2 depth, does not model your balance or self-trade prevention, and can lag while the gateway processes market-data messages. Place and cancel orders through the authenticated REST endpoints.

User Fill Stream

Bots use /ws for private order events. Authenticate with an API key that has order:read. Place, cancel, and list orders through REST. GET /api/v1/trades uses the same signed top-level fee / cashflow convention; nested order.cum_fees stays engine-positive.

WebSocket URL
wss://api.unhedged.gg/ws
Scope
order:read
Auth Message
{
  "type": "auth",
  "token": "Bearer ak_your_key_here"
}
Auth Responses
// success
{ "type": "authenticated", "userId": "clx...", "role": "USER" }

// missing order:read
{
  "type": "error",
  "error": "forbidden",
  "code": "MISSING_SCOPE",
  "scope": "order:read"
}
trade

Your executed order leg. Top-level fee is signed (negative when paid); cashflow equals payment plus fee. Same convention on GET /api/v1/trades. One match emits one event per participating leg (2 for a regular trade, N for a cross trade), all sharing a trade_id that is only unique within a market — key on market_id + trade_id + order_id.

share_update

Live share position snapshot for one market/outcome after ask lock/unlock, trade settlement, or auto-redeem. available, locked_me, and total (available + locked_me) are post-change contract counts; treat as authoritative for that leg.

burned

Your complete-set burn/redeem event. payment and cashflow are $1 per burned set.

conditional_order

Final order snapshot for a triggered stop-loss or take-profit step.

cancelled_order

Final order snapshot for a cancelled resting order.

Trade Event Shape
{
  "type": "trade",
  "room": "me:clx...",
  "timestamp": 1770000000000,
  "data": {
    "exec_id": "42",
    "trade_id": "184221",
    "qty": 25,
    "transact_ts": 1770000000000000,
    "market_id": "clx...",
    "trade_type": "direct",
    "role": "taker",
    "is_maker": false,
    "price": "0.62",
    "fee": "-0.25",
    "payment": "-15.5",
    "cashflow": "-15.75",
    "order": {
      "id": "123",
      "market_id": "clx...",
      "outcome": "out_0",
      "outcome_label": "YES",
      "side": "bid",
      "type": "Limit",
      "user": "clx_balance...",
      "price": "0.62",
      "quantity": 100,
      "cum_quantity": 25,
      "cum_quote_quantity": "15.5",
      "cum_fees": "0.25",
      "last_trade_fee": "0.25",
      "last_exec_price": "0.62",
      "status": "Placed"
    }
  }
}
Burn Event Shape
{
  "type": "burned",
  "room": "me:clx...",
  "timestamp": 1770000000000,
  "data": {
    "msg": "burned",
    "burn_id": "1",
    "market_id": "clx...",
    "balance_id": "clx_balance...",
    "quantity": 1,
    "cause_type": "trade",
    "reference_id": "clx...:184221",
    "payment": "1",
    "cashflow": "1",
    "transact_ts": 1770000000000000
  }
}
Share Update Event Shape
{
  "type": "share_update",
  "room": "me:clx...",
  "timestamp": 1770000000000,
  "data": {
    "msg": "share_update",
    "user": "clx_balance...",
    "market_id": "clx...",
    "outcome": "out_0",
    "outcome_label": "YES",
    "available": 12,
    "locked_me": 3,
    "total": 15,
    "cause_type": "trade",
    "reference_id": "clx...:184221",
    "transact_ts": 1770000000000000
  }
}
Bot Connection Example
const apiKey = process.env.UNHEDGED_API_KEY;
const marketId = "clx...";
const ws = new WebSocket("wss://api.unhedged.gg/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "auth",
    token: `Bearer ${apiKey}`
  }));
};

ws.onmessage = async (event) => {
  const message = JSON.parse(event.data);
  if (message.type === "authenticated") {
    await backfillFills(marketId);
    return;
  }
  if (message.type === "trade") {
    handleFill(message.data);
  }
  if (message.type === "share_update") {
    applyShareSnapshot(message.data);
  }
  if (message.type === "burned") {
    handleBurn(message.data);
  }
};
Reconnect Recovery

The stream has no replay cursor. After connect or reconnect, call GET /api/v1/trades?limit=500&startTime=<last fill time> (ISO 8601 or epoch milliseconds — divide transact_ts micros by 1000) so the backfill stops at your last processed fill instead of paging the full history, and dedupe fills by market_id, trade_id, and order_id. Apply live share_update snapshots to your positions map; on reconnect also refresh with GET /api/v1/portfolio/me/shares?marketId=clx... in case any snapshots were missed.

POST /api/v1/orders AUTH
Place Order Creates a Limit, Market, StopLoss, or TakeProfit order on an ORDER_BOOK market. On acceptance, cost moves from available balance to lockedEngine. Optional clientOrderId tags the open order for later cancel/replace; it is unique among open orders for this balance in this market and appears on the order as client_order_id.
Scope: order:place Rate limit: 120 req/min · 60 per market
Request Body
marketId string * — Market ID. Must be type ORDER_BOOK and status ACTIVE.
outcome string * — Outcome label or engine ID. Ambiguous label/engine ID collisions return 400.
side string * — "bid" (buy outcome) or "ask" (sell outcome).
type string * — Limit | Market | StopLoss | TakeProfit
price string — Limit price decimal string in (0,1) with exactly 2 decimals. Required for Limit orders, omitted for Market/Stop/TP.
triggerPrice string — Trigger price decimal string in (0,1) with exactly 2 decimals. Required for StopLoss/TakeProfit.
quantity number — Number of contracts as a positive integer. Mutually exclusive with quoteQuantity.
quoteQuantity string — Cost cap decimal string in USD (> 0) with exactly 2 decimals. Mutually exclusive with quantity.
clientOrderId string — Optional client id (1–64 chars, [A-Za-z0-9._:-], case preserved). Unique among open orders for this balance in this market (all outcomes/API keys). Reusable after the order closes — not a placement retry receipt.
Example
curl -X POST https://api.unhedged.gg/api/v1/orders \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "marketId": "clx...",
  "outcome": "YES",
  "side": "bid",
  "type": "Limit",
  "price": "0.62",
  "quantity": 100,
  "clientOrderId": "bot-42"
}'
Response
{
  "order": {
    "id": "213",
    "market_id": "clx...",
    "outcome": "YES",
    "side": "bid",
    "type": "Limit",
    "price": "0.62",
    "quantity": 100,
    "client_order_id": "bot-42",
    "exec_id": "42",
    "cum_quantity": 0,
    "cum_quote_quantity": "0",
    "cum_fees": "0",
    "last_exec_price": null,
    "status": "Placed"
  }
}
POST /api/v1/orders/batch AUTH
Batch Place Orders Places 1–32 orders on one ORDER_BOOK market in a single request (shared marketId; authenticated balance is the user). Legs may fill. Market type is rejected at validation (400) — use single Place Order. The engine transaction commits even if a leg fails; HTTP 400 with results when any leg failed, 201 when every leg succeeded. Counts as one write against the place/replace rate budget. Engine semantics: matching-engine MATCHING-ENGINE-WEBSOCKET-API.md (batch_new_order).
Scope: order:place Rate limit: 120 req/min · 60 per market
Request Body
marketId string * — Shared market ID. Must be type ORDER_BOOK and status ACTIVE.
orders array * — 1–32 place legs. Same fields as Place Order except marketId, but type is Limit | StopLoss | TakeProfit only (Market not allowed). Legs may fill against the book and against earlier legs in the batch.
Example
curl -X POST https://api.unhedged.gg/api/v1/orders/batch \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "marketId": "clx...",
  "orders": [
    { "outcome": "YES", "side": "bid", "type": "Limit", "price": "0.40", "quantity": 5 },
    { "outcome": "YES", "side": "bid", "type": "Limit", "price": "0.45", "quantity": 5 }
  ]
}'
Response
{
  "results": [
    { "order": { "id": "213", "status": "Placed", ... } },
    { "err": { "OrderNotFound": 999 } }
  ]
}
DELETE /api/v1/orders AUTH
Cancel Orders Cancels open orders in one market. Targets are the union of orderIds and clientOrderIds. Cancel-all (every open order for this balance on the market) runs only when both lists are empty. Numeric ids and clientOrderIds are best-effort (continue after soft failures such as unresolved client ids; { dropped, err } when some succeed).
Scope: order:cancel Rate limit: 120 req/min · 60 per market
Request Body
marketId string * — Market ID
orderIds string[] * — Numeric order IDs (as strings). Pass [] when cancelling only by clientOrderIds, or with empty/omitted clientOrderIds for cancel-all.
clientOrderIds string[] — Optional client order IDs (same charset/length as clientOrderId). Union with orderIds. Both empty = cancel-all.
Example
curl -X DELETE https://api.unhedged.gg/api/v1/orders \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "marketId": "clx...",
  "orderIds": ["213"],
  "clientOrderIds": ["bot-42"]
}'
Response
{
  "result": {
    "dropped": ["213", "214"]
  }
}

// client-only cancel:
// { "marketId": "clx...", "orderIds": [], "clientOrderIds": ["bot-42"] }

// cancel-all:
// { "marketId": "clx...", "orderIds": [] }
POST /api/v1/orders/replace AUTH
Replace Order Cancels one open Limit order and places one replacement Limit order in the same engine transaction. Identify the source with exactly one of orderId or clientOrderId. When replacementClientOrderId is omitted, the replacement inherits the source client_order_id. The requested replacement alias is checked for conflicts first. If price and quantity are unchanged and the alias passes that check, the response is "unchanged" and the numeric id, priority, and current client_order_id stay the same. Requires scopes order:place and order:cancel.
Scope: order:place, order:cancel Rate limit: 120 req/min · 60 per market
Request Body
marketId string * — Market ID (must be ORDER_BOOK and active).
orderId string — Numeric string ID of the open Limit order to replace. Exactly one of orderId / clientOrderId.
clientOrderId string — Client id of the open Limit order to replace. Exactly one of orderId / clientOrderId.
replacementClientOrderId string — Optional client id for the replacement (same charset/length). Omit to inherit the source's client_order_id. On an actual amend, must not collide with another open order.
price string * — New limit price decimal string in (0,1) with exactly 2 decimals.
quantity number * — New size in contracts as a positive integer. quoteQuantity is not supported here.
Example
curl -X POST https://api.unhedged.gg/api/v1/orders/replace \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "marketId": "clx...",
  "clientOrderId": "bot-42",
  "price": "0.64",
  "quantity": 50
}'
Response
{
  "result": {
    "cancelledOrderId": "213",
    "replacementOrderId": "214",
    "cancelledOrder": { "id": "213", "status": "Cancelled", ... },
    "replacementOrder": {
      "id": "214",
      "status": "Placed",
      "price": "0.64",
      "quantity": 50,
      "client_order_id": "bot-42",
      ...
    }
  }
}

// unchanged no-op (same price/size; client-id-only change does not rename):
{
  "result": {
    "status": "unchanged",
    "orderId": "213"
  }
}
POST /api/v1/orders/replace/batch AUTH
Batch Replace Orders Replaces 1–32 open Limit orders on one market (same scopes as single replace). Replacements may fill. Self-trade follows the market setting. The engine transaction commits even if a leg fails; HTTP 400 with results when any leg failed. Counts as one write against the place/replace rate budget. Engine semantics: matching-engine MATCHING-ENGINE-WEBSOCKET-API.md (batch_replace_order).
Scope: order:place, order:cancel Rate limit: 120 req/min · 60 per market
Request Body
marketId string * — Shared market ID (ORDER_BOOK, active).
replaces array * — 1–32 replace legs. Each leg: exactly one of orderId / clientOrderId, plus price, quantity, optional replacementClientOrderId. Crossing replacements cancel the source only (no fills).
Example
curl -X POST https://api.unhedged.gg/api/v1/orders/replace/batch \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "marketId": "clx...",
  "replaces": [
    { "orderId": "1", "price": "0.52", "quantity": 10 },
    { "orderId": "2", "price": "0.57", "quantity": 10 },
    { "orderId": "3", "price": "0.62", "quantity": 10 }
  ]
}'
Response
{
  "results": [
    {
      "cancelledOrderId": "1",
      "replacementOrderId": "4",
      "replacementOrder": { "id": "4", "status": "Placed", "price": "0.52", ... }
    },
    {
      "cancelledOrderId": "2",
      "replacementOrderId": "5",
      "replacementOrder": { "id": "5", "status": { "InternalCancel": ... }, ... }
    },
    {
      "cancelledOrderId": "3",
      "replacementOrderId": "6",
      "replacementOrder": { "id": "6", "status": "Placed", "price": "0.62", ... }
    }
  ]
}
GET /api/v1/orders AUTH
List Open Orders Returns open orders (including pending StopLoss/TakeProfit) from newest to oldest. Omit marketId to cover the account; follow every nextCursor (default page size 100, max 500) until nextCursor is null. Keep marketId, outcome, and limit fixed while paging. Each row may include client_order_id.
Scope: order:read Rate limit: 120 req/min
Query Parameters
marketId string — Optional market filter.
outcome string — Optional label or engine ID filter. Requires marketId. Ambiguous label/engine ID collisions return 400.
limit number — Page size, 1-500. Default 100.
cursor string — Previous page nextCursor. Omit on page 1.
Example
curl https://api.unhedged.gg/api/v1/orders \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "orders": [
    {
      "id": "213",
      "market_id": "clx...",
      "outcome": "YES",
      "side": "bid",
      "type": "Limit",
      "price": "0.62",
      "quantity": 100,
      "client_order_id": "bot-42",
      "exec_id": "42",
      "cum_quantity": 25,
      "cum_fees": "0.5000000000",
      "status": "Placed",
      "created_at": "2026-04-12T15:33:21.000Z"
    }
  ],
  "nextCursor": "eyJjcmVhdGVkQXQiOiIxNzc2MDA4MDAxMDAwIiwibWFya2V0SWQiOiJjbHguLi4iLCJvcmRlcklkIjoiMjEzIn0"
}

// next request:
// GET /api/v1/orders?marketId=clx...&limit=100&cursor=eyJjcmVhdGVkQXQiOiIxNzc2MDA4MDAxMDAwIiwibWFya2V0SWQiOiJjbHguLi4iLCJvcmRlcklkIjoiMjEzIn0

// final page:
{
  "orders": [],
  "nextCursor": null
}
GET /api/v1/orders/aliases AUTH
Recover Client Order IDs Lists retained mappings for accepted orders tagged with a client ID, whether open or closed, on your account. Results are available when the matching engine is disabled. Omit marketId to page across all markets; follow nextCursor until it is null. Client IDs can be reused after closure, so multiple closed orders may share one. Match a mapping to trade history by market_id and order_id. Results report retention_days (default 30). This is identity history, not an order-status lookup or replay receipt. Absence does not prove that a request failed or was never executed.
Scope: order:read Rate limit: 240 req/min default; configurable
Query Parameters
marketId string — Optional market filter. Omit to enumerate all markets for your account.
limit number — Page size, 1-500. Default 100.
cursor string — Previous page nextCursor. Omit on page 1.
Example
curl https://api.unhedged.gg/api/v1/orders/aliases \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "aliases": [
    {
      "market_id": "clx...",
      "order_id": "213",
      "client_order_id": "quote-17",
      "accepted_at": "2026-09-01T12:00:00.123Z"
    }
  ],
  "nextCursor": "eyJhY2NlcHRlZEF0IjoiMTc4ODI2NDAwMDEyMyIsIm1hcmtldElkIjoiY2x4Li4uIiwib3JkZXJJZCI6IjIxMyJ9",
  "retention_days": 30,
  "coverage": "Mappings are retained for retention_days after acceptance. Absence does not imply that an unobserved request failed or was never executed; the mapping may have expired, predate this feature, or never have been accepted with a client_order_id."
}
GET /api/v1/trades AUTH
Trade History Returns fills from newest to oldest. Omit cursors on page 1. Pass nextCursor as cursor and burnsNextCursor as burnsCursor. Stop each stream when its cursor is null. Use startTime/endTime to bound a time window. One row is one order leg, not one match: a regular trade writes 2 legs (aggressive + passive) and a cross trade writes N (1 aggressive + N-1 passives, N = outcomes in the market), all sharing one trade_id, and trade_id restarts at 1 in every market — dedup on fill_id (burns: burn_uid), never on trade_id or burn_id, and count matches as distinct market_id + trade_id. Rows expose numeric order IDs only — no client_order_id mapping; absence from open orders does not prove a timed-out placement never executed.
Scope: order:read Rate limit: 120 req/min
Query Parameters
marketId string — Optional market filter.
limit number — Page size, 1-500. Default 100.
cursor string — Previous trades nextCursor. Omit on page 1.
burnsCursor string — Previous burnsNextCursor. Omit on page 1.
startTime string — Optional inclusive lower bound on execution time (transact_ts). ISO 8601 date-time, or Unix epoch milliseconds if all digits. Applies to trades and burns.
endTime string — Optional inclusive upper bound on execution time (transact_ts). Same formats as startTime. Must not be before startTime.
Example
curl https://api.unhedged.gg/api/v1/trades \
  -H "Authorization: Bearer ak_your_key_here"
Response
{
  "trades": [
    {
      "fill_id": "clx...:184221:213",
      "market_id": "clx...",
      "trade_id": "184221",
      "order_id": "213",
      "exec_id": "42",
      "outcome": "YES",
      "side": "bid",
      "trade_type": "direct",
      "role": "taker",
      "is_maker": false,
      "price": "0.62",
      "qty": 25,
      "quote_quantity": "15.5",
      "fee": "-0.25",
      "payment": "-15.5",
      "cashflow": "-15.75",
      "transact_ts": "1770000000000000000",
      "created_at": "2026-04-12T15:33:21.000Z"
    }
  ],
  "burns": [
    {
      "msg": "burned",
      "burn_uid": "clx...:1",
      "burn_id": "1",
      "market_id": "clx...",
      "balance_id": "clx_balance...",
      "quantity": 1,
      "cause_type": "trade",
      "reference_id": "clx...:184221",
      "payment": "1",
      "cashflow": "1",
      "transact_ts": "1770000000000000000",
      "created_at": "2026-04-12T15:33:22.000Z"
    }
  ],
  "nextCursor": "eyJ0cmFuc2FjdFRpbWUiOiIxNzcwMDAwMDAwMDAwMDAwMDAwIiwibWFya2V0SWQiOiJjbHguLi4iLCJ0cmFkZUlkIjoiMTg0MjIxIiwib3JkZXJJZCI6IjIxMyJ9",
  "burnsNextCursor": "eyJ0cmFuc2FjdFRpbWUiOiIxNzcwMDAwMDAwMDAwMDAwMDAwIiwibWFya2V0SWQiOiJjbHguLi4iLCJidXJuSWQiOiIxIn0"
}

// next request, continuing both streams:
// GET /api/v1/trades?marketId=clx...&limit=100&cursor=eyJ0cmFuc2FjdFRpbWUiOiIxNzcwMDAwMDAwMDAwMDAwMDAwIiwibWFya2V0SWQiOiJjbHguLi4iLCJ0cmFkZUlkIjoiMTg0MjIxIiwib3JkZXJJZCI6IjIxMyJ9&burnsCursor=eyJ0cmFuc2FjdFRpbWUiOiIxNzcwMDAwMDAwMDAwMDAwMDAwIiwibWFya2V0SWQiOiJjbHguLi4iLCJidXJuSWQiOiIxIn0

// final page:
{
  "trades": [],
  "burns": [],
  "nextCursor": null,
  "burnsNextCursor": null
}

Types & statuses

Values returned on order objects and accepted by POST /api/v1/orders. Engine order WebSocket RPC names are listed for debugging direct ME connections — production bots should use REST.

Order types
Limit Rests on the book at price. Requires price as a decimal string in (0,1) with exactly 2 decimals.
Market Consumes liquidity immediately. No price; bounded only by available balance / book depth.
StopLoss Conditional. Triggers a market-style execution when last trade price falls to triggerPrice.
TakeProfit Conditional. Triggers a market-style execution when last trade price rises to triggerPrice.
Order statuses
New Accepted by API; not yet on the book.
Placed Resting on the book (Limit) or armed (StopLoss/TakeProfit).
Filled Fully executed. cum_quantity == requested quantity.
Cancelled Cancelled by user (or auto-cancelled at market close).
InternalCancel Engine could not place the resting remainder (e.g. insufficient balance after partial fill). Includes a reason string.
Cursor pagination
Page 1 Omit cursor. Use limit to choose 1-500 rows; the default is 100.
Next page Pass nextCursor as cursor. Stop when nextCursor is null.
Stable filters Keep marketId, outcome, limit, startTime, and endTime fixed within one cursor chain.
Trade burns Trades and burns page independently. Pass nextCursor as cursor and burnsNextCursor as burnsCursor.
Row identity Dedup on fill_id (trades) and burn_uid (burns). trade_id and burn_id restart at 1 in every market, and one match writes one row per leg — 2 for a regular trade, N for a cross trade (N = outcomes) — so those keys repeat by design.
Engine order WebSocket RPC
new_order Engine order RPC behind POST /api/v1/orders. Wire params use snake_case fields such as market_id, trigger_price, quote_quantity, and optional client_order_id.
batch_new_order Behind POST /api/v1/orders/batch. Shared market_id/user plus 1–32 place legs (Limit/StopLoss/TakeProfit only — Market rejected; same other per-leg fields as new_order without market_id/user). Legs may fill; one outer PgTxn commits even if a leg fails. HTTP 400 when any leg failed. One write rate unit per HTTP request.
cancel_order Engine order RPC behind DELETE /api/v1/orders. Cancels by numeric order_ids and/or client_order_ids (union). Both empty = cancel-all for that user in the market.
replace_order Behind POST /api/v1/orders/replace. Atomically cancel one open Limit and submit a new Limit (market_id, exactly one of order_id / client_order_id, price, quantity; optional replacement_client_order_id). Outcome, side, and type are copied from the source order.
batch_replace_order Behind POST /api/v1/orders/replace/batch. Shared market_id/user plus 1–32 replace legs. One outer PgTxn commits even if a leg fails. HTTP 400 when any leg failed. One write rate unit per HTTP request.
query_orders Engine order RPC used to list a user's open orders for one market.