🤖
Building with an AI Agent or LLM?
The Myriad CLI is the best, fastest, way to get your agent trading on Myriad Markets.
npm install -g @myriadmarkets/cli
This document describes the public REST API exposed by the Myriad Protocol API service — both the AMM endpoints and the Order Book (orders, positions, events, and real-time WebSocket updates).
Base URLAuthenticationLegacy auth sunset — August 15, 2026Getting your first API keySession JWT (Sign-In With Ethereum) — for first-party appsAPI key + secret (HMAC) — for agents / market makersWebSocket (Centrifugo) subscription tokensRate LimitingPaginationMarketsGET /marketsGET /markets/:idGET /markets/:id/eventsGET /markets/:id/referralsGET /markets/:id/holdersGET /markets/:id/orderbookGET /markets/:id/tradesPOST /markets/quotePOST /markets/quote_with_feePOST /markets/claimUsersGET /users/:address/eventsGET /users/:address/referralsGET /users/:address/portfolioGET /users/:address/marketsOrder Book ConceptsTrading ModelOrder Signing (EIP-712)Signature TypesPrice and Amount ScalePrice Tick SizeSidesOutcomesTime-in-ForceOrder PriorityMatch TypesOrder LifecycleOrdersPOST /ordersGET /ordersGET /orders/:orderHashDELETE /orders/:orderHashPOST /orders/batchPOST /orders/batch-modifyPOST /orders/cancel-batchPOST /orders/cancel-allPositionsPOST /positions/splitPOST /positions/mergePOST /positions/redeemPOST /positions/redeem-voidedPOST /positions/neg-risk/splitPOST /positions/neg-risk/mergeEventsGET /eventsGET /events/:idGET /events/:id/orderbookGET /events/:id/actionsReal-time Updates (WebSockets)ConnectionUsing the client library (recommended)Raw WebSocket (no client library)ChannelsPayload conventionsOrder book deltaTradeSettlementPrice updateOrder updatePosition updateMarket eventTagsGET /tagsTopicsGET /topicsPrice DataErrorsNetworksBNB Smart ChainOther chainsAbstractLineaCeloAuditsOrder BookAMMChangelogV2.0.0V2.0.1V2.0.2V2.0.3V2.0.4

Base URL

Authentication

