Predictu
API Reference

Casino API

The Casino API is the primary interface for interacting with Predictu’s prediction market platform. It handles trading, balance management, position tracking, and market configuration. All endpoints require authentication via a session token (for user-facing requests) or an operator API key (for server-to-server requests).

Base URL: https://app.predictu.com/api/casino
Authentication: Bearer token in Authorization header, or operator API key in X-Operator-Key header for S2S calls.
Content-Type: All requests and responses use application/json.

Error Handling

All endpoints return errors in a consistent format. HTTP status codes follow REST conventions.

// Error response format:
{
  "error": "Insufficient balance"
}

Error responses always contain an error string. Some endpoints return structured error codes for programmatic handling:

HTTP StatusMeaningExample Error
400Validation / business logic errorMissing required fields, Invalid side, Amount must be positive
400Market not tradeableMarket not tradeable, Market exposure cap reached
400Insufficient balanceInsufficient balance
400Risk engine rejectionTrade rejected by risk engine
401Missing or invalid authenticationUnauthorized
403Authenticated but not authorizedAdmin access required
500Unexpected server errorFailed to fetch positions

Trade (Buy/Sell)

POST/api/casino/trade

Place a buy or sell trade on a prediction market. This is the core trading endpoint. It runs through a 12-step execution pipeline: validate inputs, run risk assessment (5 walls of defence), calculate spread-adjusted price, resolve operator, get wallet adapter, check balance, debit/credit via adapter, record trade, upsert position, update exposure, update wagering stats, and return the result (or rollback via compensating transaction on failure).

Request Body

{
  "marketId": "0x1234abcd...",
  "side": "buy",
  "outcome": "yes",
  "amount": 25.00,
  "midPrice": 62,
  "marketTitle": "Will Bitcoin hit $100k by June?",
  "category": "crypto",
  "liquidity": 150000
}
FieldTypeRequiredDescription
marketIdstringYesThe market to trade on (Polymarket condition ID).
sidestringYes"buy" or "sell"
outcomestringYes"yes" or "no" (lowercase)
amountnumberYesUSD amount to spend (buy) or number of shares to sell (sell). Must be > 0.
midPricenumberYesCurrent market mid price in cents (0–100). The spread is applied on top of this.
marketTitlestringNoHuman-readable market title. Stored on the trade and position records.
categorystringNoMarket category (e.g. "crypto", "politics"). Used for tradeability checks.
liquiditynumberNoMarket liquidity value. Used for tradeability checks (defaults to 100000 if omitted).

Response (200 OK)

{
  "success": true,
  "tradeId": "uuid-string",
  "positionId": "uuid-string",
  "executionPrice": 64,
  "shares": 39.0625,
  "cost": 25.00,
  "newBalance": 75.00
}
FieldTypeDescription
successbooleanAlways true on 200 responses.
tradeIdstringUUID of the recorded trade.
positionIdstringUUID of the upserted position.
executionPricenumberSpread-adjusted execution price in cents.
sharesnumberNumber of shares bought or sold.
costnumberUSD cost of the trade (buy) or proceeds received (sell).
newBalancenumberUser’s balance after the trade.

Selling Positions

When side is "sell", the amount field represents the number ofshares to sell (not a dollar amount). The user must hold at least this many shares in the specified outcome. Sell responses include an additional realizedPnl field.

// Sell request:
{
  "marketId": "0x1234abcd...",
  "side": "sell",
  "outcome": "yes",
  "amount": 20.00,
  "midPrice": 68
}

// Sell response includes realizedPnl:
{
  "success": true,
  "tradeId": "uuid-string",
  "positionId": "uuid-string",
  "executionPrice": 66,
  "shares": 20.00,
  "cost": 13.20,
  "newBalance": 88.20,
  "realizedPnl": 0.60
}
Compensating transactions: If any step after the balance change fails, the system automatically reverses the debit/credit via the wallet adapter. If the reversal also fails, the error message will indicate that the balance was NOT restored and manual intervention is needed.

