EventTrader
AI-Native Trading
PAPER
Menu
Dark Mode
Plain English Mode
PAPER TRADING MODE — Enable real trading on your Account page
Back

Prediction Pools API

Closest-guess price contest. Each pool runs on a fixed cadence — btc-5m settles every 5 minutes, btc-4h every 4 hours, and event-5m (the tradable-event pool on the landing page; the slug is historical — it settles every 15 minutes). BTC pools predict BTC/USD; event-5m predicts the live event index — the price-weighted basket of the assets moved by the currently-featured news headline (the /state response's subject object carries the headline card and its pw_index). The prediction closest to the settlement price (P1) takes the whole pool. Stakes run $0.10 to $1,000,000 per round. One prediction per user per round: re-entering tops up your stake at the price you already predicted, and the price itself can never be changed — a top-up carrying a different predicted_price is rejected with 400 "you already predicted $X this round — top-ups must use the same price". (Allowing a change would let a player chase the live index with their lock-in already placed.) Cancel and re-enter if you want a different price while entries are open. Every endpoint below works identically for all three pools — just pass pool=event-5m. pool defaults to btc-4h when omitted and is echoed back in every response, so always read it off the response rather than assuming.

Round Lifecycle

Every round moves through: open (predictions accepted, and cancellable for a full refund — this is the status the entry-accepting round carries) → locked (entries frozen at P0, measuring window running) → settled (P1 taken; the closest prediction wins the pool) or voided (index unavailable past the grace window — fail-closed, all stakes refunded in full). The /state endpoint returns all three views at once: the open entry_round (status: "open"), the measuring_round (status: "locked"), and the last_round. Filter the live active round on status == "open", not "entry".

Authentication

Read endpoints are public. POST /api/v1/btc-pool/enter, POST /api/v1/btc-pool/cancel and GET /api/v1/btc-pool/me require a JWT — all three return 401 without one.

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

# Then on every authenticated call:
#   Authorization: Bearer <access_token>

Endpoints

GET /api/v1/btc-pool/state?pool=btc-5m

Public state of one pool: the open entry round (with the anonymized prediction tape), the locked measuring round, the last terminal round, and the live BTC/USD index. pool defaults to btc-4h.

curl "https://cymetica.com/api/v1/btc-pool/state?pool=btc-5m"
{
  "pool": "btc-5m",
  "enabled": true,
  "now": "2026-07-21T04:20:00+00:00",
  "entry_round": {
    "pool": "btc-5m",
    "round_epoch": 5948760,
    "status": "open",
    "entry_opens_at": "2026-07-21T04:20:00+00:00",
    "locks_at": "2026-07-21T04:25:00+00:00",
    "entries_close_at": "2026-07-21T04:24:40+00:00",
    "entry_cutoff_seconds": 20,
    "cadence_seconds": 300,
    "settles_at": "2026-07-21T04:30:00+00:00",
    "p0": null, "p1": null,
    "winning_price": null, "winning_distance": null,
    "winners_count": null, "void_reason": null,
    "entries": 2,
    "pool_usdc": "0.20",
    "min_entry_usdc": "0.10",
    "max_entry_usdc": "1000000",
    "fee_pct": "0",
    "subject_ref": null,
    "predictions": [
      {"id": 4812, "price": "65476.59", "amount_usdc": "0.10", "side": "up"},
      {"id": 4813, "price": "65493.59", "amount_usdc": "0.10", "side": "up"}
    ]
  },
  "measuring_round": { "...": "same shape, status locked, p0 set" },
  "last_round":      { "...": "same shape, status settled or voided" },
  "index": {"price": "65481.22", "at": "2026-07-21T04:19:58+00:00"},

  // event-5m ONLY — the headline this round settles against. Every round
  // also carries its own "subject_ref" (the pinned card_id), so settlement
  // stays reproducible even after the featured headline rotates.
  "subject": {
    "card_id": "03cb7480-fb59-4023-9be9-da9ddb6b70b0",
    "title":   "Bispecific antibodies sweep into the clinic",
    "pw_index": "100.0897"
  }
}

Field notes. cadence_seconds is the round length — read it instead of inferring from the slug: event-5m is frozen for compatibility but runs a 900-second cadence, not 300. entries counts unique participants (one per user per round) — topping up does not increment it, and cancelling decrements it. entries_close_at is earlier than locks_at by entry_cutoff_seconds (anti-sniping); count down to that, not to the lock, or you will offer a form the server rejects. On BTC pools subject is absent and subject_ref is null.

POST /api/v1/btc-pool/enter

Enter (or top up) a prediction in the currently-open round. Requires auth. Funds are debited from your USDC balance and escrowed until settlement. predicted_price must be > 0 and ≤ 100,000,000; amount_usdc must be between 0.10 and 1,000,000 (cumulative per user per round). A top-up must repeat the same predicted_price; a different one is rejected with 400 and your existing entry is left untouched. Topping up does not change entries, which counts unique participants.

curl -X POST https://cymetica.com/api/v1/btc-pool/enter \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"predicted_price": "65450.00", "amount_usdc": "1.00", "pool": "btc-5m"}'
{
  "pool": "btc-5m",
  "round_epoch": 5948760,
  "predicted_price": "65450.00",
  "amount_usdc": "1.00",
  "pool_usdc": "1.20",
  "entries": 3
}
POST /api/v1/btc-pool/cancel

Cancel your entry in the currently-open round — full refund of the escrowed stake, allowed strictly until lock. Requires auth. After cancelling you may re-enter the same round (at any price) while it is still open.

curl -X POST https://cymetica.com/api/v1/btc-pool/cancel \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"pool": "btc-5m"}'
{
  "pool": "btc-5m",
  "round_epoch": 5948760,
  "refunded_usdc": "1.00",
  "pool_usdc": "0.20",
  "entries": 2
}
GET /api/v1/btc-pool/me?pool=btc-5m

Your ticket in the open round, your all-time net profit in this pool (profit_usdc — terminal rounds only, so it accumulates as rounds settle), plus your last 20 settled/voided outcomes. Requires auth.

{
  "enabled": true,
  "pool": "btc-5m",
  "profit_usdc": "0.10",
  "entry": {"predicted_price": "65450.00", "amount_usdc": "1.00"},
  "history": [
    {
      "round_epoch": 5948683,
      "status": "settled",
      "predicted_price": "65493.59",
      "amount_usdc": "0.10",
      "payout_usdc": "0.20",
      "refund_usdc": null,
      "p1": "65493.51",
      "winning_price": "65493.59"
    }
  ]
}
GET /api/v1/btc-pool/history?pool=btc-5m&limit=24

Recent terminal rounds (settled or voided), newest first. Public. limit 1–200, default 24.

{
  "pool": "btc-5m",
  "rounds": [
    {
      "pool": "btc-5m",
      "round_epoch": 5948683,
      "status": "settled",
      "p0": "65488.10",
      "p1": "65493.51",
      "winning_price": "65493.59",
      "winning_distance": "0.08",
      "winners_count": 1,
      "entries": 2,
      "pool_usdc": "0.20",
      "fee_pct": "0"
    }
  ]
}

WebSocket

One socket streams every pool — no polling, no auth, public aggregates only. Each message is {"type": …, "data": …, "ts": …}; state-scoped messages carry data.pool, so filter on it client-side.

wss://cymetica.com/ws/btc-pool
TypeWhenPayload (data)
stateOn connect (one per pool) and every ~10sFull public state — same shape as GET /state
index_tick~1s, when the BTC/USD index changes{"price", "at", "index", "pools"}always the shared BTC median: index is "btc-usd" and pools lists the slugs it settles (["btc-4h","btc-5m"]). Event pools never tick here; their index arrives inside state, which carries its own pool and index. Do not apply this tick to an event-pool chart.
pool_updateSomeone entered or cancelled in the open round{"pool", "round_epoch", "entries", "pool_usdc"} plus the book delta: "rung" {"id", "price", "amount_usdc", "side"} on an entry (a repeat entry at the same price returns that rung's new cumulative amount_usdc), or "rung_removed" (the rung id) on a cancel. Apply the delta to the one matching rung — no need to re-fetch /state.
round_updateA round locked / settled / voidedRound dict (same shape as rounds in /history)
wins_recentOn connect{"wins": […]} — recent anonymized wins (country-level, no PII)
win_eventA round settles with a winnerOne anonymized win — powers the global win map

Keepalive: send the literal text ping → server replies pong. Idle sockets receive a JSON {"type": "ping"} every 60s.

Rate Limits

Standard platform rate limits apply per IP/account — see the X-RateLimit-* headers on REST responses. Entry is additionally bounded by the per-round cumulative stake cap ($1,000,000 per user per round).

Errors

Errors return {"detail": "…"} with a standard status code:

CodeMeaning
400Unknown pool, entries locked, stake outside $0.10–$1,000,000, insufficient balance, no entry to cancel
401Missing/invalid JWT on /enter, /cancel, or /me
503Pool not live ("This pool is not live yet")