Most read endpoints remain public, with higher rate limits if an API key is provided. Some endpoints require authentication and whitelisting, such as /markets/quote_with_fee.
Order endpoints, the /user/* reads, and per-trader WebSocket subscriptions require authentication tied to your account's connected wallet. Unauthenticated requests to those endpoints return 401 on/after the cutover date — August 15, 2026 (see Legacy auth sunset). As part of this change, GET /orders returns only your own orders (the ?trader= parameter is deprecated). The /users/:address/* reads stay open — anyone can read any address; /user/* offers the same reads self-scoped to your connected wallet.
An account is identified by a wallet address and/or an email and can hold multiple API keys. The connected wallet is the wallet that authenticated the account via SIWE (see below); wallet-scoped endpoints operate on it. An account that has never completed a wallet sign-in (e.g. created by email) has no connected wallet — wallet-scoped endpoints then return 409 { "code": "NO_WALLET_CONNECTED" }, resolved by completing a SIWE login with the wallet once.

Legacy auth sunset — August 15, 2026

The transitional "soft window" ends on August 15, 2026. Until then, legacy and unauthenticated access patterns keep working but every response served through them carries Deprecation: true and Sunset: <date> headers. From the cutover, they are rejected. What changes, per surface:
Surface
Until Aug 15, 2026 (soft window)
From Aug 15, 2026
Bare API keysx-api-key header or ?api_key= with no HMAC signature, on any authenticated endpoint
Accepted, with deprecation headers
Rejected: 401 { "code": "SIGNATURE_REQUIRED" }. Every keyed request must be HMAC-signed (x-api-key + x-api-timestamp + x-api-signature) or use a session JWT
Order endpoints without a connected walletGET /orders, GET /orders/:orderHash, POST /orders, DELETE /orders/:orderHash, POST /orders/batch, POST /orders/batch-modify, POST /orders/cancel-batch, POST /orders/cancel-all
Unscoped (legacy) access allowed, with deprecation headers
A connected wallet is required: no credential → 401; authenticated but no wallet on the account → 409 { "code": "NO_WALLET_CONNECTED" }. Orders whose trader differs from your connected wallet are rejected (403 WALLET_MISMATCH; GET /orders/:orderHash returns 404)
Not affected by the date: the /users/:address/* reads (anyone can read any address, before and after), the /user/* reads (already require a connected wallet today), and most market-data reads (/markets, /events, orderbook, trades) remain public.
To migrate before the cutover:
  1. Replace bare-key integrations with HMAC signing (see API key + secret). Keys issued before secrets existed have no secret and cannot be upgraded — mint a new key via POST /auth/api-keys or Myriad Account settings and retire the old one.
  1. Connect a wallet to the account by completing one SIWE login (POST /auth/login) with the wallet your orders trade as — API keys minted from that session act on behalf of that wallet.
  1. Drop the ?trader= parameter on GET /orders (it must match your connected wallet anyway).
  1. Watch for Deprecation/Sunset response headers in the meantime — any request still receiving them will break at the cutover.

Getting your first API key

Every credential starts with a wallet sign-in (SIWE). Logging in creates your account on first use and permanently ties it to your wallet. API keys are then minted from that session and act on behalf of that wallet — there is no separate "link key to wallet" step.
plain text
1. GET /auth/nonce?address=0xYourWallet → { nonce, message } 2. Sign `message` with the wallet # personal_sign — any wallet lib / MetaMask 3. POST /auth/login { message, signature }{ accessToken, ... } # account auto-created 4. POST /auth/api-keys { "label": "my agent" }{ key, secret } # Authorization: Bearer <accessToken>
Agents keep the key + secret from step 4 and HMAC-sign every request from then on — the wallet sign-in was only needed once, to mint the key. First-party apps can skip step 4 entirely and keep using the session token. Keys can also be created without touching the API, from Myriad Account settings.
The two credential types in detail:

Session JWT (Sign-In With Ethereum) — for first-party apps

plain text
1. GET /auth/nonce?address=0xYourWallet → { nonce, message } 2. Sign `message` with the wallet (personal_sign). 3. POST /auth/login { message, signature }{ accessToken, refreshToken, user } 4. Use it: Authorization: Bearer <accessToken> 5. POST /auth/refresh { refreshToken }new tokens; POST /auth/logout { refreshToken }
Access tokens are short-lived (default 15m); refresh tokens are long-lived (default 30d), rotated on use, and safe for the frontend to persist.
Nonces expire after 10 minutes and are single-use. A nonce is consumed on successful login, and also by any smart-contract-wallet (EIP-1271) verification attempt — successful or not — so after a failed SCW attempt, request a fresh nonce and re-sign. A signature that fails plain EOA recovery does not consume the nonce.
Smart-contract wallets (SCW): POST /auth/login accepts EIP-1271 signatures as well as EOA signatures. It first attempts standard ECDSA recovery; if that fails, it verifies the signature via EIP-1271 (isValidSignature) against the address in the SIWE message. This lets a smart-contract wallet sign in with the same flow — no extra parameter is required.

API key + secret (HMAC) — for agents / market makers

Long-lived, never needs re-auth mid-session. A key is bound to the account — and therefore the connected wallet — of the session that created it: it reads and trades as that wallet. Create one with a session JWT (see above), or via Myriad Account settings:
plain text
POST /auth/api-keys { "label": "MM agent prod" }{ id, key, secret, ... } # secret shown ONCE GET /auth/api-keys # list (never returns secrets) DELETE /auth/api-keys/:id # revoke
Sign each request: X-Signature = HMAC-SHA256(secret, "<timestamp>.<METHOD>.<path+query>.<rawBody>").
javascript
import crypto from 'crypto'; const ts = Math.floor(Date.now() / 1000).toString(); const sig = crypto.createHmac('sha256', SECRET) .update(`${ts}.POST./orders.${JSON.stringify(body)}`) .digest('hex'); await fetch('https://api-v2.myriadprotocol.com/orders', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': KEY, 'x-api-timestamp': ts, 'x-api-signature': sig }, body: JSON.stringify(body), });
  • The timestamp must be within ±30s of server time (replay protection).
  • path includes the query string; rawBody is the exact bytes sent (empty for GET).
  • A legacy bare x-api-key (no signature) is accepted until August 15, 2026, then rejected — see Legacy auth sunset.
A key may be configured by the Myriad team with bypass_signature_verification to skip the server-side EIP-712 order-signature check on order create/cancel for faster ingestion. The order signature is still required in every request, is still stored, and is still verified on-chain at settlement — only the redundant API-side check is skipped, and only for the wallet connected to that key. Reach out to the Myriad team if your agent needs this.

WebSocket (Centrifugo) subscription tokens

Public channels (orderbook:*, trades:*, settlements:*, prices:*, markets:*) are open. The per-trader orders:* / positions:* channels require a short-lived token, issued only for channels belonging to your connected wallet:
plain text
POST /auth/centrifugo/subscription-token { "channel": "orders:97:0xyourwallet" }{ token, expiresIn }
Connect to Centrifugo anonymously and supply the token via the SDK's per-subscription getToken callback so it auto-refreshes on expiry.

Rate Limiting

  • Requests with API key - 200 requests/second per IP and/or API key.
  • Requests w/o API key - 30 requests/10 seconds per IP
  • Order placement: 200 orders per 10-second window per trader address (shared across single and bulk order endpoints).
  • Headers included on responses:
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • X-RateLimit-Reset

Pagination

All list endpoints support pagination:
  • page (default: 1)
  • limit (default: 20, max: 100)
Response pagination object:
json
{ "pagination": { "page": 1, "limit": 20, "total": 123, "totalPages": 7, "hasNext": true, "hasPrev": false } }

Markets

GET /markets

Paginated list of markets with filtering and sorting.
Query params:
  • page, limit (pagination)
  • trading_model: amm | ob | all (default: amm). AMM-only by default — pass ob for Order Book markets or all for both (see Order Book Concepts)
  • sort: volume | volume_24h | volume_notional | volume_notional_24h | liquidity | expires_at | published_at | in_play_starts_at | featured (default: volume)
  • order: asc | desc (default: desc)
  • network_id: comma-separated list of network ids (e.g. 2741,59144)
  • state: open | closed | resolved
  • token_address: comma-separated list of ERC20 token addresses
  • oracle_address: comma-separated list of oracle contract addresses (matched case-insensitively)
  • topics: comma-separated list of topics
  • tags: tag slug(s) to filter by. Repeat the param to AND multiple groups; comma-separate within a value to OR (e.g. tags=world-cup&tags=stage-a,stage-bworld-cup AND (stage-a OR stage-b))
  • exclude_tags: comma-separated tag slugs to exclude (a market matching ANY is filtered out)
  • keyword: full-text search across title, description, and outcome titles
  • ids: comma-separated list of on-chain market ids
  • event_id: filter to markets belonging to a single event (UUID)
  • in_play: boolean (true/false/1/0)
  • moneyline: boolean (true/false/1/0)
  • min_duration: minimum market duration in seconds (filters by expiresAt - publishedAt)
  • max_duration: maximum market duration in seconds (filters by expiresAt - publishedAt)
  • in_play_starts_at_gte: unix seconds (inclusive) — only markets with inPlayStartsAt >= this timestamp; markets without inPlayStartsAt are excluded
  • in_play_starts_at_lte: unix seconds (inclusive) — only markets with inPlayStartsAt <= this timestamp; markets without inPlayStartsAt are excluded
  • group_by_event: boolean (true/false/1/0). When true, sibling markets sharing an event are collapsed into a single event row ({ type: "event", ... }); standalone markets are returned as { type: "market", ...market }. Pagination then operates on logical rows. Event rows for sports carry scoreboard (the match scoreboard, same shape as the market field above) at the event level; child markets in markets[] are ordered canonically (matching outcome0/outcome1), so align them positionally. Filters such as state, keyword, topics, in_play, moneyline, ids, min_duration, max_duration, and the fee filters apply at the sibling level — an event row is included if any of its siblings match; network/token/visibility filters always apply per market.
Fee filters
Filter markets by protocol fees (decimals between 0 and 1). Fees can be filtered separately for buy and sell, for individual components or by total.
  • Components:
    • lp (liquidity provider fee, field fee)
    • dt (distributor fee, field distributor_fee)
    • tr (treasury fee, field treasury_fee)
    • total = fee + treasury_fee + distributor_fee
  • Actions: buy, sell
  • Operators: lt, lte, gt, gte, eq
  • Parameter patterns:
    • Component-specific: {action}_{component}_fee_{operator} (e.g., buy_lp_fee_lte=0.01)
    • Total: {action}_fee_{operator} (e.g., sell_fee_lt=0.02)
  • Examples:
    • buy_lp_fee_lte=0.01 → buy LP fee ≤ 1%
    • sell_tr_fee_gte=0.0025 → sell treasury fee ≥ 0.25%
    • buy_dt_fee_eq=0.003 → buy distributor fee = 0.3%
    • sell_fee_lt=0.02 → total sell fee < 2%
  • Notes:
    • Multiple fee filters are combined with AND.
    • All fee values are decimals (e.g., 0.01 = 1%).
Example:
plain text
GET /markets?keyword=eth&network_id=2741&page=1&limit=20&sort=volume_24h
Response data (per market):
  • id: blockchain market id
  • networkId: number
  • slug, title, shortName, ctaName, description
  • publishedAt, expiresAt, resolvesAt
  • fees, state, voided, resolvedOutcomeId, topics, tags, resolutionSource, resolutionTitle
  • token: { address, symbol, name, decimals }
  • imageUrl, bannerImageUrl, ogImageUrl
  • liquidity, liquidityPrice, volume, volume24h, volumeNotional, volumeNotional24h, users, shares
  • featured, featuredAt, inPlay, inPlayStartsAt, perpetual, moneyline
  • oracle: always-present object { address, name, url, image_url, data }. address/data are null for markets Myriad resolves itself; name/url/image_url are always populated (brand metadata).
  • scoreboard: live-scores scoreboard for standalone sports markets, else null. For event-bound markets the scoreboard lives on the parent event (see GET /events/:id), not the child. Only fields with data are present — a soccer card omits periodScores, a pre-game card omits clock, etc. Always present: type, status, outcome0, outcome1, updatedAt. Shape:
    • type: sport slug, e.g. "soccer" / "tennis" — the frontend picks a card layout from this
    • status: "scheduled" | "live" | "finished"; statusDetail: provider status text (e.g. "1st Half"), present when known
    • startsAt: ISO kickoff time, present when known
    • clock: { period?, minute?, stopped } — present only while live and carrying time. Animate the minute locally between refreshes.
    • outcome0 / outcome1: the two match sides (always exactly two), each { name, ctaName?, score?, imageUrl? } (optional keys present only when available). outcome0 is the home team / first player; name/ctaName/imageUrl prefer the Myriad market/event data (outcome title, CTA label, outcome image) and fall back to the provider's participant data. Betting outcomes are created in canonical order matching these (home/away or player1/player2), so align rows positionally.
    • periodScores: array of { period, outcome0, outcome1 } (e.g. tennis games per set), present only when the sport has them
    • venue: { name, location? } — the match venue, present when known
    • updatedAt: ISO timestamp of when the score was last captured (score freshness)
  • externalSources: array of { id, providerName, externalMarketId, externalMarketUrl, externalMarketTitle } (may be empty)
  • topHolders: array (may be empty)
  • outcomes: array with:
    • id (on-chain outcome id)
    • title
    • price, closingPrice, priceChange24h
    • bestBid, bestAsk: best resting order per side as a 0–1 decimal (binary-implied from the opposite outcome included). null when there is no order on that side, or when the market is not open. Use bestAsk to price a buy without waiting on a separate order-book fetch.
    • shares, sharesHeld
    • holders
    • imageUrl
    • tokenId: ERC1155 token id ((ethMarketId << 1) | outcomeId)
Order Book / event fields (present on every market regardless of trading model — see Order Book Concepts):
  • executionMode: 0 (AMM) or 1 (Order Book)
  • tradingModel: "amm" or "ob" (string form of executionMode)
  • eventId: parent event UUID, or null for standalone markets
  • outcomeIndex: index within the parent event, or null
  • negRisk: boolean — whether the market belongs to a NegRisk (mutually-exclusive) event
Notes:
  • Numeric monetary/decimal fields are returned as numbers.

GET /markets/:id

Get a single market by slug or by marketId + network_id.
Modes:
  • By slug: GET /markets/{slug}
  • By id + network: GET /markets/{marketId}?network_id=2741
Requires trading_model=ob or trading_model=all to return Order Book markets (AMM-only by default).
Price charts:
  • Field outcomes[*].price_charts.
  • Timeframes and buckets
    • 24h: 5-minute (max 288)
    • 7d: 30-minute (max 336)
    • 30d: 4-hour (max 180)
    • all: 4-hour
  • Series end at min(now, expiresAt) with backfill from the last known price before the window start.
Example:
plain text
GET /markets/164?network_id=2741

GET /markets/:id/events

Paginated actions (trades/liquidity/claims) for a market, ordered by timestamp desc.
Lookup:
  • By slug: GET /markets/{slug}/events
  • By id + network: GET /markets/{marketId}/events?network_id=2741
Query params:
  • page, limit
  • trading_model: amm | ob | all (default amm) — pass ob or all to include Order Book market events
  • since: unix seconds (inclusive)
  • until: unix seconds (inclusive)
  • only_relevant: 1/true/yes to exclude wash-trade actions (where relevant = false)
Response items:
  • user: wallet address
  • action: buy | sell | add_liquidity | remove_liquidity | claim_winnings | claim_liquidity | claim_fees | claim_voided
  • marketTitle, marketSlug, marketId, networkId
  • outcomeTitle, outcomeId
  • imageUrl
  • shares, value: numbers
  • timestamp: unix seconds
  • blockNumber: number
  • token: ERC20 token address used for this market
  • txId: transaction hash
Example:
plain text
GET /markets/164/events?network_id=2741&since=1755600000&until=1755800000&page=1&limit=50

GET /markets/:id/referrals

Paginated referrals for a market, ordered by timestamp desc.
Lookup:
  • By slug: GET /markets/{slug}/referrals
  • By id + network: GET /markets/{marketId}/referrals?network_id=2741
Query params:
  • page, limit
  • since: unix seconds (optional, inclusive)
  • until: unix seconds (optional, inclusive)
  • code: referral code (optional)
Response items (camelCase):
  • user: wallet address
  • action: buy | sell (referral trade type)
  • marketTitle, marketSlug, marketId, networkId
  • outcomeTitle, outcomeId
  • imageUrl
  • value: number (trade value that generated referral fees)
  • timestamp: unix seconds
  • blockNumber: number
  • token: ERC20 token address used for this market
  • code: referral code used
  • fees:
    • lp (number): LP fee portion attributed to this referral
    • treasury (number): treasury fee portion
    • distributor (number): distributor/referrer fee portion
Example:
plain text
GET /markets/164/referrals?network_id=2741&since=1755600000&until=1755800000&page=1&limit=50

GET /markets/:id/holders

Market holders grouped by outcome. Aggregates buy/sell actions to compute net shares per user for each outcome, filters holders with at least 1 share, orders by shares, and applies the limit per outcome.
Lookup:
  • By slug: GET /markets/{slug}/holders
  • By id + network: GET /markets/{marketId}/holders?network_id=2741
Query params:
  • page, limit (pagination; limit applies per outcome)
  • network_id (required when using marketId)
Response data (per outcome):
  • outcomeId: number
  • outcomeTitle: string | null
  • totalHolders: total addresses with ≥ 1 share in this outcome
  • holders: array limited per outcome with:
    • user: address
    • shares: number
Pagination notes:
  • total equals the maximum total_holders across outcomes for this market.
  • totalPages = ceil(max(total_holders) / limit); limit is applied per outcome.
Example:
plain text
GET /markets/164/holders?network_id=2741&page=1&limit=50

GET /markets/:id/orderbook

Aggregated orderbook for an Order Book market outcome. Returns open, non-expired orders with remaining size, grouped by price level. Accepts either a numeric market ID (with network_id) or a market slug.
Query parameters:
Param
Type
Description
network_id
number
Network ID (required when using numeric ID)
outcome
0 or 1
Outcome to query (default 0)
Response (200):
json
{ "bids": [ ["500000000000000000", "3000000000000000000"], ["490000000000000000", "1500000000000000000"] ], "asks": [ ["510000000000000000", "2000000000000000000"], ["520000000000000000", "5000000000000000000"] ] }
Each entry is [price, remaining_amount] as strings. Bids are sorted descending by price, asks ascending.

GET /markets/:id/trades

Recent Order Book trades for a market, ordered by timestamp descending. Accepts either a numeric market ID (with network_id) or a market slug.
Query parameters:
Param
Type
Description
network_id
number
Network ID (required when using numeric ID)
outcome
0 or 1
Filter by outcome (optional)
limit
number
1-200 (default 50)
page
number
Page number (default 1)
Response (200):
json
[ { "price": "0.5", "priceAfterFees": "0.51", "amount": "1000000000000000000", "side": "buy", "outcome": 0, "txHash": "0x...", "timestamp": 1719835200, "fees": { "total": "10000000000000000", "lp": "0", "treasury": "5000000000000000", "distributor": "5000000000000000" } } ]
side reflects the underlying action and may be buy, sell, split, or merge (mint/merge settlements surface as split/merge). price is the pre-fee execution price and priceAfterFees folds in the trade's fees; amount and all fees.* values are uint strings in the token's smallest unit.

POST /markets/quote

Get a trade quote and transaction calldata for a specific market outcome.
  • Method: POST
  • Path: /markets/quote
  • Body: JSON
Request body:
  • Exactly one of the following is required (send only one):
    • market_id (number): on-chain market id + network_id (number): network id
    • market_slug (string)
  • outcome_id (number, required): on-chain outcome id
  • action (string, required): buy | sell
  • Exactly one of the following is required (send only one):
    • value (number): amount of tokens to spend (buy) or receive (sell)
    • shares (number): number of shares to buy or sell
  • slippage (number, optional, default 0.005): between 0 (0%) and 1 (100%)
Validation rules:
  • For buy: provide only value; shares must be omitted.
  • For sell: provide exactly one of value or shares.
  • Market must exist and be open.
  • Market must have at least 2 outcomes and sufficient shares/liquidity.
Response body:
  • value (number): input value used for the calculation
  • shares (number): expected shares bought/sold
  • shares_threshold (number): min acceptable based on slippage (for buy, min shares; for sell, min value)
  • price_average (number): average execution price
  • price_before (number): price before the trade
  • price_after (number): price after the trade
  • calldata (string): hex-encoded calldata for the contract
  • net_amount (number): value after protocol/treasury/distributor fees
  • fees:
    • treasury (number)
    • distributor (number)
    • fee (number)
Example request (buy by value):
json
{ "market_id": 164, "outcome_id": 0, "network_id": 2741, "action": "buy", "value": 100, "slippage": 0.01 }
Example success response:
json
{ "value": 100, "shares": 312.3456, "shares_threshold": 309.2221, "price_average": 0.3201, "price_before": 0.315, "price_after": 0.3252, "calldata": "0x...", "net_amount": 99.3, "fees": { "treasury": 0.2, "distributor": 0.1, "fee": 0.4 } }
Possible errors:
  • 400 Invalid request parameters, unsupported network, market not open, insufficient liquidity, or invalid slippage/value/shares combination
  • 404 Market or outcome not found
  • 500 Unable to resolve token decimals or unexpected server error

POST /markets/quote_with_fee

Select integrators can charge a frontend fee at the time of a trade by using bundled transactions (wallet_sendCalls) that include a transfer plus the trade (and token approval when needed). The frontend fee is in addition to any of the standard fees that apply to markets, and which are managed by the smart contract.
To use this flow, your dapp/wallet integration must support EIP-5792 transactions.
This endpoint requires API authentication and whitelisting. If you’re interested in using the Myriad API to charge frontend fees for your integration, please reach out to the Myriad team.
Get the trade quote plus an ordered set of EIP-5792 calls that can be sent via wallet_sendCalls to:
  1. (Optionally) approve ERC20 collateral spending for the PredictionMarket contract (buy-only, included only when allowance is insufficient)
  1. Transfer a frontend fee to to_wallet
  1. Execute the trade
Request body:
  • All fields from POST /markets/quote
  • fee (number, required): decimal fee rate, between 0 and 0.05 (e.g. 0.01 = 1%)
  • from_wallet (string, required): wallet that will sign/send the bundle (used for allowance+balance checks)
  • to_wallet (string, required): fee recipient wallet
Response body:
  • All fields from POST /markets/quote
  • fees.frontend (number): frontend fee amount in token units
  • approval:
    • required (boolean)
    • currentAllowanceWei (string, optional)
    • requiredAllowanceWei (string, optional)
  • calldata (object): ready to pass to wallet_sendCalls as the first (and only) params item to an EIP-5792 capable wallet
    • version: "2.0.0"
    • from: sender address (same as from_wallet)
    • chainId: hex chain id (derived from market network_id)
    • atomicRequired: true
    • calls: ordered array of { to, data, value } (hex quantities)
Example request (buy with 1% frontend fee):
json
{ "market_id": 164, "outcome_id": 0, "network_id": 2741, "action": "buy", "value": 100, "slippage": 0.01, "fee": 0.01, "from_wallet": "0x0000000000000000000000000000000000000001", "to_wallet": "0x0000000000000000000000000000000000000002" }
Example success response:
json
{ "value": 100, "shares": 107.71301097629218, "shares_threshold": 107.17444592141072, "price_average": 0.9191090203744288, "price_before": 0.89954079, "price_after": 0.9019004096700267, "calldata": { "version": "2.0.0", "from": "0x0000000000000000000000000000000000000001", "chainId": "0xab5", "atomicRequired": true, "calls": [ { "to": "0x55d398326f99059fF775485246999027B3197955", "data": "0x095ea7b300000000000000000000000039e66ee6b2ddaf4defded3038e0162180dbef3400000000000000000000000000000000000000000000000055de6a779bbac0000", "value": "0x0" }, { "to": "0x55d398326f99059fF775485246999027B3197955", "data": "0xa9059cbb00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a7640000", "value": "0x0" }, { "to": "0x39E66eE6b2ddaf4DEfDEd3038E0162180dbeF340", "data": "0x1281311d00000000000000000000000000000000000000000000000000000000000023270000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000005cf581ebb20ffaa2d0000000000000000000000000000000000000000000000055de6a779bbac0000", "value": "0x0" } ] }, "net_amount": 97.02, "fees": { "treasury": 0, "distributor": 0.99, "fee": 0.99, "frontend": 1 }, "approval": { "required": true, "currentAllowanceWei": "0", "requiredAllowanceWei": "99000000000000000000" } }
Examples with two wallet providers:
Notes:
  • Buy fee semantics: for action="buy", the request value is treated as the total amount charged from the user. The frontend fee is deducted first, and the trade is quoted/executed with the remaining amount. The response value equals the total charged (same as request value).
  • Sell fee semantics: for action="sell", the frontend fee is charged after the trade, and the response value reflects the amount after frontend fee deduction.
  • Clients should strongly prefer requiring atomic bundling (i.e. "atomicRequired": true) so the fee transfer is not executed without the trade.
  • Insufficient balance (buy only): for action="buy", the from_wallet balance is checked against the total spend (trade value + frontend fee). If it can't cover the total, the endpoint returns 400 with the token, current balance, and required amount (in wei).
Possible errors:
  • 400 Invalid request parameters; fee too high for the provided value; insufficient from_wallet balance (buy); plus any POST /markets/quote error
  • 401 Missing/invalid API key (no authenticated user)
  • 403 API key is not whitelisted for frontend fees
  • 404 Market or outcome not found
  • 500 Unable to resolve token decimals or unexpected server error

POST /markets/claim

Get a trade claim and transaction calldata for a specific market.
  • Method: POST
  • Path: /markets/claim
  • Body: JSON
Request body:
  • Exactly one of the following is required (send only one):
    • market_id (number): on-chain market id + network_id (number): network id
    • market_slug (string)
  • outcome_id (number, optional): on-chain outcome id (only required when market is voided)
Validation rules:
  • Market must exist and be resolved.
Response body:
  • action (string): claim_winnings | claim_voided, depending on whether the market is voided (both require the market to be resolved)
  • outcome_id: winning outcome id, or voided outcome id to be claimed
  • calldata (string): hex-encoded calldata for the contract
Example request (buy by value):
json
{ "market_id": 164, "outcome_id": 0, "network_id": 2741 }
Example success response:
json
{ "action": "claim_winnings", "outcome_id": 0, "calldata": "0x..." }
Possible errors:
  • 400 Invalid request parameters, unsupported network, market not resolved
  • 404 Market or outcome not found
  • 500 Unable to resolve token decimals or unexpected server error

Users

GET /users/:address/events

Paginated actions for a user across markets, ordered by timestamp desc.
Query params:
  • page, limit
  • trading_model: amm | ob | all (default amm) — pass ob to filter to Order Book actions only
  • market_id: chain market id (optional)
  • market_slug: market slug (optional; resolves to id/network for filtering)
  • network_id: number (optional)
  • token_address: comma-separated list of ERC20 token addresses (optional)
  • since: unix seconds (inclusive)
  • until: unix seconds (inclusive)
  • only_relevant: 1/true/yes to exclude wash-trade actions (optional)
Response items:
  • user: wallet address
  • action: action type
  • marketTitle, marketSlug, marketId, networkId
  • outcomeTitle, outcomeId
  • imageUrl
  • shares, value: numbers
  • timestamp: unix seconds
  • blockNumber: number
  • token: ERC20 token address
  • txId: transaction hash
Example:
plain text
GET /users/0x1234.../events?network_id=2741&market_id=144&page=1&limit=50

GET /users/:address/referrals

Paginated referrals attributed to a user across markets, ordered by timestamp desc.
Query params:
  • page, limit
  • market_id: chain market id (optional)
  • market_slug: market slug (optional; resolves to id/network)
  • network_id: number (optional)
  • since: unix seconds (optional, inclusive)
  • until: unix seconds (optional, inclusive)
  • code: referral code (optional)
Response items (camelCase):
  • user: wallet address (referrer or user recorded on referral)
  • action: buy | sell
  • marketTitle, marketSlug, marketId, networkId
  • outcomeTitle, outcomeId
  • imageUrl
  • value: number
  • timestamp: unix seconds
  • blockNumber: number
  • token: ERC20 token address
  • code: referral code used
  • fees:
    • lp (number)
    • treasury (number)
    • distributor (number)
Example:
plain text
GET /users/0x1234.../referrals?network_id=2741&market_id=144&page=1&limit=50

GET /users/:address/portfolio

Aggregated user positions per market/outcome/network, ordered by latest activity.
Query params:
  • page, limit
  • trading_model: amm | ob | all (default amm) — pass ob for Order Book positions or all for both
  • min_shares: minimum shares threshold per outcome and for liquidity positions (default 0.1)
  • market_slug: market unique slug (optional)
  • market_id: chain market id (optional)
  • network_id: number (optional)
  • token_address: comma-separated list of ERC20 token addresses (optional)
  • status: comma-separated subset of ongoing | lost | won | claimed | sold | voided, or all to include every position regardless of size (optional)
  • exclude_history: true to return only currently-actionable positions (open, or resolved+unclaimed) (optional)
  • keyword: full-text search across title, description, and outcome titles (optional)
  • sort: asc | desc (default desc)
  • sort_by: created_at (default) | profit | roi | shares | value | marketTitle | expires_at
  • group_by_event: true/1 to fold positions sharing an event into a single event row (optional)
Notes:
  • Positions with net shares < min_shares (default 0.1) are excluded (unless status is provided or status=all).
  • Pagination and totals are computed after filtering.
  • price is the current outcome price; shares is net buys minus sells; average buy price follows proportional cost-basis when selling.
Response items:
  • marketId, marketTitle, marketSlug, outcomeId, outcomeTitle, networkId, token, imageUrl
  • shares: net shares held (number)
  • price: average buy price (number)
  • value: shares * currentPrice
  • profit: shares * (currentPrice - price)
  • roi: (profit - totalAmount) / totalAmount (null if not computable)
  • totalProfit, totalRoi: realized + unrealized P/L over the lifetime of the position (null if not computable)
  • positionFees, totalFees: fees attributed to the open position / to all activity on it
  • winningsToClaim: true if resolved, holding winning outcome, and no claim_winnings
  • winningsClaimed: true if resolved, holding winning outcome, and claim_winnings exists
  • voidedWinningsToClaim, voidedWinningsClaimed: same, for voided markets
  • claimed: true if winnings or voided winnings were claimed
  • status: ongoing | lost | won | claimed | sold | voided
  • expiresAt: market expiry (ISO string or null)
  • eventId: parent event UUID, or null
  • executionMode: 0 (AMM) or 1 (Order Book); tokenId (ERC1155 id) is set for Order Book positions — see Order Book Concepts
Example:
plain text
GET /users/0x1234.../portfolio?network_id=2741&token_address=0x84A71ccD554Cc1b02749b35d22F684CC8ec987e1&page=1&limit=20

GET /users/:address/markets

Portfolio view aggregated by markets (grouped by market, ordered by latest user activity in the market). For Order Book positions, includes on-chain balances from the ConditionalTokens contract.
Query params:
  • page, limit (pagination; default limit=10, max 100)
  • trading_model: amm | ob | all (default amm) — pass ob for Order Book positions or all for both
  • min_shares: minimum shares threshold per outcome and for liquidity positions (default 0.1)
  • network_id: number (optional)
  • state: open | closed | resolved (optional)
  • token_address: ERC20 token address (optional)
  • topics: comma-separated list of topics (optional)
  • keyword: full-text search across title, description, and outcome titles
  • market_ids: comma-separated list of {networkId}:{marketId} pairs (optional), e.g. 2741:164,2741:200
  • market_slug: comma-separated market slug(s) (optional)
  • status: comma-separated subset of ongoing | lost | won | claimed | sold | voided, or all (optional)
  • sort: asc | desc (default desc); sort_by: created_at (default)
  • group_by_event: true/1 to fold sibling markets into a single event row (optional)
  • Buy fee filters (optional; decimals between 0 and 1):
    • buy_lp_fee_lt, buy_lp_fee_lte, buy_lp_fee_gt, buy_lp_fee_gte, buy_lp_fee_eq
Response items:
  • market: a market object (same shape as /markets items)
  • portfolio:
    • positions: array of outcome positions with:
      • marketId, marketTitle, marketSlug, imageUrl, networkId, token
      • outcomeId, outcomeTitle
      • shares, price, value, profit, roi, totalProfit, totalRoi, positionFees, totalFees
      • winningsToClaim, winningsClaimed, voidedWinningsToClaim, voidedWinningsClaimed, claimed
      • status: ongoing | lost | won | claimed | sold | voided
    • liquidity:
      • shares, price, value
      • totalAdded, totalRemoved, totalClaimed, totalToClaim
      • feesClaimed

Order Book Concepts

The Order Book replaces the AMM for order matching on Order Book markets — traders sign EIP-712 orders off-chain, the API validates and stores them, and an on-chain matcher settles fills through the MyriadCTFExchange contract. This section defines the shared concepts; the Orders, Positions, Events, and Real-time Updates sections document the endpoints.

Trading Model

By default, all endpoints return AMM-only data. To access Order Book data, you must explicitly pass the trading_model query parameter.
Value
Description
amm
AMM markets only (default when parameter is omitted)
ob
Order Book markets only
all
Both AMM and Order Book markets

Order Signing (EIP-712)

Orders are signed off-chain using the EIP-712 typed data standard. The signing domain is:
json
{ "name": "MyriadCTFExchange", "version": "1", "chainId": "<chain_id>", "verifyingContract": "<exchange_contract_address>" }
The Order struct:
plain text
Order( address trader, uint256 marketId, uint8 outcomeId, uint8 side, uint256 amount, uint256 price, uint256 minFillAmount, uint256 nonce, uint256 expiration )

Signature Types

Order and cancel requests may carry an optional signatureType alongside signature, declaring which signing scheme produced it. It is transport metadata only — it is not part of the signed EIP-712 struct and does not affect the order hash.
Value
Scheme
How the signature is verified
0
EOA (default)
ECDSA recovery (verifyTypedData), with an EIP-1271 fallback
12
Reserved
Not supported — rejected with 400
3
SCW (smart-contract wallet)
EIP-1271 (isValidSignature) directly against the trader contract — no ECDSA recovery
signatureType is optional and defaults to 0, so existing EOA callers can omit it. Only 0 and 3 are accepted; 12 are reserved for proxy-wallet schemes the API does not implement, and any other value is likewise rejected with 400. For signatureType=3 the trader must be a deployed contract implementing EIP-1271: the API calls its isValidSignature on-chain and accepts the order only if it returns the ERC-1271 magic value (a trader with no contract code is rejected).
Smart-contract wallets can also sign in: POST /auth/login verifies the SIWE signature via EIP-1271 when standard EOA recovery fails (see Authentication).

Price and Amount Scale

  • Price: integer in [1, 1e18] representing a fraction of 1 collateral token per share. 0.50 = 500000000000000000.
  • Amount: integer in the token's smallest unit (e.g. for 18-decimal tokens, 1e18 = 1 share).

Price Tick Size

New orders must be priced on a 0.01 (1-cent) tick grid: price must be a whole number of cents, i.e. a multiple of the tick size 1e16 (10000000000000000). Equivalently, price % 1e16 == 0.
  • ✅ Valid: 10000000000000000 (0.01), 570000000000000000 (0.57), 990000000000000000 (0.99), 1000000000000000000 (1.00).
  • ❌ Rejected: 575000000000000000 (0.575 — finer than a cent), 569999999999999936 (a floating-point artifact for 0.57), 1 (1 wei).
An off-tick price is rejected with 400 and a details[] entry whose path is ["order","price"] (or ["orders",i,"order","price"] / ["place",i,"order","price"] in the batch endpoints).

Sides

Value
Meaning
0
Buy — buying outcome shares
1
Sell — selling outcome shares

Outcomes

Value
Meaning
0
Yes
1
No

Time-in-Force

TIF
Behaviour
GTC
Good-til-cancelled. Remains on the book until filled, cancelled, or the market closes. expiration must be 0.
GTD
Good-til-date. Expires at the unix timestamp in expiration. expiration must be non-zero.
FOK
Fill-or-kill. Must be fully filled in a single matcher run or it is cancelled.
FAK
Fill-and-kill. Partial fill is allowed; the unfilled remainder is cancelled after the matcher run.
PO
Post-only. Never fills as a taker — if the order would cross an older counterparty, the matcher cancels the entire order (no partial rest). expiration=0 rests indefinitely (GTC-style); expiration>0 rests until the timestamp (GTD-style).

Order Priority

Makers are filled in price-time priority: best price first, and on price ties the oldest createdAt fills first (strict FIFO).

Match Types

The on-chain matcher supports three settlement modes:
Type
Description
Direct
A BUY order is matched against a SELL order on the same outcome. Bid price >= ask price.
Mint
Two BUY orders on opposite outcomes (YES + NO) whose prices sum to 1. New shares are minted from collateral.
Merge
Two SELL orders on opposite outcomes whose prices sum to 1. Shares are burned and collateral is returned.
Cross-market
(NegRisk only) N BUY-YES orders across all outcomes of an event whose prices sum to 1.

Order Lifecycle

plain text
Trader API Matcher (on-chain) | | | | POST /orders (signed) | | | --------------------------| | | | validate, verify sig, | | | check balance/allowance | | | insert into orders table | | ◄─ { orderHash, status } | | | | NOTIFY orders_changed | | | ────────────────────────────────► | | | | load open orders | | | find matches | | | call exchange contract | | | (matchMultipleOrdersWithFees | | | or matchCrossMarketOrders) | | ◄──── OrdersMatched event ────── | | | update orders + actions | | | | | GET /orders/:hash | | | --------------------------| | | ◄─ { status: "filled" } | |
  1. Place order — trader signs an EIP-712 order and sends it to POST /orders.
  1. Validation — the API verifies the signature, checks on-chain balance/allowance, and stores the order.
  1. Matching — the matcher service loads open orders, finds compatible pairs/sets, and calls the exchange contract.
  1. Settlement — the exchange contract atomically transfers shares and collateral on-chain.
  1. Sync — the API listens for OrdersMatched / OrderCancelled events and updates order statuses.

Orders

POST /orders

Place a new order. The order is validated, the trader's signature is verified, on-chain balance/allowance is checked, and the order is persisted and pushed to the matcher.
Request body:
json
{ "order": { "trader": "0x1234...abcd", "marketId": "42", "outcomeId": 0, "side": 0, "amount": "1000000000000000000", "price": "500000000000000000", "minFillAmount": "0", "nonce": "1", "expiration": "0" }, "signature": "0x<130 hex chars>", "signatureType": 0, "network_id": 56, "time_in_force": "GTC", "accept_by": "0", "client_order_id": "17256719159" }
Field
Type
Required
Description
order.trader
address
yes
The signer's wallet address (40 hex chars, 0x-prefixed)
order.marketId
uint string
yes
On-chain market ID
order.outcomeId
0 or 1
yes
Outcome to trade
order.side
0 or 1
yes
0 = buy, 1 = sell
order.amount
uint string
yes
Maximum number of shares (in wei). Must be > 0
order.price
uint string
yes
Price per share in [1, 1e18]. Must be on the 0.01 tick grid — a multiple of 1e16 (see Price Tick Size)
order.minFillAmount
uint string
no
Minimum fill size (default "0")
order.nonce
uint string
yes
Unique nonce for the order
order.expiration
uint string
yes
Unix timestamp for GTD; "0" for GTC/FOK/FAK. PO accepts any value.
signature
hex string
yes
EIP-712 signature (0x + 130 hex chars = 65 bytes for EOA; SCW signatures may differ in length)
signatureType
number
no
Signing scheme: 0=EOA (default) or 3=SCW (EIP-1271); other values rejected. See Signature Types.
network_id
number
no
Network ID (defaults to server config)
time_in_force
string
no
GTC (default), GTD, FOK, FAK, PO
replaces_order_hash
hex string
no
Hash of an open order this one replaces. Excluded from the reserved-funds check (see below) so a place-then-cancel replacement isn't rejected; the client is expected to cancel it right after.
accept_by
uint string
no
API receive window: Unix-milliseconds timestamp by which this submission must reach the API to be accepted onto the book. If the request arrives after accept_by, it is rejected with 400 and the order is never placed. "0" (default) = no window. Values are interpreted as milliseconds; a seconds-scale value (below 1e12) is auto-detected and scaled up, so either unit works. This is an ingestion-time guard only (similar to Binance recvWindow) — it protects against acting on a stale request, not the order's lifetime once resting. To bound how long a resting order stays matchable, sign a GTD expiration instead. Not part of the signed order and not persisted.
client_order_id
string
no
Client-supplied order identifier (1-64 chars: letters, digits, -, _). The server namespaces it by prepending the authenticated user's id first 4 chars + - (e.g. you send 17256719159, it is stored as 1ccd-17256719159). Globally unique; requires an API key (x-api-key). Use it to cancel via DELETE /orders/:orderHash.
Validation rules:
  • price must be on the 0.01 tick grid (a multiple of 1e16); an off-tick price is rejected with 400 (see Price Tick Size).
  • GTC orders must have expiration = 0.
  • GTD orders must have expiration > 0.
  • PO orders accept any expiration0 behaves GTC-style, >0 behaves GTD-style.
  • accept_by, when non-zero, must not already be in the past — a submission whose receive window has already passed is rejected with 400 (the order is not placed).
  • For buy orders (non-PO/FOK/FAK): the trader must have sufficient collateral balance and allowance on the exchange contract for notional + fee (where notional = amount * price / 1e18). For NegRisk markets the check is performed against the underlying collateral token (the network's configured collateral), not a wrapped collateral — so traders only need to approve the underlying token.
  • For sell orders (non-PO/FOK/FAK): the trader must hold enough outcome shares in the ConditionalTokens contract and have approved the exchange via setApprovalForAll.
  • Reserved funds: the balance check subtracts obligations already parked in the trader's open orders on the same market (buys reserve (amount - filled) * price / 1e18 of collateral; sells reserve unfilled shares per outcome). A new order must fit within balance - reserved. To replace an existing order without cancelling it first, pass its hash as replaces_order_hash so it is left out of the reservation.
  • FOK, FAK, and PO skip the upfront collateral check — the on-chain matchOrders* call enforces allowance/balance at fill time.
Success response (200):
json
{ "orderHash": "0x...", "status": "open", "timeInForce": "GTC", "clientOrderId": "1ccd-17256719159" }
clientOrderId is only present when a client_order_id was supplied.
Error responses:
Status
Condition
400
Invalid payload, off-tick price (not a multiple of 1e16), market not open, insufficient balance/allowance, invalid signature, client_order_id supplied without an API key
404
Market not found
409
Order already exists (duplicate hash, or duplicate client_order_id)
429
Per-trader rate limit exceeded (200 orders / 10 seconds)
500
Server error or RPC failure

GET /orders

List orders with optional filters.
Query parameters:
Param
Type
Description
trader
address
Deprecated. When authenticated with a connected wallet, results are already scoped to your own orders and a differing trader is rejected with 403 TRADER_FORBIDDEN
network_id
number
Filter by network
market_id
number
Filter by on-chain market ID
status
string
open, filled, cancelled, expired
time_in_force
string
GTC, GTD, FOK, FAK, PO. Comma-separated for multiple (e.g. GTC,GTD)
keyword
string
Case-insensitive substring match against the order's market title or its outcome title
sort
string
Sort field: created_at (default), total, filled, market, resolving_soonest (see below)
order
string
asc or desc (default desc)
page
number
Page number (default 1)
limit
number
1-5000 (default 5000)
offset
number
Legacy. Raw row offset. When supplied it takes precedence over page, and page in the response is derived from it. Prefer page.
Sort fields:
Value
Sorts by
created_at
Order creation time (default)
total
Order notional (price × amount)
filled
Fill ratio (filledAmount / amount)
market
Market title (alphabetical)
resolving_soonest
Market expiry date (markets without one sort last)
Ties on the primary sort key are broken by orderHash ascending, so pagination is deterministic. An invalid sort value returns 400.
Response (200):
json
{ "data": [ { "orderHash": "0x...", "clientOrderId": "1ccd-17256719159", "order": { "trader": "0x...", "marketId": 42, "outcomeId": 0, "side": 0, "amount": "1000000000000000000", "price": "500000000000000000", "minFillAmount": "0", "nonce": "1", "expiration": "0" }, "status": "open", "signatureType": 0, "filledAmount": "0", "timeInForce": "GTC", "createdAt": "2025-07-01T12:00:00.000Z" } ], "pagination": { "page": 1, "limit": 5000, "total": 137, "totalPages": 1, "hasNext": false, "hasPrev": false } }
The data array items are unchanged; the pagination object is returned alongside them. With the default limit=5000 a trader's full position set is typically returned in a single page.

GET /orders/:orderHash

Get a single order by its hash.
Response (200):
json
{ "orderHash": "0x...", "clientOrderId": "1ccd-17256719159", "order": { "trader": "0x...", "marketId": 42, "outcomeId": 0, "side": 0, "amount": "1000000000000000000", "price": "500000000000000000", "minFillAmount": "0", "nonce": "1", "expiration": "0" }, "status": "open", "signatureType": 0, "filledAmount": "0", "timeInForce": "GTC", "networkId": 56, "createdAt": "2025-07-01T12:00:00.000Z", "updatedAt": "2025-07-01T12:00:00.000Z", "cancelledAt": null, "filledAt": null }
clientOrderId is null for orders placed without a client_order_id.
Errors: 404 if order not found.

DELETE /orders/:orderHash

Cancel an open order. Requires the original order + signature in the request body for ownership verification.
The path segment accepts either:
  • an order hash0x + 64 hex chars; or
  • a raw client_order_id — the value you passed to POST /orders (e.g. 17256719159), without the user prefix. The server reconstructs the namespaced id from the authenticated user, so cancelling by client_order_id requires an API key (x-api-key). The signed order in the body must still match the stored order.
The path type is auto-detected: anything matching 0x + 64 hex is treated as a hash; everything else as a client_order_id. The orderHash in the response is always the resolved order hash.
Request body:
json
{ "order": { "trader": "0x...", "marketId": "42", "outcomeId": 0, "side": 0, "amount": "1000000000000000000", "price": "500000000000000000", "minFillAmount": "0", "nonce": "1", "expiration": "0" }, "signature": "0x<130 hex chars>", "signatureType": 0, "network_id": 56 }
signatureType is optional (default 0); set it to 3 to cancel with a smart-contract-wallet signature. See Signature Types.
Success response (200):
json
{ "orderHash": "0x...", "status": "cancelled" }
Error responses:
Status
Condition
400
Missing body, invalid payload, hash mismatch, invalid signature, order already filled/expired, client_order_id cancellation without an API key
404
Order not found

POST /orders/batch

Place up to 200 signed orders in a single atomic request. Each entry is validated independently (signature, market lookup, duplicate check) and the valid entries are inserted via a single bulk SQL statement so the matcher is notified only once.
The on-chain collateral check is skipped for bulk placement — the exchange contract enforces allowance/balance at match time.
Request body:
json
{ "orders": [ { "order": { "trader": "0x...", "marketId": "42", "outcomeId": 0, "side": 0, "amount": "1000000000000000000", "price": "500000000000000000", "minFillAmount": "0", "nonce": "1", "expiration": "0" }, "signature": "0x<130 hex chars>", "time_in_force": "GTC" } ], "network_id": 56, "allow_partial": true }
Field
Type
Required
Description
orders
array
yes
1–200 {order, signature, signatureType?, time_in_force?, accept_by?} entries
orders[].signatureType
number
no
Per-entry signing scheme (default 0). See Signature Types.
orders[].time_in_force
string
no
Per-entry TIF (GTC, GTD, FOK, FAK, PO)
orders[].accept_by
uint string
no
Per-entry API receive window (see POST /orders). "0" (default) = no window.
network_id
number
no
Network ID (defaults to server config)
allow_partial
boolean
no
true (default) accepts valid entries and reports per-entry errors; false rejects the whole batch with 400 if any entry fails
Success response (200):
json
{ "placed": ["0xabc...", "0xdef..."], "errors": [ { "index": 3, "orderHash": null, "reason": "Invalid order signature" } ] }
Error responses:
Status
Condition
400
Invalid payload (including any off-tick price — rejects the whole batch regardless of allow_partial), missing CLOB config, or allow_partial=false and any entry failed
429
Rate limit exceeded (per-trader budget is 200 orders / 10s)
500
Server error

POST /orders/batch-modify

Cancel existing orders and place new orders in a single atomic request. The endpoint runs the cancel phase before the place phase (same DB round-trip order as the matcher's view), so the matcher can never observe both the old and the new orders simultaneously.
Request body:
json
{ "cancel": [ { "order": { /* ... */ }, "signature": "0x..." } ], "place": [ { "order": { /* ... */ }, "signature": "0x...", "time_in_force": "GTC" } ], "network_id": 56, "allow_partial": true }
Field
Type
Required
Description
place
array
no
Up to 200 orders to place ({order, signature, signatureType?, time_in_force?, accept_by?} — same entry shape as POST /orders/batch)
cancel
array
no
Up to 200 orders to cancel ({order, signature, signatureType?} — no TIF)
network_id
number
no
Network ID (defaults to server config)
allow_partial
boolean
no
true (default) executes the valid subset; false rejects the entire call if any entry fails
At least one of place or cancel must be non-empty.
Success response (200):
json
{ "placed": ["0xabc..."], "cancelled": ["0xdef..."], "errors": [ { "phase": "place", "index": 2, "orderHash": null, "reason": "Market not found" } ] }
Error responses:
Status
Condition
400
Invalid payload (including any off-tick place price — rejects the whole request regardless of allow_partial; cancel entries are exempt), missing CLOB config, or allow_partial=false and any entry failed
429
Rate limit exceeded
500
Server error