Trade Executor Pipeline

Every trade passes through a 12-step pipeline inside the Trade Executor:

  1. Validate inputs - check required fields, side, outcome, positive amount.
  2. Check market tradeability - blocked categories, disabled markets, price/liquidity criteria.
  3. Risk assessment - 5 walls of defence (velocity, tier limits, exposure caps, sharp detection, operator limits).
  4. Calculate spread-adjusted price - apply base spread + per-market overrides + per-user sharp adjustments.
  5. Resolve operator - look up the user’s operator_id for multi-tenancy.
  6. Get wallet adapter - resolve the S2S callback adapter for the user’s operator.
  7. Check balance - verify sufficient funds via the adapter.
  8. Debit/credit balance - execute the balance change via the wallet adapter.
  9. Record trade - insert into casino_trades with full audit fields.
  10. Upsert position - create or average into casino_positions (with optimistic concurrency).
  11. Update exposure - recalculate casino_exposure (net exposure, max loss).
  12. Update wagering stats - increment total_wagered on the user record (best-effort).

On failure after step 8, a compensating transaction reverses the balance change viaadapter.rollback().

Quote

GET/api/casino/quote

Get a price quote for a potential trade without executing it. Quotes include the spread and are calculated synchronously against the default spread configuration. No authentication is required.

Query Parameters

ParameterTypeRequiredDescription
sidestringYes"buy" or "sell"
outcomestringYes"yes" or "no" (lowercase)
amountnumberYesUSD amount (buy) or number of shares (sell)
midPricenumberYesCurrent market mid price in cents (0–100)

Response (200 OK)

{
  "executionPrice": 64,
  "shares": 39.0625,
  "cost": 25.00,
  "spreadPct": 4,
  "payout": 39.06,
  "midPrice": 62
}
FieldTypeDescription
executionPricenumberSpread-adjusted execution price in cents. Ask price for buys, bid price for sells.
sharesnumberNumber of shares the user would receive (buy) or is selling (sell).
costnumberUSD cost of the trade.
spreadPctnumberEffective spread percentage applied.
payoutnumberPotential payout if the outcome is correct ($1 per share).
midPricenumberThe mid price that was passed in, echoed back.
Note: Quotes from this endpoint use the default spread configuration (4% default, 1% min, 15% max). Actual trade execution may apply per-market spread overrides and per-user sharp adjustments that are not reflected in quotes.

Balance

Get Balance

GET/api/casino/balance

Returns the authenticated user’s current balance. The balance is resolved through the wallet adapter, which sends a BALANCE callback to the operator’s S2S server.

Query Parameters

ParameterTypeRequiredDescription
ledgerstringNoSet to "true" to include the 50 most recent ledger entries.

Response (200 OK)

{
  "balance": 100.00,
  "ledger": null
}

When ledger=true is passed, the ledger field contains an array of the 50 most recent s2s_transactions entries (most recent first). When omitted or false,ledger is null.

// With ledger=true:
{
  "balance": 100.00,
  "ledger": [
    {
      "id": "uuid",
      "user_id": "uuid",
      "amount": 25.00,
      "type": "trade_debit",
      "description": "Buy 39 shares YES @ 64c",
      "created_at": "2025-03-18T14:32:01.000Z"
    }
  ]
}

Admin Balance Adjustment

POST/api/casino/balance

Admin-only endpoint to deposit or withdraw funds from a user’s balance. Used for promotional credits, refunds, and manual corrections. Requires admin role.

Request Body

{
  "targetUserId": "uuid-string",
  "amount": 50.00,
  "action": "deposit",
  "description": "Promotional welcome bonus"
}
FieldTypeRequiredDescription
targetUserIdstringYesUUID of the target user.
amountnumberYesAmount to deposit or withdraw. Must be > 0.
actionstringYes"deposit" or "withdraw"
descriptionstringNoReason for the adjustment (stored in the ledger). Defaults to "Admin deposit" or "Admin withdrawal".

