Skip to main content
PredictStreet runs two market families, both resolved through the same oracle pipeline but with different on-chain settlement adapters.

Binary markets

YES / NO on a single question. Settled through CTFExchange against ConditionalTokens.

Neg-risk markets

3+ mutually exclusive outcomes. Settled through NegRiskAdapter
  • PredictStreetNegRiskCtfExchange.

Lifecycle

DRAFT → PROPOSED → PRE_MARKET → OPEN → PAUSED/SUSPENDED → RESOLVED/VOIDED/CANCELLED
See Market status for the full state machine.

Listing markets

GET /api/markets?status=OPEN
# Public — no auth required. Sending X-Api-Key raises your rate-limit ceiling.
Response:
{
  "markets": [
    {
      "id": "78fe1d92-656c-4daf-af6c-c08baf7d6a1e",
      "eventId": "8e905d52-...",
      "symbol": "M1V1-RMA-MCI-959311",
      "slug": "real-madrid-vs-manchester-city-real-madrid-to-win-the-tie",
      "title": "Real Madrid (ESP) vs Manchester City (ENG) — UCL R16",
      "groupItemTitle": "Real Madrid to win the tie",
      "conditionId": "0xabc…",
      "questionId": "0xdef…",
      "yesTokenId": "12345678901234567890",
      "noTokenId": "98765432109876543210",
      "status": "OPEN",
      "currentReviewRound": 1,
      "outcomeCount": 2,
      "outcomeLabels": ["YES", "NO"],
      "initialFairValue": "0.500000",
      "initialOutcomePrices": ["0.520000", "0.480000"],
      "feeTakerBps": 240,
      "opensAt": "2026-04-23T16:00:00Z",
      "closesAt": "2026-04-30T13:30:00Z",
      "kickoff": "2026-04-30T15:48:31Z",
      "resolutionWindowHours": 24,
      "pausedAt": null,
      "resolvedAt": null,
      "payoutNumerators": null,
      "officialDataSource": "UEFA official aggregate",
      "sourceMatchId": "ucl-26-r16-rma-mci",
      "resolutionCriteria": "Settles to the team that advances on aggregate.",
      "negRiskEligible": false,
      "isNewMarketType": false,
      "createdAt": "2026-04-20T12:00:00Z",
      "updatedAt": "2026-04-20T12:00:00Z"
    }
  ]
}

Polymarket-equivalent fields

FieldMeaning
slugCanonical URL-safe identifier — lowercased kebab-case derived from event.slug + groupItemTitle + title. Required path parameter for GET /api/markets/{slug} (see Breaking change below). symbol is the operator-facing identifier and is unchanged.
groupItemTitlePer-market label inside a multi-market event accordion ("Real Madrid", "Liverpool", "Draw"). Equal to title for single-market events.
initialOutcomePricesPer-outcome seed prices parallel to outcomeLabels. Polymarket calls this outcomePrices.

feeTakerBps

The per-market quadratic taker-fee curve rate, in basis points (1 bps = 0.01 %). Read this fresh from GET /api/markets/{slug} each time you build an order digest — your EIP-712 feeRateBps field MUST equal this value or the backend rejects the signed order with bad_signature (see EIP-712 signing). Production fee on every listed market: 240 bps (2.4 %). The admin.markets.fee_taker_bps column on every live market carries 240. Treat that as the working figure for any fee-cost projection and partner integration test — there is no per-listing variation in the production catalogue today. The effective fee the user actually pays is not flat 2.4 % — the quadratic curve fee = k × P × (1−P) × outcomeTokens brings the effective rate down at the price extremes (≈ 0 % near 0/1, peak near 0.5). See the CTFExchange contract page for the exact fee = feeRateBps × … formula split by BUY / SELL leg. Maker fees are currently 0 across every market (taker pays). Fees are immutable for the lifetime of a market. Listing operators cannot change fee_taker_bps while a market is OPEN / resolving; if a different rate is needed, they create a new market.

On-chain identifiers

conditionId / questionId are the bytes32 hex values from ConditionalTokens. yesTokenId / noTokenId are the ERC-1155 position ids you sign over in your EIP-712 Order typed data. All four are null until the market’s deployment is confirmed (status PRE_MARKET or beyond). For multi-outcome (neg-risk) markets noTokenId is null — each outcome is its own YES token managed by the NegRiskAdapter. Pull the per-outcome token ids from the on-chain adapter via the conditionId. See Events overview for how markets group under events and how to filter the public catalog.

Individual market detail

GET /api/markets/{slug}
Breaking change (2026-05-16) — root market lookup is now resolved by slug, not symbol. Sending a symbol returns 404 market_not_found. The list response above carries a slug field (canonical, URL-safe, derived from the parent event + title); pass that value here.The sub-resource paths (/orderbook, /trades, /ohlc, /traders below) still resolve by symbol — only the root market detail endpoint changed.
Same shape as the list item above. For the event metadata around the market (group, stage, teamA, teamB, tags, eventStartTime), call GET /api/events/{id} with the event id from the parent grouping (see the Events group in the API reference sidebar).
# Discover slugs from the list endpoint, then fetch detail by slug.
curl "$BASE/api/markets?status=OPEN" \
  | jq -r '.markets[] | "\(.symbol)\t\(.slug)"'

curl "$BASE/api/markets/real-madrid-vs-manchester-city-real-madrid-to-win-the-tie"

Orderbook snapshot

GET /api/markets/{symbol}/orderbook?outcome=0&depth=20
Sub-resource — still keyed by symbol. For live book updates, use the orderbook WebSocket channel — polling is rate-limited and latency-inferior.