POST /orders/cancel-batch

Cancel multiple orders in a single request. Each order requires its original order data and signature for ownership verification.
Request body:
json
{ "orders": [ { "order": { "trader": "0x...", "marketId": "42", "outcomeId": 0, "side": 0, "amount": "1000000000000000000", "price": "500000000000000000", "minFillAmount": "0", "nonce": "1", "expiration": "0" }, "signature": "0x<130 hex chars>" } ], "network_id": 56, "allow_partial": true }
Field
Type
Required
Description
orders
array
yes
Array of {order, signature, signatureType?} objects (1-200 items)
orders[].order
object
yes
Full order struct (same as POST /orders)
orders[].signature
hex string
yes
EIP-712 signature for this order
orders[].signatureType
number
no
Signing scheme (default 0). See Signature Types.
network_id
number
no
Network ID (defaults to server config)
allow_partial
boolean
no
true (default) cancels valid entries and reports per-entry errors; false rejects the whole batch with 400 if any entry fails
Success response (200):
json
{ "cancelled": ["0xabc...", "0xdef..."], "errors": [ { "index": 2, "orderHash": "0x123...", "reason": "Order not found" } ] }
Error responses:
Status
Condition
400
Invalid payload, missing CLOB config, or allow_partial=false and any entry failed
500
Server error