Response (200 OK)

{
  "success": true,
  "newBalance": 150.00
}
Withdrawal guard: Admin withdrawals cannot reduce a user’s balance below zero. The debitBalance engine will return an error if the amount exceeds the user’s current balance.

Deposit

POST/api/casino/deposit

Deposit funds into the authenticated user’s account. This is the user-facing deposit endpoint (as opposed to the admin balance adjustment above). A maximum balance cap of $100,000 is enforced.

Request Body

{
  "amount": 100.00
}
FieldTypeRequiredDescription
amountnumberYesDeposit amount. Must be > 0 and ≤ 100,000. The deposit will be rejected if the resulting balance would exceed $100,000.

Response (200 OK)

{
  "balance": 200.00
}
FieldTypeDescription
balancenumberThe user’s new balance after the deposit.
Balance cap: The maximum account balance is $100,000. A deposit that would push the balance above this cap is rejected with an error.

Withdraw

POST/api/casino/withdraw

Withdraw funds from the authenticated user’s Predictu balance. A maximum withdrawal of $100,000 per request is enforced, and the user must have sufficient balance.

Request Body

{
  "amount": 50.00
}
FieldTypeRequiredDescription
amountnumberYesWithdrawal amount. Must be > 0, ≤ 100,000, and ≤ the user’s current balance.

Response (200 OK)

{
  "success": true,
  "newBalance": 150.00,
  "amount": 50.00
}
FieldTypeDescription
successbooleanAlways true on 200 responses.
newBalancenumberThe user’s balance after the withdrawal.
amountnumberThe amount that was withdrawn (echoed back).

Positions

GET/api/casino/positions

Returns all positions for the authenticated user, filtered by status. Results are ordered byupdated_at descending (most recently updated first).

Query Parameters

ParameterTypeRequiredDescription
statusstringNoPosition status filter. Default: "open". Other values: "closed", "resolved", "voided".

Response (200 OK)

{
  "positions": [
    {
      "id": "uuid-string",
      "user_id": "uuid-string",
      "market_id": "0x1234abcd...",
      "market_title": "Will Bitcoin hit $100k by June?",
      "outcome": "yes",
      "shares": 39.0625,
      "avg_price": 64,
      "status": "open",
      "realized_pnl": 0,
      "operator_id": "uuid-string",
      "created_at": "2025-03-18T14:32:01.000Z",
      "updated_at": "2025-03-18T15:00:00.000Z"
    }
  ]
}
FieldTypeDescription
positionsarrayArray of position objects. Empty array if no positions match the filter.
No pagination: This endpoint returns all matching positions without pagination. The result set is scoped to the authenticated user only.

Trades

GET/api/casino/trades

Returns the authenticated user’s trade history, ordered by created_at descending (most recent first).

Query Parameters

ParameterTypeRequiredDescription
limitnumberNoMaximum number of trades to return. Default: 50, max: 200.
marketIdstringNoFilter trades to a specific market.

Response (200 OK)

{
  "trades": [
    {
      "id": "uuid-string",
      "user_id": "uuid-string",
      "market_id": "0x1234abcd...",
      "market_title": "Will Bitcoin hit $100k by June?",
      "side": "buy",
      "outcome": "yes",
      "shares": 39.0625,
      "price_mid": 62,
      "price_executed": 64,
      "spread_applied": 2,
      "cost": 25.00,
      "balance_before": 100.00,
      "balance_after": 75.00,
      "operator_id": "uuid-string",
      "s2s_transaction_id": null,
      "created_at": "2025-03-18T14:32:01.000Z"
    }
  ]
}
FieldTypeDescription
tradesarrayArray of trade objects. Empty array if no trades match.

Markets Configuration

GET/api/casino/markets

