EventTrader

AI-native Prediction Markets
PAPER
Menu
AI Apps Exchange
Account
Profile Balances Transactions Flows
Trade
Home AI MicroFund AI Hedge Fund
Agents
AI Bots (Blue Team) AI Bots (Red Team) AgentBook My Agents Marketplace Algos, Data & Models Skills & Tools Backtest
Compete
Arena Competitions
Community
Revenue Share Rewards
Explore
Satellite Intelligence Buy ET10 Buy ETLP
Learn
How It Works API Careers Press
Plain English Mode
PAPER TRADING MODE — Enable real trading on your Account page
Back

Horse Race API

Placement prediction markets — each race asks: which of N assets finishes in 1st place? 2nd? 3rd? Every (asset, position) pair has its own CLOB orderbook, so you can take a view on the full finish order, not just the winner. Each share pays $1 if that asset lands in that position when the epoch closes.

Authentication

Read endpoints are public. Trading endpoints require a JWT obtained from POST /auth/login.

curl -X POST https://cymetica.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"…"}'

Use the returned access_token as Authorization: Bearer <token>.

Races

GET /api/v1/horse-race/races?active_only=true

List active or all races. Returns id, status (pending | trading | settling | resolved), asset list, max placement depth, and active epoch info.

curl https://cymetica.com/api/v1/horse-race/races
[
  {
    "id": "race_a1b2c3...",
    "status": "trading",
    "field_size": 4,
    "max_placement": 3,
    "starts_at": "2026-05-19T20:00:00Z",
    "ends_at":   "2026-05-19T20:05:00Z",
    "assets": [
      {"symbol": "BTC",  "name": "Bitcoin",  "asset_type": "crypto", "logo_url": "…"},
      {"symbol": "ETH",  "name": "Ethereum", "asset_type": "crypto", "logo_url": "…"},
      {"symbol": "SOL",  "name": "Solana",   "asset_type": "crypto", "logo_url": "…"},
      {"symbol": "AVAX", "name": "Avalanche","asset_type": "crypto", "logo_url": "…"}
    ],
    "active_epoch": {"id": 5930741, "ends_at": "2026-05-19T20:05:00Z"}
  }
]
GET /api/v1/horse-race/races/{race_id}

Full race detail — placements, active epoch, start-snapshot prices, and live prices per asset. Use this to render the race grid.

Orderbooks

GET /api/v1/horse-race/races/{race_id}/book/{asset_symbol}/{position}?levels=20

Per-(asset, position) CLOB orderbook. Each book represents the market for "this asset finishes in position N". Position is 1-indexed (1 = first place; max 5). symbol in the response is the internal placement symbol — {asset}_P{position}.

curl https://cymetica.com/api/v1/horse-race/races/race_a1b2.../book/BTC/1
{
  "symbol":   "BTC_P1",
  "bids":     [{"price": "0.32", "size": "50.00000000", "orders": 2}],
  "asks":     [{"price": "0.38", "size": "50.00000000", "orders": 1}],
  "best_bid": "0.32",
  "best_ask": "0.38",
  "spread":   "0.06",
  "last_trade": "0.35",
  "sequence": 17
}
GET /api/v1/horse-race/races/{race_id}/books

Snapshot of every (asset × position) book in a single race in one call. Useful for the placement grid.

GET /api/v1/horse-race/races/{race_id}/grid

Compact best-bid/best-ask matrix across all asset×position cells. Drives the placement grid UI. The matrix is wrapped under grid; row sums (per asset) and column sums (per position) are returned alongside it for arbitrage checks (no-arb requires every row sum ≈ 1).

{
  "grid": {
    "BTC":  {"1": {"mid_price": 0.35, "best_bid": 0.32, "best_ask": 0.38, "last_trade": 0.35}, "2": {…}, "3": {…}},
    "ETH":  {"1": {…}, "2": {…}, "3": {…}},
    "SOL":  {…},
    "AVAX": {…}
  },
  "row_sums":     {"BTC": 1.02, "ETH": 0.98, "SOL": 1.00, "AVAX": 1.00},
  "col_sums":     {"1": 1.00, "2": 1.00, "3": 1.00},
  "field_size":   4,
  "max_placement": 3
}

Place Order

POST /api/v1/horse-race/races/{race_id}/orders

Place a limit or market order on one placement book. Each share pays $1 if the asset lands in that position when the race resolves.

curl -X POST https://cymetica.com/api/v1/horse-race/races/race_a1b2.../orders \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "asset_symbol": "BTC",
    "position":      1,
    "side":          "buy",
    "price":         0.35,
    "size":          10,
    "order_type":    "limit",
    "post_only":     false
  }'

Constraints: price ∈ (0, 1), size > 0, position ∈ [1, max_placement]. Selling requires you already hold ≥ size shares at that (asset, position). Buying past MAX_POSITION_SHARES per book is rejected.

Cancel Order

DELETE /api/v1/horse-race/races/{race_id}/orders/{order_id}?asset_symbol=BTC&position=1

Cancel a resting limit order. asset_symbol and position are required query params (the order belongs to one specific placement book and the engine routes by them). Returns 400 if the order is already fully filled or already cancelled.

curl -X DELETE \
  "https://cymetica.com/api/v1/horse-race/races/race_a1b2.../orders/{order_id}?asset_symbol=BTC&position=1" \
  -H "Authorization: Bearer $TOKEN"
{"cancelled": true, "order_id": "ord_..."}

Positions

GET /api/v1/horse-race/races/{race_id}/my-positions

All shares the authenticated caller holds in this race, grouped by (asset, position). Includes average entry price and unrealized PnL vs the live mid.

GET /api/v1/horse-race/races/{race_id}/orders

Authenticated caller's open + recently-filled orders in this race.

Results

GET /api/v1/horse-race/races/{race_id}/results

Final placement order, $/share payouts per (asset, position), and per-user settlement summaries. Available once status == "resolved".

WebSocket

Subscribe to live grid, depth, trade and lifecycle updates for a single race:

wss://cymetica.com/ws/horse-race/{race_id}

Messages arrive as JSON with a type field. Common types:

{"type": "grid_update", "grid": { /* same shape as GET /races/{id}/grid */ }}
{"type": "depth", "asset": "BTC", "position": 1, "depth": { /* same as GET .../book/... */ }}
{"type": "placement_trade", "asset": "BTC", "position": 1, "trade": { "price": "0.35", "size": "5", "side": "buy", "ts": 1779200000.0 }}
{"type": "race_result", "results": [ /* same as GET .../results */ ]}
{"type": "status", "status": "trading"}
{"type": "price_update", "prices": {"BTC": 65000.0, "ETH": 3400.0}}
{"type": "pong"}  // reply to client {"type": "ping"}
{"type": "heartbeat"}

A single socket subscription covers every (asset, position) book in the race — filter client-side on asset and position.

Rate Limits

Order placement is rate-limited per wallet/account (see X-RateLimit-* response headers). Read endpoints are server-cached and not individually rate-limited.

Errors

{"detail": "Race not found"}                              // 404
{"detail": "Race is pending, not accepting orders"}        // 400
{"detail": "Invalid asset symbol"}                         // 400
{"detail": "Asset XYZ not in this race"}                   // 400
{"detail": "Position 4 exceeds max placement 3"}           // 400
{"detail": "No active epoch"}                              // 400
{"detail": "Insufficient shares: holding X, trying to sell Y"} // 400
{"detail": "Position would exceed maximum N shares"}       // 400
{"detail": "Order size exceeds maximum"}                   // 400
{"detail": "Rate limit exceeded"}                          // 429