POST /orders/cancel-all

Cancel all open orders for a trader, optionally filtered by market. Requires an EIP-712 CancelAll signature to prove wallet ownership.
EIP-712 CancelAll struct:
plain text
CancelAll( address trader, uint256 marketId, uint256 timestamp )
Set marketId to 0 to cancel across all markets. The signing domain is the same as for Order (see Order Signing).
Request body:
json
{ "trader": "0x1234...abcd", "market_id": 42, "timestamp": "1719835200", "signature": "0x<130 hex chars>", "signatureType": 0, "network_id": 56 }
Field
Type
Required
Description
trader
address
yes
Trader wallet address
market_id
number
no
On-chain market ID. Omit to cancel across all markets
timestamp
uint string
yes
Current unix timestamp (must be within 5 minutes of server time)
signature
hex string
yes
EIP-712 CancelAll signature
signatureType
number
no
Signing scheme (default 0); 3 for a smart-contract wallet. See Signature Types.
network_id
number
no
Network ID (defaults to server config)
Success response (200):
json
{ "cancelled_count": 12, "market_ids_affected": ["uuid-...", "uuid-..."] }
Error responses:
Status
Condition
400
Invalid payload, bad signature, timestamp too old, missing CLOB config
500
Server error

Positions

Position endpoints return transaction calldata ({ to, calldata, value }) that the client signs and submits on-chain. All amounts are in the token's smallest unit (uint string, e.g. "1000000000000000000" for 1 token with 18 decimals).