Returns the casino’s market configuration: blocked categories, individually disabled markets, market selection criteria, and per-market custom spreads. This is not a market listing endpoint -markets are sourced from Polymarket and filtered client-side using these rules. No authentication is required.

Response (200 OK)

{
  "blockedCategories": ["adult", "drugs"],
  "disabledMarketIds": ["0xabc123...", "0xdef456..."],
  "criteria": {
    "min_liquidity": 5000,
    "min_price": 5,
    "max_price": 95
  },
  "customSpreads": {
    "0x1234abcd...": 6,
    "0x5678efgh...": 2.5
  }
}
FieldTypeDescription
blockedCategoriesstring[]Categories that are blocked from trading.
disabledMarketIdsstring[]Individual market IDs that have been manually disabled.
criteriaobjectMarket selection criteria. Contains min_liquidity, min_price, and max_price thresholds.
customSpreadsobjectMap of market ID to custom spread percentage override. Markets not in this map use the default spread.
Client-side filtering: The frontend fetches markets from Polymarket’s API and applies the blocked categories, disabled IDs, and criteria from this endpoint to determine which markets to display. Custom spreads are applied at trade execution time by the Trade Executor.

Resolve Market

POST/api/casino/resolve

Admin-only endpoint to resolve or void a prediction market. Resolving triggers settlement: winning positions are credited $1 per share via the wallet adapter, losing positions are marked as resolved. Voiding refunds all positions at their original cost basis.

Irreversible: Market resolution cannot be undone. Once resolved, all positions are settled and payouts are distributed. Double-check the outcome before calling this endpoint.

Request Body

// Resolve a market:
{
  "marketId": "0x1234abcd...",
  "outcome": "yes",
  "action": "resolve"
}

// Void a market:
{
  "marketId": "0x1234abcd...",
  "action": "void",
  "reason": "Resolution criteria became ambiguous"
}
FieldTypeRequiredDescription
marketIdstringYesThe market to resolve or void.
outcomestringYes (resolve)"yes" or "no" (lowercase). Required when action is "resolve". Ignored for voids.
actionstringNo"resolve" (default) or "void". When "void", all positions are refunded at cost basis.
reasonstringNoReason for voiding. Stored on the settlement record. Defaults to "Voided by admin" when omitted.

Response (200 OK) - Resolve

{
  "success": true,
  "marketId": "0x1234abcd...",
  "outcome": "yes",
  "totalPositions": 142,
  "totalWinners": 89,
  "totalPayout": 4250.00,
  "casinoProfit": -1340.00
}
FieldTypeDescription
successbooleanWhether the resolution completed successfully.
marketIdstringThe market that was resolved.
outcomestringThe winning outcome ("yes" or "no").
totalPositionsnumberTotal number of open positions that were settled.
totalWinnersnumberNumber of winning positions.
totalPayoutnumberTotal USD paid out to winners ($1 per share).
casinoProfitnumberCasino profit: total cost basis collected minus total payouts. Negative means the casino lost money.

Response (200 OK) - Void

{
  "success": true,
  "marketId": "0x1234abcd...",
  "totalPositions": 142,
  "totalPayout": 3500.00,
  "casinoProfit": 0
}

When voiding, all positions are refunded at their original cost basis. casinoProfit is always 0 for voids. The totalPayout represents the total amount refunded.

Rate Limits

The Casino API enforces rate limits to prevent abuse and ensure fair access.

EndpointLimitWindow
POST /trade20 requestsPer minute per user
GET /quote60 requestsPer minute per user
GET /balance60 requestsPer minute per user
GET /positions30 requestsPer minute per user
GET /markets30 requestsPer minute per user
GET /trades30 requestsPer minute per user
POST /deposit5 requestsPer minute per user
POST /withdraw5 requestsPer minute per user
POST /resolve10 requestsPer minute per admin

Rate limit headers are included in every response:

X-RateLimit-Limit: 20
X-RateLimit-Remaining: 18
X-RateLimit-Reset: 1710772321