Reference

API reference

Every endpoint, in one place, checked against what the build actually serves.

Two surfaces

Partner paths 18Sandbox-gated 5Public /api/*Version v1
SurfaceAuthWhat it is for
/api/v1/*Operator key + scopeThe partner contract. Opaque contract ids, exact money, keyset cursors, no venue named anywhere.
/api/*NoneThe public catalogue and the public demo. It powers a page anyone can open; it is not a contract.

They are different routes with different rules rather than one route with a flag. Adding auth to the public surface would break the demo; leaving it off the partner surface would make an operator's orders placeable by anyone who learns a player id.

http
Authorization: Bearer pk_test_...      # or sk_live_... in production
X-Predicta-User: your-own-player-id    # on every quote and order
Content-Type: application/json

A machine-readable OpenAPI 3.1 document covering all eighteen paths is at /openapi.json.

Catalogue

GET/api/v1/eventsAPI key
The partner catalogue, keyset-paged. Scope markets:read. category, sort, dir, search, tradable, closesAfter, closesBefore, limit (1–100, default 25), cursor. An unrecognised filter value is a 400, not a silently unfiltered catalogue.
GET/api/v1/events/{id}API key
One event with every outcome. {id} is an event id or its slug. Scope markets:read.
GET/api/v1/categoriesAPI key
The values ?category= accepts, with live event counts. Scope markets:read.
GET/api/v1/images/{id}No auth
Event artwork from Predicta's own origin. {id} is the event id. The only unauthenticated route on this surface, because it is a URL a browser loads.

Trading

POST/api/v1/quotesAPI key
Price a trade and hold that price. Scope quotes:write. Exactly one of stake (sizes a BUY) or contracts (sizes a SELL). Requires X-Predicta-User.
POST/api/v1/ordersAPI key
Spend a quote. Scope orders:write. Idempotent on clientOrderId: 201 on creation, 200 with idempotentReplay: true on a replay. Requires X-Predicta-User.
GET/api/v1/ordersAPI key
Order history, newest first. Scope orders:read. externalUserId, clientOrderId (with externalUserId), status, since, until, limit (1–200, default 50), cursor.
GET/api/v1/orders/{id}API key
One order, with settlementState. Scope orders:read. ?fills=true includes the individual executions.

Positions

GET/api/v1/positionsAPI key
What your players hold. Scope positions:read. externalUserId, status, limit (1–200, default 50), cursor.
GET/api/v1/positions/{id}API key
One position by positionId. Scope positions:read.

Settlement and reconciliation

GET/api/v1/settlementsAPI key
The settlement register, keyset-paged on a monotonic sequence. Scope settlements:read. externalUserId, outcome, settledFrom, settledTo, limit (1–500, default 100), cursor.
GET/api/v1/settlements/positionsAPI key
The older position-settlement feed, cursored on settledAt. Superseded by /api/v1/settlements, which its own response says in supersededBy: this one pages with no tiebreaker, and settlements written in one transaction share a timestamp exactly.
GET/api/v1/reconciliationAPI key
The verdict: do Predicta's records, the cash ledger and the venue agree? Scope ledger:read. Optional from.
GET/api/v1/reconciliation/feedAPI key
Every money fact recorded for you, totally ordered and resumable. Scope ledger:read. cursor, type (repeatable), externalUserId, limit (1–500, default 100).

Sandbox only

These five answer 403 sandbox_only in live mode

They model a per-user balance held by Predicta, which exists so an integration can be learned against simulated money. A live operator owns its customers' cash ledger, so there is nothing here for it to read — the live contract is operatorMoney, operatorExitMoney and settlementCredit.

GET/api/v1/users/{id}/balanceAPI key
A player's derived balance. Scope ledger:read. Optional currency.
GET/api/v1/users/{id}/ledgerAPI key
The movements behind that balance, newest first. Scope ledger:read. limit (1–200, default 50), cursor.
GET/api/v1/funding/supported-assetsAPI key
Fundable asset and chain pairs, read live on every call. Scope funding:write. symbol, symbols, chainId.
POST/api/v1/funding/deposit-addressAPI key
Issue a deposit address for one player. Scope funding:write. 201 on success.
GET/api/v1/funding/deposits/{id}API key
Deposit status, timeline and history. Scope funding:write. ?sync=false skips the bridge poll.

Public — no credentials

These power the public site and the public demo. They are useful to read and they are not the partner contract: they name contracts by internal ids, take no key, and carry no operator scoping.

GET/api/eventsNo auth
The public event feed. Offset-paged.
GET/api/events/{key}No auth
One event with price history. range is 1d, 1w, 1m or all.
GET/api/marketsNo auth
The public contract catalogue.
GET/api/markets/{id}No auth
One contract, its history and related contracts.
GET/api/markets/quotes?ids=…No auth
Up to 100 contracts by id in one request.
GET/api/categoriesNo auth
Categories that currently have contracts, with counts.
GET/api/settlementsNo auth
The market-level resolution feed. Outcomes, not payouts.
GET/api/healthNo auth
Database, ingest and price-coverage status. 503 only when the database is down.
POST/api/demo/quotesNo auth
The demo's quote path. Same pricing code, no credentials.
POST/api/demo/ordersNo auth
The demo's order path. Same execution code, under a well-known demo operator.

Do not integrate against /api/demo/* or /api/*

The demo exists so the public page exercises the production path rather than a parallel implementation of it. It is unauthenticated, its identity model is not yours, and it is not a contract. The public catalogue is likewise a website feed: partners read /api/v1/events.

Conventions that hold everywhere

  • Auth. Authorization: Bearer <key> on every /api/v1/* request except /api/v1/images/{id}. Scope is checked before the rate limit is consumed, so a scope mistake never arrives disguised as a 429 on the retry.
  • Identity. X-Predicta-User carries your id for the player. Predicta never authenticates end users and stores only that opaque value. A write path creates a user on first use; a read never does.
  • Idempotency. clientOrderId on orders, enforced by a unique index on (operator, user, clientOrderId) rather than by a check in a handler. Retrying is the correct response to a timeout.
  • Money. Exact decimal strings inside operatorMoney, operatorExitMoney and a settlement. Numbers elsewhere are for display. Contract prices are 0–1, and sub-cent prices are real.
  • Cursors. Keyset, not offset. Loop on nextCursor; a last page legitimately has none. A malformed cursor is refused rather than treated as “from the beginning”.
  • Errors. A JSON body with a machine-readable error. Branch on the code, never on the message.
  • Rate limits. Per key, per minute. RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset are on every response, not only on a 429.
  • Timestamps. ISO-8601, UTC, always as strings.
  • Sourcing. No response on this surface names a venue. A contract is a ctr_… id.

Scopes

ScopeReaches
markets:readevents, events/{id}, categories
quotes:writequotes
orders:writePOST orders
orders:readGET orders, orders/{id}
positions:readpositions, positions/{id}
settlements:readsettlements, settlements/positions
ledger:readreconciliation, reconciliation/feed, and the sandbox balance and ledger
funding:writeall three funding routes, including the two reads