POST /positions/split

Split collateral into YES + NO outcome shares.
Request body:
json
{ "market_id": 42, "amount": "1000000000000000000", "network_id": 56 }
Field
Type
Required
Description
market_id
number
yes
On-chain market ID
amount
uint string
yes
Collateral amount to split
network_id
number
no
Network ID
Response (200):
json
{ "to": "0x<ConditionalTokens address>", "calldata": "0x...", "value": "0" }

POST /positions/merge

Merge YES + NO outcome shares back into collateral.
Request body: Same as /positions/split.

POST /positions/redeem

Redeem winning outcome shares for collateral after market resolution.
Request body:
json
{ "market_id": 42, "network_id": 56 }
Field
Type
Required
Description
market_id
number
yes
On-chain market ID
network_id
number
no
Network ID

POST /positions/redeem-voided

Redeem shares from a voided market at the market's voided payout ratios.
Request body: Same as /positions/redeem.

POST /positions/neg-risk/split

Split collateral into YES + NO shares for a specific outcome within a NegRisk event. The underlying collateral is wrapped into WCOL by the NegRiskAdapter.
Request body:
json
{ "event_id": "0x<64 hex chars>", "outcome_index": 0, "amount": "1000000000000000000", "network_id": 56 }
Field
Type
Required
Description
event_id
bytes32 hex
yes
NegRisk event ID
outcome_index
number
yes
Index of the outcome within the event
amount
uint string
yes
Underlying collateral amount
network_id
number
no
Network ID

POST /positions/neg-risk/merge

Merge YES + NO shares for a NegRisk outcome back into underlying collateral.
Request body: Same as /positions/neg-risk/split.

Events

Events group multiple binary Order Book markets under a single parent (e.g. "Who will win the election?" with outcomes A, B, C, ...). Each sibling market is its own binary market with Yes / No outcomes. The negRisk flag determines the resolution semantics:
  • NegRisk events (negRisk: true) — sibling markets are mutually exclusive: exactly one resolves to Yes and all others resolve to No (e.g. "Who will win the election?").
  • Non-NegRisk events (negRisk: false) — sibling markets resolve independently, so zero, one, or multiple siblings can resolve to Yes (e.g. "Which teams will make the playoffs?").

GET /events

List all published events with nested sibling markets and aggregated metrics.
Query parameters:
Param
Type
Description
network_id
number
Filter by network (optional)
state
string
Filter by state (optional)
Response (200):
Each item is an event row with:
  • type: "event" — discriminator (matches the shape produced by grouped endpoints elsewhere).
  • negRisk / negRiskId — replaces the previous ethEventId field. negRiskId is the on-chain NegRisk event id (bytes32 hex).
  • Aggregated trading metrics (volume, volume24h, volumeNotional, volumeNotional24h, liquidity, users, featured, featuredAt) summed/aggregated across the event's sibling markets.
  • markets — array of fully-serialized sibling markets (same shape as GET /markets items, including their own outcomes and externalSources), ordered by outcomeIndex.
  • scoreboard — for sports events, the live-scores scoreboard (same object documented for GET /markets items); omitted for non-sports events. Sibling markets are in canonical outcome order, so align their rows positionally with the scoreboard's outcome0/outcome1.
  • externalSources — event-level external sources.
json
{ "data": [ { "type": "event", "id": "uuid-...", "networkId": 56, "slug": "2028-election", "title": "Who will win the 2028 election?", "description": "...", "imageUrl": "https://...", "bannerImageUrl": "https://...", "state": "open", "resolvedOutcomeIndex": null, "expiresAt": "2028-11-05T00:00:00.000Z", "publishedAt": "2025-06-01T00:00:00.000Z", "negRisk": true, "negRiskId": "0x...", "volume": 123456.78, "volume24h": 1234.56, "volumeNotional": 234567.89, "volumeNotional24h": 2345.67, "liquidity": 9876.54, "users": 1234, "featured": false, "featuredAt": null, "externalSources": [], "markets": [ { "id": 42, "title": "Candidate A", "outcomeIndex": 0, "state": "open", "outcomes": [ /* ... */ ], "externalSources": [ /* ... */ ] /* ...full market shape... */ } ] } ] }
top-level ethEventId, rules, createdAt, or the minimal outcomes array ({ marketId, ethMarketId, title, outcomeIndex, state, tokenId }). Use negRiskId instead of ethEventId, and read sibling market data — including tokenId — from markets[].outcomes[].

GET /events/:id

Get a single published event by UUID or slug. Returns 404 if the event is unpublished or not found.
Response (200): A single event row with the exact same shape as one item in GET /events (the response is the event object directly, not wrapped in data).

GET /events/:id/orderbook

Combined orderbook across all outcome markets in a published NegRisk event. Returns the orderbook per outcome market.
Response (200):
json
{ "outcomes": [ { "marketId": "uuid-...", "ethMarketId": 42, "outcomeIndex": 0, "title": "Candidate A", "tokenId": "84", "orderbook": { "bids": [["500000000000000000", "1000000000000000000"]], "asks": [["520000000000000000", "2000000000000000000"]] } }, { "marketId": "uuid-...", "ethMarketId": 43, "outcomeIndex": 1, "title": "Candidate B", "tokenId": "86", "orderbook": { "bids": [], "asks": [] } } ] }
tokenId is the ERC1155 token id for the outcome ((ethMarketId << 1) | outcomeIndex).

GET /events/:id/actions

Paginated trade actions across all outcome markets in an event, ordered by timestamp descending. Accepts an event UUID or slug.
Query parameters:
Param
Type
Description
trading_model
amm, ob, all
Filter outcome markets by trading model (default amm)
since
unix seconds
Only include actions at or after this timestamp
until
unix seconds
Only include actions at or before this timestamp
only_relevant
1/true/yes
Exclude wash-trade actions (where relevant = false)
page
number
Page number (default 1)
limit
number
1-100 (default 20)
Response (200):
json
{ "data": [ { "user": "0x...", "action": "buy", "marketTitle": "Candidate A", "marketSlug": "candidate-a", "marketId": 42, "networkId": 56, "outcomeTitle": "Yes", "outcomeId": 0, "imageUrl": "https://...", "shares": 1.5, "value": 0.75, "timestamp": 1719835200, "blockNumber": 12345678, "token": "0x<collateral address>", "txId": "0x..." } ], "pagination": { "page": 1, "limit": 20, "total": 137, "totalPages": 7, "hasNext": true, "hasPrev": false } }
The action field is normalized: split is reported as buy and merge as sell. Maker-side actions are excluded (only role = 'taker' or null are returned).
Errors: 404 if the event is not found.

Real-time Updates (WebSockets)

Live order book, trade, order, position, price, and market updates are delivered over WebSocket. The realtime layer is powered by Centrifugo, which runs a small framing protocol on top of WebSocket (it is not plain "one JSON object per message").
You're not tied to JavaScript. The examples below happen to use JS, but Centrifugo maintains client SDKs for many stacks — JavaScript/TypeScript, Go, Python, Dart/Flutter, Swift, and Java/Android among them (see the full client SDK list). An SDK just handles the framing, reconnects, history recovery, and ping/pong for you.
No SDK is strictly required either: the protocol is a thin framing on top of WebSocket, so any language with a WebSocket client can implement it natively — the raw WebSocket example below does exactly that (in Node's ws, but the same handshake applies anywhere).

Connection

Environment
URL
Staging
wss://ws.staging.myriadprotocol.com/ws
Production
wss://ws.myriadprotocol.com/ws
Public channels (orderbook:*, trades:*, settlements:*, prices:*, markets:*) are open — connect anonymously, no token or API key required. The per-trader orders:* / positions:* channels require a short-lived subscription token issued for your connected wallet — see WebSocket subscription tokens.

Using the client library (recommended)

javascript
import { Centrifuge } from 'centrifuge'; const client = new Centrifuge('wss://ws.myriadprotocol.com/ws'); const sub = client.newSubscription('orderbook:56:42'); sub.on('publication', (ctx) => console.log(ctx.data)); sub.subscribe(); client.connect();

Raw WebSocket (no client library)

If you'd rather use the native ws library, you implement the Centrifugo bidirectional JSON protocol yourself. It's small:
  1. Connect — after the socket opens, send a connect command. Anonymous connections send empty params: { "connect": {}, "id": 1 }.
  1. Subscribe — once the connect reply arrives, send one subscribe command per channel: { "subscribe": { "channel": "…" }, "id": N }. Each command needs a unique incrementing id.
  1. Ping/pong — the server periodically sends an empty frame ({}); reply with an empty frame ({}) to keep the connection alive.
  1. Publications — channel data arrives as an async push with no id: { "push": { "channel": "…", "pub": { "data": { … } } } }. The payload you care about is push.pub.data.
A single WebSocket frame may pack several commands/replies joined by newlines (\n), so split on \n before parsing.
javascript
import WebSocket from 'ws'; const ws = new WebSocket('wss://ws.myriadprotocol.com/ws'); const channels = ['orderbook:56:42', 'trades:56:42']; let nextId = 1; const send = (cmd) => ws.send(JSON.stringify(cmd)); ws.on('open', () => send({ connect: {}, id: nextId++ })); // 1. authenticate (anonymous) ws.on('message', (frame) => { for (const line of frame.toString().split('\n')) { if (!line) continue; const msg = JSON.parse(line); if (Object.keys(msg).length === 0) { send({}); continue; } // 3. ping → pong if (msg.connect) { // 2. connect reply → subscribe for (const channel of channels) send({ subscribe: { channel }, id: nextId++ }); continue; } if (msg.push?.pub) { // 4. channel data console.log(msg.push.channel, msg.push.pub.data); } } });

Channels

Subscribe to one or more of the channels below. The segment after the namespace is always {networkId} (the numeric chain id), followed by either the on-chain {marketId} or the {trader} wallet address.
Channel
Pattern
Payload
Description
Order book
orderbook:{networkId}:{marketId}
Order book delta
Incremental price-level changes for a market (both outcomes)
Trades
trades:{networkId}:{marketId}
Trade
One event per on-chain match transaction
Trades (per trader)
trades:{networkId}:{marketId}:{trader}
Trade
Trades involving one trader; marketId or trader may be * (not both)
Settlements
settlements:{networkId}:{marketId}
Settlement
Match broadcast on-chain, pre-confirmation (optimistic)
Settlements (per trader)
settlements:{networkId}:{marketId}:{trader}
Settlement
One trader's settlements; marketId or trader may be * (not both)
Prices
prices:{networkId}:{marketId}
Price update
Best bid/ask (and last) per outcome
Orders
orders:{networkId}:{trader}
Order update
Lifecycle updates for a trader's orders
Positions
positions:{networkId}:{trader}
Position update
Share-balance changes for a trader
Markets
markets:{networkId}
Market event
Market created / resolved on a network
History & recovery. Channels retain a short history buffer. Subscribe with positioned: true, recoverable: true to receive a streamPosition on subscribed and have the client automatically replay missed publications after a reconnect:
javascript
client.newSubscription('trades:56:42', { positioned: true, recoverable: true });

Payload conventions

  • Each Centrifugo publication carries the JSON object documented below as its data.
  • All numeric fields are decimal strings. Prices are 1e18-scaled (see Price and Amount Scale); amounts/shares and fees are in the token's smallest unit.
  • ts is a unix timestamp in milliseconds.
  • Server-side, publishes are coalesced into ~100 ms windows, so a single message may batch several changes.

Order book delta

Incremental changes to aggregated price levels. amount is the new total remaining at that level; "0" means the level was removed. Each message may include changes for both outcomes (disambiguated by outcome), mirroring the aggregation rules of GET /markets/:id/orderbook.
json
{ "marketId": 42, "networkId": 56, "ts": 1719835200123, "changes": [ { "outcome": 0, "side": "bid", "price": "500000000000000000", "amount": "3000000000000000000" }, { "outcome": 0, "side": "ask", "price": "510000000000000000", "amount": "0" } ] }

Trade

One event per on-chain match transaction. A single taker can fill against multiple makers in one transaction (matchMultipleOrdersWithFees), so the maker legs are collapsed into a makers[] array and the taker side is reported as a volume-weighted taker aggregate.
json
{ "marketId": 42, "networkId": 56, "txHash": "0x...", "blockNumber": 12345678, "ts": 1719835200123, "taker": { "trader": "0x...", "outcome": 0, "side": "buy", "orderHash": "0x...", "totalAmount": "1000000000000000000", "averagePrice": "500000000000000000", "averagePriceAfterFees": "510000000000000000", "totalFees": { "total": "10000000000000000", "lp": "0", "treasury": "5000000000000000", "distributor": "5000000000000000" } }, "makers": [ { "trader": "0x...", "role": "maker", "outcome": 0, "side": "sell", "orderHash": "0x...", "price": "500000000000000000", "priceAfterFees": "500000000000000000", "amount": "1000000000000000000", "fees": { "total": "0", "lp": "0", "treasury": "0", "distributor": "0" } } ] }
side may be buy, sell, split, or merge (mint/merge settlements surface as split/merge).

Settlement

Fired the instant the matcher broadcasts the settlement transaction, before it is confirmed on-chain — the optimistic counterpart to Trade, which only lands once the block is mined. Market-makers can act on a fill as soon as it's irreversibly committed rather than waiting out the block time.
Unlike trades:, settlements carry no fees or prices (those are only known once the on-chain OrdersMatched logs are read) — just the participating orders and their fill amounts in a flat legs[] array (one taker leg + N maker legs for a direct match; all-taker legs for a cross-market neg-risk fill). Reconcile against the matching Trade on the same txHash once it arrives.
status is always "submitted" today; it's reserved so a confirmed/failed lifecycle can be added later without breaking subscribers. matchType is "direct" (one taker vs N makers in a single market) or "cross_market" (a neg-risk N-way fill, published once per touched market).
json
{ "networkId": 56, "marketId": 42, "txHash": "0x...", "matchType": "direct", "status": "submitted", "ts": 1719835200123, "legs": [ { "trader": "0x...", "role": "taker", "orderHash": "0x...", "outcome": 0, "side": "buy", "fillAmount": "1000000000000000000" }, { "trader": "0x...", "role": "maker", "orderHash": "0x...", "outcome": 0, "side": "sell", "fillAmount": "1000000000000000000" } ] }
side reports each order's own side (buy / sell); the split/merge framing and realised fees surface on the confirmed Trade event.

Price update

Best bid/ask per outcome (including binary-implied values derived from the opposite outcome). Fields are omitted when there is no resting liquidity on that side.
json
{ "networkId": 56, "marketId": 42, "ts": 1719835200123, "outcomes": [ { "outcome": 0, "bestBid": "500000000000000000", "bestAsk": "510000000000000000", "last": "505000000000000000" }, { "outcome": 1, "bestBid": "490000000000000000", "bestAsk": "500000000000000000" } ] }

Order update

Lifecycle transitions for an order, published to the owning trader's channel.
json
{ "type": "PARTIALLY_FILLED", "orderHash": "0x...", "networkId": 56, "marketId": 42, "trader": "0x...", "side": "buy", "outcome": 0, "price": "500000000000000000", "amount": "1000000000000000000", "filledAmount": "400000000000000000", "status": "open", "timeInForce": "GTC", "matchTxHash": "0x...", "createdAt": "2025-07-01T12:00:00.000Z", "updatedAt": "2025-07-01T12:00:05.000Z" }
Field
Description
type
PLACED, PARTIALLY_FILLED, FILLED, CANCELLED, or EXPIRED
status
Current persisted status: open, filled, cancelled, or expired
matchTxHash
Settlement tx hash (present on fill events only)

Position update

Share-balance changes for a trader, emitted per fill leg as well as for redeem/split/merge.
json
{ "networkId": 56, "marketId": 42, "trader": "0x...", "outcome": 0, "delta": "+1000000000000000000", "balance": "3000000000000000000", "averageEntryPrice": "500000000000000000", "reason": "fill", "txHash": "0x...", "ts": 1719835200123 }
Field
Description
delta
Signed share change from this event ("+…" gained, "-…" lost)
balance
New on-chain balance after the event (optional; refetch via REST if absent)
reason
fill, redeem, split, or merge

Market event

json
{ "type": "resolved", "networkId": 56, "marketId": 42 }
type is created or resolved; additional event-specific fields may be present.

Tags

GET /tags

Aggregates tag occurrences across published, open markets, returning one row per distinct tag slug with a marketCount scoped to currently-tradeable markets. Useful for filter chips on a markets listing page.
Tags are denormalized onto markets (synced from the offchain CMS); the matching tags/exclude_tags filters on GET /markets accept the same slugs.
Query params (optional):
  • type: filter to a single tag namespace (e.g. type=league)
Example:
plain text
GET /tags?type=league
Response:
json
{ "data": [ { "marketCount": 18, "tag": { "type": "league", "title": "Premier League", "slug": "premier-league", "imageUrl": "https://..." } } ] }
Sorted by marketCount descending, then tag title ascending.

Topics

GET /topics

Per-topic market counts, broken down by markets.topics. Intended for clients such as sidebar category counters that need counts per topic.
Counts are always restricted to published markets that are currently open (state = open and expires_at in the future) with at least 1 hour between publication and expiry. Sibling markets under the same event always collapse into a single tally — an event is counted once even if several of its siblings carry the topic.
Query params (all optional):
  • network_id: comma-separated list of network ids
Example:
plain text
GET /topics?network_id=2741,59144
Response:
json
{ "data": [ { "topic": "crypto", "marketCount": 42 }, { "topic": "sports", "marketCount": 31 } ] }
Topics are returned sorted by marketCount descending, then topic ascending.

Price Data

  • Historical price data is built from on-chain events (MarketOutcomeShares) and stored in prices.
  • Outcome prices are derived from outcome shares.
  • Liquidity price computation follows the contract logic; resolved markets use final shares/liquidity, otherwise #outcomes / (liquidity * Σ(1/shares)).

Errors

Common errors:
  • 401 Unauthorized – missing/invalid API key
  • 429 Too Many Requests – rate limit exceeded
  • 400 Bad Request – invalid query parameters
  • 404 Not Found – resource not found
  • 409 Conflict – duplicate order (Order Book: duplicate hash or client_order_id)
  • 500 Internal Server Error

Networks

The Myriad Protocol’s main deployment is on BNB Smart Chain, where most markets will be denominated in USD1 and some markets are still denominated in USDT.
Some markets are also available on other EVM-compatible blockchains.

BNB Smart Chain

Order Book Contracts
Mainnet
Testnet
OBExchange
0xa0b6f8ef8EdB64f395018D1933f2273Ce9f0f16A
OBConditionalTokens
0x6413734f92248D4B29ae35883290BD93212654Dc
OBManager
0xaB5591E280fF9Bf368DB60c3b775b5C7Ba5ea3dB
OBFeeModule
0xc1BB36bb0BA236603b95544E809F2ab1893BBC0C
OBNegRiskAdapter
0xd96F26703Ddbf7d1Cb6858640eca34cF1893d53A
OBWrappedCollateral
0x9F124ce59D8De0274574949400640a2677067ACC
AMM Contracts
Tokens

Other chains

Abstract

AMM Contracts
Tokens

Linea

AMM Contracts
Tokens

Celo

AMM Contracts
Mainnet
Testnet
PredictionMarket
Coming soon
PredictionMarketQuerier
Coming soon
Tokens
Token
Mainnet
Testnet
USDT
Coming soon

Audits

Order Book

The Order Book contracts have been audited by Cyfrin:
  1. Global report: https://drive.google.com/file/d/1RNqS5NrVL9sCCPhdPKU6iWdyftFyHVYg/view
  1. Reports per component and reviews:
    1. https://github.com/Cyfrin/cyfrin-audit-reports/blob/main/reports/2026-03-13-cyfrin-myriad-clob-v2.0.pdf
    2. https://github.com/Cyfrin/cyfrin-audit-reports/blob/main/reports/2026-04-08-cyfrin-myriad-realitio-oracle-v2.0.pdf
    3. https://github.com/Cyfrin/cyfrin-audit-reports/blob/main/reports/2026-04-07-cyfrin-myriad-pr145-v2.0.pdf

AMM


Changelog

V2.0.0

  • Added API key authentication
  • Added rate limiting
  • Markets endpoints with keyword search and charts
  • Market events and user events endpoints with timestamp filtering
  • Historical prices ingestion + charting

V2.0.1

  • Added portfolio endpoint
  • Added market holders endpoint

V2.0.2

  • Added market quote endpoint

V2.0.3

  • Added market quote_with_fee endpoint

V2.0.4

  • Made most API endpoints public
Share
Content