# API reference Source: https://docs.bayse.markets/api-reference/introduction Complete reference for all Bayse Markets API endpoints ## Base URL ``` https://relay.bayse.markets ``` Our sandbox environment is currently a work in progress. For now, please use the production URL and sign in with your production credentials. ## Authentication Authentication requirements vary by endpoint. See the [Authentication guide](/authentication) for full details. No authentication required. Requires `x-auth-token` + `x-device-id` (from login). Requires `X-Public-Key` header. Requires `X-Public-Key` + `X-Timestamp` + `X-Signature`. ## Error responses All errors return a consistent JSON body: ```json theme={null} { "error": "error_code", "message": "Human-readable description", "statusCode": 400 } ``` Common status codes: | Code | Meaning | | ---- | ---------------------------------------------- | | 400 | Bad request — invalid parameters. | | 401 | Unauthorized — missing or invalid credentials. | | 404 | Not found — resource does not exist. | | 422 | Unprocessable entity — validation failed. | | 500 | Internal server error. | ## Pagination List endpoints use `page` and `size` query parameters: ``` GET /v1/pm/events?page=1&size=20 ``` Paginated responses include a `pagination` object: ```json theme={null} { "pagination": { "page": 1, "size": 20, "lastPage": 5, "totalCount": 98 } } ``` ## Request tracing You can provide a custom trace ID for debugging: ```bash theme={null} curl -H "x-trace-id: my-trace-123" https://relay.bayse.markets/v1/pm/events ``` The trace ID is echoed back in response headers. ## Endpoints ### System `GET /health` `GET /version` ### User `POST /v1/user/login` `POST /v1/user/me/api-keys` `GET /v1/user/me/api-keys` `DELETE /v1/user/me/api-keys/{keyId}` `POST /v1/user/me/api-keys/{keyId}/rotate` ### Trading `GET /v1/pm/events` `GET /v1/pm/events/{eventId}` `POST /v1/pm/events/{eventId}/markets/{marketId}/quote` `POST /v1/pm/events/{eventId}/markets/{marketId}/orders` `GET /v1/pm/portfolio` `GET /v1/pm/orders` `GET /v1/pm/orders/{orderId}` `DELETE /v1/pm/orders/{orderId}` `POST /v1/pm/markets/{marketId}/mint` `POST /v1/pm/markets/{marketId}/burn` `GET /v1/pm/activities` ### Wallet `GET /v1/wallet/assets` ### Market data `GET /v1/pm/events/{eventId}/price-history` `GET /v1/pm/books` `GET /v1/pm/markets/{marketId}/ticker` `GET /v1/pm/trades` ### Sports data `GET /v1/pm/sports/leagues` `GET /v1/pm/sports/teams` `GET /v1/pm/sports/games` # Activities Source: https://docs.bayse.markets/api-reference/pm/activities GET /v1/pm/activities Get your trading activity history ## Authentication Read authentication required — `X-Public-Key` header. ## Query parameters Filter activities by category. Accepted values: | Filter | Activity types included | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `buys` | `BUY_MARKET_ORDER_CREATED`, `BUY_TRADE_FILL` | | `sells` | `SELL_MARKET_ORDER_CREATED`, `SELL_TRADE_FILL` | | `limits` | `BUY_LIMIT_ORDER_CREATED`, `SELL_LIMIT_ORDER_CREATED`, `BUY_LIMIT_ORDER_CANCELLED`, `SELL_LIMIT_ORDER_CANCELLED`, `BUY_LIMIT_ORDER_EXPIRED`, `SELL_LIMIT_ORDER_EXPIRED` | | `payout` | `PAYOUT_WIN`, `PAYOUT_LOSS` | When no filter is provided, all activity types are returned except `BUY_TRADE_FILL` and `SELL_TRADE_FILL`. Page number. Results per page. ## Activity types Each activity in the response has a `type` field with one of the following values: | Type | Description | | ---------------------------- | --------------------------------------- | | `BUY_MARKET_ORDER_CREATED` | A market buy order was placed | | `SELL_MARKET_ORDER_CREATED` | A market sell order was placed | | `BUY_LIMIT_ORDER_CREATED` | A limit buy order was placed | | `SELL_LIMIT_ORDER_CREATED` | A limit sell order was placed | | `BUY_LIMIT_ORDER_CANCELLED` | A limit buy order was cancelled | | `SELL_LIMIT_ORDER_CANCELLED` | A limit sell order was cancelled | | `BUY_LIMIT_ORDER_EXPIRED` | A limit buy order expired | | `SELL_LIMIT_ORDER_EXPIRED` | A limit sell order expired | | `BUY_TRADE_FILL` | A buy order was filled via trade match | | `SELL_TRADE_FILL` | A sell order was filled via trade match | | `PAYOUT_WIN` | Payout received for a winning position | | `PAYOUT_LOSS` | Market resolved against your position | | `BUY_REFUND` | Refund issued for a buy order | ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/activities?type=buys&page=1&size=20" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/activities?type=buys&page=1&size=20', { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const activities = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/activities', params={'type': 'buys', 'page': 1, 'size': 20}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) activities = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/activities", nil) q := req.URL.Query() q.Add("type", "buys") q.Add("page", "1") q.Add("size", "20") req.URL.RawQuery = q.Encode() req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a paginated list of your trading activities across all markets. ### Response fields Activity ID. Activity type (see [Activity types](#activity-types)). Event ID. Market ID. Outcome ID. Order ID (if applicable). Settlement ID (if applicable). Transaction ID (if applicable). Event type. Event title. Market title. Outcome name (e.g. `"YES"`, `"NO"`). Resolved outcome (for payout activities). Currency code (e.g. `"USD"`, `"NGN"`). Order amount. Fee charged. Number of shares. Filled share quantity (limit orders). Remaining share quantity (limit orders). Order price. Average fill price (limit orders). Total cost of the order. Currency base multiplier. Order status. Expiration timestamp (limit orders with GTD). Payout amount (for `PAYOUT_WIN`). Amount spent before cancellation/expiry (buy limit orders). Amount refunded on cancellation/expiry (buy limit orders). Amount earned before cancellation/expiry (sell limit orders). Shares returned on cancellation/expiry (sell limit orders). ISO 8601 timestamp. ISO 8601 timestamp. Current page number. Results per page. Last page number. Total number of activities. ```json 200 OK theme={null} { "activities": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "type": "BUY_MARKET_ORDER_CREATED", "eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "outcomeId": "d4e5f6a7-b8c9-0123-def1-234567890123", "orderId": "e5f6a7b8-c9d0-1234-ef12-345678901234", "eventType": "SINGLE", "imageUrl": "https://example.com/image.png", "eventTitle": "Will BTC reach $100k by March 2026?", "marketTitle": "Bitcoin Price Prediction", "outcome": "YES", "currency": "USD", "amount": "100", "fee": "2", "size": "138.21", "price": "0.7235", "totalCost": "102", "currencyBaseMultiplier": "1", "status": "FILLED", "createdAt": "2026-02-17T12:00:00Z", "updatedAt": "2026-02-17T12:00:00Z" } ], "pagination": { "page": 1, "size": 20, "lastPage": 4, "totalCount": 73 } } ``` # Batch amend orders Source: https://docs.bayse.markets/api-reference/pm/batch-amend-orders POST /v1/pm/orders/batch/amend Modify the price and/or size of up to 20 open CLOB orders in a single round-trip Modify up to **20 CLOB orders** in a single request. Each item names an existing `orderId` you own and supplies the new `price`, the new total `size`, or both. Orders may belong to different markets and events. CLOB-only: any AMM order is rejected per-item with `UNSUPPORTED_ENGINE`. Amend is the natural complement to place/cancel for market makers running cancel-and-replace ladders. It mutates an order in place — preserving time priority when possible — instead of cancelling and re-placing. See the [Batch orders](/concepts/batch-orders) concept page for limits, semantics, and rate-limit behavior. ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Headers Optional. 1–255 characters of `[A-Za-z0-9_-]`. Retries within 24 hours that share the same key, body, and route replay the original response with `Idempotent-Replayed: true`. A retry with the same key but a different body is rejected with `422`. A concurrent retry (sent while the first is still in flight) is rejected with `409` — back off briefly and retry once the first call has finished. Transient responses (`5xx`, `429`, `408`) are not cached, so you can recover by retrying. ## Request body 1–20 amend items. Each item is processed independently — one bad item does not abort the others. UUID of the order to amend. Must be owned by the caller. Status must be `open` or `partial_filled`; terminal orders return `NOT_FOUND`. New limit price per share (0.01–0.99). Absolute, not a delta. Optional — omit to keep the order's current price. **At least one of `newPrice` or `newSize` must be supplied per item**; items with neither are rejected with `BAD_REQUEST`. New TOTAL size of the order in the order's original currency. Must be greater than the order's `filledSize`, and the resulting remaining (`newSize − filledSize`) must be at least the minimum order size. Optional — omit to keep the order's current size. ## Self-trade prevention Self-trade prevention on amend is a fixed server policy: **always `CANCEL_OLDEST`**. If the amend would put the order in a position that crosses a same-user resting order, the resting crosser is cancelled and the amend proceeds. * The order's resting `stpMode` (set at placement) is **not** consulted for amend — it governs matching-time self-cross behavior on incoming orders, which is a different event from the amend itself. * Orders being amended in the same batch are automatically excluded from the cancel set, so simultaneous amends that transiently cross don't kill each other. * If you want an amend to fail rather than cancel a crosser, cancel + re-place instead of amending. ## Example request ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/orders/batch/amend" BODY='{"items":[{"orderId":"f6a7b8c9-d0e1-2345-fabc-678901234567","newPrice":0.50,"newSize":15},{"orderId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","newPrice":0.40,"newSize":8}]}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Idempotency-Key: 9d3f2c0e-7b1a-4e5d-9f80-3a8b1c2d4e5f" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'POST'; const path = '/v1/pm/orders/batch/amend'; const body = JSON.stringify({ items: [ { orderId: 'f6a7b8c9-d0e1-2345-fabc-678901234567', newPrice: 0.50, newSize: 15, }, { orderId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', newPrice: 0.40, newSize: 8, }, ], }); const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Idempotency-Key': crypto.randomUUID(), 'Content-Type': 'application/json', }, body, }); const result = await response.json(); ``` ```python Python theme={null} import hmac, hashlib, base64, json, time, uuid, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'POST' path = '/v1/pm/orders/batch/amend' body = json.dumps({ 'items': [ { 'orderId': 'f6a7b8c9-d0e1-2345-fabc-678901234567', 'newPrice': 0.50, 'newSize': 15, }, { 'orderId': 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', 'newPrice': 0.40, 'newSize': 8, }, ], }) body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() resp = requests.post( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Idempotency-Key': str(uuid.uuid4()), 'Content-Type': 'application/json', }, data=body, ) result = resp.json() ``` ```go Go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "net/http" "strconv" "strings" "time" ) timestamp := time.Now().Unix() method := "POST" urlPath := "/v1/pm/orders/batch/amend" body := []byte(`{"items":[{"orderId":"f6a7b8c9-d0e1-2345-fabc-678901234567","newPrice":0.50,"newSize":15},{"orderId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","newPrice":0.40,"newSize":8}]}`) bodySum := sha256.Sum256(body) bodyHash := hex.EncodeToString(bodySum[:]) payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, urlPath, bodyHash) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("POST", "https://relay.bayse.markets"+urlPath, strings.NewReader(string(body))) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) req.Header.Set("Idempotency-Key", "9d3f2c0e-7b1a-4e5d-9f80-3a8b1c2d4e5f") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response Always `CLOB` for batch endpoints today. Per-item outcomes, in the same order as the request. Position of this item in the request `items` array (zero-based). The order UUID submitted in the request. `true` if the amend transitioned the order to the new `(price, size)`, `false` if it failed. The amended CLOB order. Present when `success` is `true`. Includes the new `price`, `size`, `remainingSize`, `amount`, and updated timestamps. See the CLOB order fields on [Place order](/api-reference/pm/place-order) for the full schema. Present when `success` is `false`. Machine-readable code (e.g. `INSUFFICIENT_BALANCE`, `INSUFFICIENT_SHARES`, `NOT_FOUND`, `MARKET_CLOSED`, `UNSUPPORTED_ENGINE`). Human-readable description. Total items submitted. Items that transitioned to the new `(price, size)`. Items that failed; see each result's `error`. ```json 200 OK theme={null} { "engine": "CLOB", "results": [ { "index": 0, "orderId": "f6a7b8c9-d0e1-2345-fabc-678901234567", "success": true, "order": { "id": "f6a7b8c9-d0e1-2345-fabc-678901234567", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "userId": "9a8b7c6d-5e4f-3210-abcd-ef1234567890", "outcome": "YES", "side": "BUY", "orderType": "LIMIT", "type": "BUY", "status": "open", "amount": 7.50, "price": 0.50, "size": 15, "filledSize": 0, "remainingSize": 15, "avgFillPrice": 0, "fee": 0, "postOnly": false, "stpMode": "SKIP", "quantity": 0, "createdAt": "2026-05-12T17:33:21Z", "updatedAt": "2026-05-12T17:38:52Z" } }, { "index": 1, "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "success": false, "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient balance" } } ], "summary": { "total": 2, "succeeded": 1, "failed": 1 } } ``` ```json 400 Bad Request — body shape theme={null} { "error": "bad_request", "message": "invalid request body: items[] required (1..20 entries with orderId and at least one of newPrice / newSize)", "statusCode": 400 } ``` ```json 200 OK — per-item bad request (neither newPrice nor newSize) theme={null} { "engine": "CLOB", "results": [ { "index": 0, "orderId": "f6a7b8c9-d0e1-2345-fabc-678901234567", "success": false, "error": { "code": "BAD_REQUEST", "message": "at least one of newPrice or newSize must be specified" } } ], "summary": { "total": 1, "succeeded": 0, "failed": 1 } } ``` ```json 409 Conflict theme={null} { "error": "conflict", "message": "A request with this Idempotency-Key is already in flight; retry shortly.", "statusCode": 409 } ``` ```json 422 Unprocessable Entity theme={null} { "error": "unprocessable_entity", "message": "Idempotency-Key reused with a different request body", "statusCode": 422 } ``` ```json 429 Too Many Requests theme={null} { "message": "Rate limit exceeded for batch payload size. Please reduce the batch or try again later.", "retryAfter": 2 } ``` ## Per-item error codes | Code | Meaning | Caller action | | ---------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | `INSUFFICIENT_BALANCE` | Wallet balance is too low for the BID amend's additional USD/NGN lock | Reduce `newSize` or `newPrice`, or top up the wallet, or fire a cancel batch first to free funds | | `INSUFFICIENT_SHARES` | Free share balance is too low for the ASK amend's additional shares lock | Reduce `newSize`, or cancel another ASK first to free shares | | `NOT_FOUND` | Order not owned by the caller, or already terminal (`filled` / `cancelled` / `expired` / `rejected`) | Check the order's current status; amend works only while `open` or `partial_filled` | | `MARKET_CLOSED` | Market is not `OPEN` (paused, closed, or resolved) | Wait for the market to reopen, or cancel the order instead | | `UNSUPPORTED_ENGINE` | Order belongs to an LMSR / AMM market | Cancel and re-place to change price/size on AMM markets | | `DUPLICATE_ORDER_ID` | The same `orderId` appears more than once in `items` | Deduplicate the request before retrying | | `INTERNAL` | Unexpected upstream error | Retry; if persistent, contact support | ## Order ordering convention A typical cancel-and-replace cycle uses three sibling calls in this order: 1. `DELETE /v1/pm/orders/batch` — cancel stale orders (frees locked capital and shares). 2. `POST /v1/pm/orders/batch/amend` — modify in-place orders (uses the freshly freed capacity). 3. `POST /v1/pm/orders/batch` — place new orders (uses what remains). Funding rejections are evaluated per-item against the wallet's current state at the time the amend reaches the matching engine. By cancelling first you give your amend batch the best chance of clearing for size-up / price-up changes. The amend itself does not aggregate releases ahead of debits within the batch; you'll get an `INSUFFICIENT_BALANCE` per item that can't fit even though the batch's net delta might. Batch amend charges **one rate-limit token per item** against your write rate-limit bucket — a 20-item amend costs 20 tokens. Over-budget batches are rejected with `429` before any amends reach the matching engine. See [Rate limits](/rate-limits). Amend preserves time priority when the new `(price, size)` is unchanged or shrunk at the same price level. Price changes (up or down) move the order to the new tail of the new level, same as a cancel-and-replace. # Batch cancel orders Source: https://docs.bayse.markets/api-reference/pm/batch-cancel-orders DELETE /v1/pm/orders/batch Cancel up to 100 CLOB orders across one or more markets in a single round-trip Cancel up to **100 CLOB orders** in a single request. Order IDs may belong to different markets and events. CLOB-only: any AMM order is rejected per-item with `UNSUPPORTED_ENGINE`. Ownership is enforced upstream — order IDs the caller does not own return `ORDER_NOT_FOUND`. See the [Batch orders](/concepts/batch-orders) concept page for limits, semantics, and rate-limit behavior. ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Headers Optional. 1–255 characters of `[A-Za-z0-9_-]`. Retries within 24 hours that share the same key, body, and route replay the original response with `Idempotent-Replayed: true`. A retry with the same key but a different body is rejected with `422`. A concurrent retry (sent while the first is still in flight) is rejected with `409` — back off briefly and retry once the first call has finished. Transient responses (`5xx`, `429`, `408`) are not cached, so you can recover by retrying. ## Request body 1–100 order UUIDs to cancel. Each ID is processed independently — failures on one ID do not abort the rest of the batch. ## Example request ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="DELETE" URL_PATH="/v1/pm/orders/batch" BODY='{"orderIds":["f6a7b8c9-d0e1-2345-fabc-678901234567","a1b2c3d4-e5f6-7890-abcd-ef1234567890"]}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X DELETE "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Idempotency-Key: 9d3f2c0e-7b1a-4e5d-9f80-3a8b1c2d4e5f" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'DELETE'; const path = '/v1/pm/orders/batch'; const body = JSON.stringify({ orderIds: [ 'f6a7b8c9-d0e1-2345-fabc-678901234567', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', ], }); const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Idempotency-Key': crypto.randomUUID(), 'Content-Type': 'application/json', }, body, }); const result = await response.json(); ``` ```python Python theme={null} import hmac, hashlib, base64, json, time, uuid, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'DELETE' path = '/v1/pm/orders/batch' body = json.dumps({ 'orderIds': [ 'f6a7b8c9-d0e1-2345-fabc-678901234567', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', ], }) body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() resp = requests.delete( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Idempotency-Key': str(uuid.uuid4()), 'Content-Type': 'application/json', }, data=body, ) result = resp.json() ``` ```go Go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "strconv" "strings" "time" ) timestamp := time.Now().Unix() method := "DELETE" urlPath := "/v1/pm/orders/batch" body := []byte(`{"orderIds":["f6a7b8c9-d0e1-2345-fabc-678901234567","a1b2c3d4-e5f6-7890-abcd-ef1234567890"]}`) bodySum := sha256.Sum256(body) bodyHash := hex.EncodeToString(bodySum[:]) payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, urlPath, bodyHash) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("DELETE", "https://relay.bayse.markets"+urlPath, strings.NewReader(string(body))) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) req.Header.Set("Idempotency-Key", "9d3f2c0e-7b1a-4e5d-9f80-3a8b1c2d4e5f") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` This DELETE carries a JSON body, so the signing payload's `bodyHash` is the SHA-256 hash of the body — not the empty hash used for body-less DELETEs like [Cancel order](/api-reference/pm/cancel-order). ## Response Always `CLOB` for batch endpoints today. Per-order outcomes, in the same order as the request. The order UUID submitted in the request. `true` if the cancel was accepted upstream, `false` if it failed. Present when `success` is `false`. Machine-readable code (e.g. `ORDER_NOT_FOUND`, `ORDER_NOT_CANCELLABLE`, `UNSUPPORTED_ENGINE`). Human-readable description. Total IDs submitted. IDs cancelled successfully. IDs that failed to cancel. ```json 200 OK theme={null} { "engine": "CLOB", "results": [ { "orderId": "f6a7b8c9-d0e1-2345-fabc-678901234567", "success": true }, { "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "success": false, "error": { "code": "ORDER_NOT_FOUND", "message": "Order not found" } } ], "summary": { "total": 2, "succeeded": 1, "failed": 1 } } ``` ```json 400 Bad Request theme={null} { "error": "bad_request", "message": "invalid request body: orderIds[] required (1..100 UUIDs)", "statusCode": 400 } ``` ```json 409 Conflict theme={null} { "error": "conflict", "message": "A request with this Idempotency-Key is already in flight; retry shortly.", "statusCode": 409 } ``` ```json 422 Unprocessable Entity theme={null} { "error": "unprocessable_entity", "message": "Idempotency-Key reused with a different request body", "statusCode": 422 } ``` ```json 429 Too Many Requests theme={null} { "message": "Rate limit exceeded for batch payload size. Please reduce the batch or try again later.", "retryAfter": 2 } ``` Only CLOB orders with status `open` or `partial_filled` can be cancelled. AMM orders execute instantly and cannot be cancelled — sell your shares to exit instead. Batch cancel charges **one rate-limit token per ID**. # Batch place orders Source: https://docs.bayse.markets/api-reference/pm/batch-place-orders POST /v1/pm/orders/batch Place up to 20 CLOB orders across one or more markets in a single round-trip Submit up to **20 CLOB orders** in a single request. Orders may span multiple markets and events — each item carries only `outcomeId`, and the server resolves the parent market and event. CLOB-only: AMM markets are rejected per-order with `UNSUPPORTED_ENGINE`. See the [Batch orders](/concepts/batch-orders) concept page for limits, semantics, and rate-limit behavior. ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Headers Optional. 1–255 characters of `[A-Za-z0-9_-]`. Retries within 24 hours that share the same key, body, and route replay the original response with `Idempotent-Replayed: true`. A retry with the same key but a different body is rejected with `422`. A concurrent retry (sent while the first is still in flight) is rejected with `409` — back off briefly and retry once the first call has finished. Transient responses (`5xx`, `429`, `408`) are not cached, so you can recover by retrying. ## Request body 1–20 order items. Each item is processed independently — one bad item does not abort the others. UUID of the outcome to trade. Use [Get Event](/api-reference/pm/get-event) to find outcome IDs. `BUY` or `SELL`. `LIMIT` or `MARKET`. Amount to spend (buy) or receive (sell). Must be greater than 0. `USD` (default) or `NGN`. Limit price per share (0.01–0.99). Required for `LIMIT` orders. `GTC`, `GTD`, `FAK`, or `FOK`. Defaults to `GTC` for limit, `FAK` for market. If `true`, the order is rejected instead of crossing the spread. Limit orders only. Maximum acceptable slippage for market orders (0.00–1.00). ISO 8601 expiration timestamp. Required for `GTD` orders. Self-trade prevention mode: `SKIP` (default), `CANCEL_OLDEST`, `CANCEL_NEWEST`, or `CANCEL_BOTH`. See [Place order](/api-reference/pm/place-order) for full semantics. Optional client-supplied identifier. Echoed back in the matching result so you can correlate items with their source ticket without relying on array order. ## Example request ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/orders/batch" BODY='{"orders":[{"outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","side":"BUY","type":"LIMIT","amount":100,"price":0.70,"timeInForce":"GTC","clientOrderId":"mm-001"},{"outcomeId":"d4e5f6a7-b8c9-0123-defa-456789012345","side":"SELL","type":"LIMIT","amount":50,"price":0.32,"timeInForce":"GTC","clientOrderId":"mm-002"}]}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Idempotency-Key: 9d3f2c0e-7b1a-4e5d-9f80-3a8b1c2d4e5f" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'POST'; const path = '/v1/pm/orders/batch'; const body = JSON.stringify({ orders: [ { outcomeId: 'c3d4e5f6-a7b8-9012-cdef-345678901234', side: 'BUY', type: 'LIMIT', amount: 100, price: 0.70, timeInForce: 'GTC', clientOrderId: 'mm-001', }, { outcomeId: 'd4e5f6a7-b8c9-0123-defa-456789012345', side: 'SELL', type: 'LIMIT', amount: 50, price: 0.32, timeInForce: 'GTC', clientOrderId: 'mm-002', }, ], }); const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Idempotency-Key': crypto.randomUUID(), 'Content-Type': 'application/json', }, body, }); const result = await response.json(); ``` ```python Python theme={null} import hmac, hashlib, base64, json, time, uuid, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'POST' path = '/v1/pm/orders/batch' body = json.dumps({ 'orders': [ { 'outcomeId': 'c3d4e5f6-a7b8-9012-cdef-345678901234', 'side': 'BUY', 'type': 'LIMIT', 'amount': 100, 'price': 0.70, 'timeInForce': 'GTC', 'clientOrderId': 'mm-001', }, { 'outcomeId': 'd4e5f6a7-b8c9-0123-defa-456789012345', 'side': 'SELL', 'type': 'LIMIT', 'amount': 50, 'price': 0.32, 'timeInForce': 'GTC', 'clientOrderId': 'mm-002', }, ], }) body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() resp = requests.post( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Idempotency-Key': str(uuid.uuid4()), 'Content-Type': 'application/json', }, data=body, ) result = resp.json() ``` ```go Go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "strconv" "strings" "time" ) timestamp := time.Now().Unix() method := "POST" urlPath := "/v1/pm/orders/batch" body := []byte(`{"orders":[{"outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","side":"BUY","type":"LIMIT","amount":100,"price":0.70,"timeInForce":"GTC","clientOrderId":"mm-001"},{"outcomeId":"d4e5f6a7-b8c9-0123-defa-456789012345","side":"SELL","type":"LIMIT","amount":50,"price":0.32,"timeInForce":"GTC","clientOrderId":"mm-002"}]}`) bodySum := sha256.Sum256(body) bodyHash := hex.EncodeToString(bodySum[:]) payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, urlPath, bodyHash) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("POST", "https://relay.bayse.markets"+urlPath, strings.NewReader(string(body))) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) req.Header.Set("Idempotency-Key", "9d3f2c0e-7b1a-4e5d-9f80-3a8b1c2d4e5f") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response Always `CLOB` for batch endpoints today. Per-order outcomes, in the same order as the request. Position of this item in the request `orders` array (zero-based). Echoed from the request, if provided. `true` if the order was accepted upstream, `false` if it failed. The placed CLOB order. Present when `success` is `true`. See the CLOB order fields on [Place order](/api-reference/pm/place-order) for the full schema. Present when `success` is `false`. Machine-readable code (e.g. `UNSUPPORTED_ENGINE`, `INSUFFICIENT_BALANCE`, `INVALID_OUTCOME`). Human-readable description. Total items submitted. Items that placed successfully. Items that failed. ```json 200 OK theme={null} { "engine": "CLOB", "results": [ { "index": 0, "clientOrderId": "mm-001", "success": true, "order": { "id": "f6a7b8c9-d0e1-2345-fabc-678901234567", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "userId": "9a8b7c6d-5e4f-3210-abcd-ef1234567890", "outcome": "YES", "side": "BUY", "orderType": "LIMIT", "type": "GTC", "status": "open", "amount": 100, "price": 0.70, "size": 100, "filledSize": 0, "remainingSize": 100, "avgFillPrice": 0, "fee": 0, "postOnly": false, "stpMode": "SKIP", "quantity": 0, "createdAt": "2026-05-04T12:00:00Z", "updatedAt": "2026-05-04T12:00:00Z" } }, { "index": 1, "clientOrderId": "mm-002", "success": false, "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient USD balance for this order" } } ], "summary": { "total": 2, "succeeded": 1, "failed": 1 } } ``` ```json 400 Bad Request theme={null} { "error": "bad_request", "message": "invalid request body: orders[] required (1..20 entries with outcomeId, side, type, amount)", "statusCode": 400 } ``` ```json 409 Conflict theme={null} { "error": "conflict", "message": "A request with this Idempotency-Key is already in flight; retry shortly.", "statusCode": 409 } ``` ```json 422 Unprocessable Entity theme={null} { "error": "unprocessable_entity", "message": "Idempotency-Key reused with a different request body", "statusCode": 422 } ``` ```json 429 Too Many Requests theme={null} { "message": "Rate limit exceeded for batch payload size. Please reduce the batch or try again later.", "retryAfter": 2 } ``` Batch calls are charged **per item** against your write rate-limit bucket — a 20-order batch costs 20 tokens. Over-budget batches are rejected with `429` before any orders reach the matching engine. See [Rate limits](/rate-limits). # Burn shares Source: https://docs.bayse.markets/api-reference/pm/burn-shares POST /v1/pm/markets/{marketId}/burn Destroy equal YES and NO shares and receive funds back Burning is the reverse of minting. You surrender an equal number of YES and NO shares (a complementary pair) and receive funds back. Like minting, burning does not affect market prices since it removes both sides equally. This is useful for exiting a position when you hold both outcomes, or for converting shares back to cash. You must hold sufficient shares of both YES and NO to burn. ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Path parameters UUID of the market. ## Request body Amount to redeem from the burn operation in the selected `currency`. For example, `quantity: 100, currency: "NGN"` means "burn enough complete-set inventory to receive 100 NGN". Must be greater than 0. `USD` (default) or `NGN`. Request `quantity` is a wallet amount in the provided currency. The response `quantity` is the normalized share quantity burned. ## Example request ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/burn" BODY='{"quantity":10,"currency":"USD"}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'POST'; const path = '/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/burn'; const body = JSON.stringify({ quantity: 10, currency: 'USD', }); const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Content-Type': 'application/json', }, body, }); const result = await response.json(); ``` ```python Python theme={null} import hmac, hashlib, base64, json, time, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'POST' path = '/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/burn' body = json.dumps({ 'quantity': 10, 'currency': 'USD', }) body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() resp = requests.post( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Content-Type': 'application/json', }, data=body, ) result = resp.json() ``` ```go Go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "strconv" "strings" "time" ) timestamp := time.Now().Unix() method := "POST" urlPath := "/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/burn" body := []byte(`{"quantity":10,"currency":"USD"}`) bodySum := sha256.Sum256(body) bodyHash := hex.EncodeToString(bodySum[:]) payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, urlPath, bodyHash) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("POST", "https://relay.bayse.markets"+urlPath, strings.NewReader(string(body))) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response UUID of the burn operation. UUID of the market. Normalized share quantity burned. Current price of outcome 1 (YES). Returned for convenience — burning does not change market prices. Current price of outcome 2 (NO). Returned for convenience — burning does not change market prices. Funds returned from the burn operation. ```json 200 OK theme={null} { "operationId": "e5f6a7b8-c9d0-1234-efab-567890123456", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "quantity": 10, "outcome1Price": 0.65, "outcome2Price": 0.35, "proceeds": 10.00 } ``` # Cancel order Source: https://docs.bayse.markets/api-reference/pm/cancel-order DELETE /v1/pm/orders/{orderId} Cancel an open or partially filled CLOB order ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Path parameters UUID of the order to cancel. ## Example request ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="DELETE" URL_PATH="/v1/pm/orders/f6a7b8c9-d0e1-2345-fabc-678901234567" # No body — bodyHash is empty, payload ends with a trailing dot PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}." SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X DELETE "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'DELETE'; const path = '/v1/pm/orders/f6a7b8c9-d0e1-2345-fabc-678901234567'; // No body — bodyHash is empty const payload = `${timestamp}.${method}.${path}.`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, }, }); ``` ```python Python theme={null} import hmac, hashlib, base64, time, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'DELETE' path = '/v1/pm/orders/f6a7b8c9-d0e1-2345-fabc-678901234567' # No body — bodyHash is empty payload = f'{timestamp}.{method}.{path}.' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() requests.delete( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, }, ) ``` ```go Go theme={null} timestamp := time.Now().Unix() method := "DELETE" urlPath := "/v1/pm/orders/f6a7b8c9-d0e1-2345-fabc-678901234567" // No body — bodyHash is empty payload := fmt.Sprintf("%d.%s.%s.", timestamp, method, urlPath) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("DELETE", "https://relay.bayse.markets"+urlPath, nil) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) http.DefaultClient.Do(req) ``` ## Response ```json 200 OK theme={null} { "message": "Order cancelled" } ``` ```json 404 Not Found theme={null} { "error": "not_found", "message": "Order not found", "statusCode": 404 } ``` Only CLOB orders with status `open` or `partial_filled` can be cancelled. AMM orders execute instantly and cannot be cancelled — sell your shares to exit instead. # Get event Source: https://docs.bayse.markets/api-reference/pm/get-event GET /v1/pm/events/{eventId} Get a specific prediction market event by ID ## Authentication Public — no authentication required. Provide `X-Public-Key` for personalized data (watchlist status). ## Path parameters UUID of the event. ## Query parameters Currency for prices: `USD` or `NGN`. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890?currency=NGN" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const eventId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'; const response = await fetch( `https://relay.bayse.markets/v1/pm/events/${eventId}?currency=NGN`, { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const event = await response.json(); ``` ```python Python theme={null} import requests event_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' resp = requests.get( f'https://relay.bayse.markets/v1/pm/events/{event_id}', params={'currency': 'NGN'}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) event = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "GET", "https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890", nil, ) q := req.URL.Query() q.Add("currency", "NGN") req.URL.RawQuery = q.Encode() req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a single event object. See [List events](/api-reference/pm/list-events) for the full field reference — the response shape is identical. ```json 200 OK theme={null} { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "slug": "super-eagles-afcon-2026", "title": "Will Super Eagles qualify for AFCON 2026?", "category": "sports", "type": "single", "engine": "AMM", "status": "open", "resolutionDate": "2025-11-20T00:00:00Z", "closingDate": "2025-11-19T18:00:00Z", "liquidity": 50000, "totalVolume": 120000, "totalOrders": 843, "supportedCurrencies": ["USD", "NGN"], "userWatchlisted": true, "markets": [ { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Will Super Eagles qualify?", "status": "open", "outcome1Id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "outcome1Label": "YES", "outcome1Price": 0.72, "outcome2Id": "d4e5f6a7-b8c9-0123-defa-234567890123", "outcome2Label": "NO", "outcome2Price": 0.28, "yesBuyPrice": 0.72, "minimumOrderAmount": 100, "noBuyPrice": 0.28, "feePercentage": 2.0, "totalOrders": 843, "rules": "Resolves YES if Nigeria qualifies for AFCON 2026." } ] } ``` ```json 404 Not Found theme={null} { "error": "not_found", "message": "Event not found", "statusCode": 404 } ``` # Get event by slug Source: https://docs.bayse.markets/api-reference/pm/get-event-by-slug GET /v1/pm/events/slug/{slug} Get a specific prediction market event by its slug ## Authentication Public — no authentication required. Provide `X-Public-Key` for personalized data (watchlist status). ## Path parameters Human-readable slug of the event (e.g. `crypto-btc-1h-feb-24-11am`). ## Query parameters Currency for prices: `USD` or `NGN`. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/events/slug/crypto-btc-1h-feb-24-11am?currency=USD" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const slug = 'crypto-btc-1h-feb-24-11am'; const response = await fetch( `https://relay.bayse.markets/v1/pm/events/slug/${slug}?currency=USD`, { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const event = await response.json(); ``` ```python Python theme={null} import requests slug = 'crypto-btc-1h-feb-24-11am' resp = requests.get( f'https://relay.bayse.markets/v1/pm/events/slug/{slug}', params={'currency': 'USD'}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) event = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "GET", "https://relay.bayse.markets/v1/pm/events/slug/crypto-btc-1h-feb-24-11am", nil, ) q := req.URL.Query() q.Add("currency", "USD") req.URL.RawQuery = q.Encode() req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a single event object. See [List events](/api-reference/pm/list-events) for the full field reference — the response shape is identical. ```json 200 OK theme={null} { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "slug": "crypto-btc-1h-feb-24-11am", "title": "Bitcoin Hourly — Feb 24 11am GMT", "category": "crypto", "type": "single", "engine": "AMM", "status": "open", "openingDate": "2025-02-24T11:00:00Z", "closingDate": "2025-02-24T12:00:00Z", "resolutionDate": "2025-02-24T12:01:00Z", "assetSymbolPair": "BTCUSDT", "eventThreshold": 96250.50, "seriesSlug": "crypto-btc-1h", "liquidity": 10000, "totalVolume": 25000, "totalOrders": 142, "supportedCurrencies": ["USD", "NGN"], "userWatchlisted": false, "markets": [ { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "BTC above $96,250.50?", "status": "open", "outcome1Id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "outcome1Label": "YES", "outcome1Price": 0.55, "outcome2Id": "d4e5f6a7-b8c9-0123-defa-234567890123", "outcome2Label": "NO", "outcome2Price": 0.45, "yesBuyPrice": 0.55, "noBuyPrice": 0.45, "feePercentage": 2.0, "totalOrders": 142, "marketThreshold": 96250.50, "rules": "Resolves YES if BTC price is above $96,250.50 at closing." } ] } ``` ```json 404 Not Found theme={null} { "error": "not_found", "message": "Event not found", "statusCode": 404 } ``` # Get order Source: https://docs.bayse.markets/api-reference/pm/get-order GET /v1/pm/orders/{orderId} Get details of a specific order ## Authentication Read authentication required — `X-Public-Key` header. ## Path parameters UUID of the order. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/orders/f6a7b8c9-d0e1-2345-fabc-678901234567" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const orderId = 'f6a7b8c9-d0e1-2345-fabc-678901234567'; const response = await fetch( `https://relay.bayse.markets/v1/pm/orders/${orderId}`, { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const order = await response.json(); ``` ```python Python theme={null} import requests order_id = 'f6a7b8c9-d0e1-2345-fabc-678901234567' resp = requests.get( f'https://relay.bayse.markets/v1/pm/orders/{order_id}', headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) order = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "GET", "https://relay.bayse.markets/v1/pm/orders/f6a7b8c9-d0e1-2345-fabc-678901234567", nil, ) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a single order object. See [Place order](/api-reference/pm/place-order) for the full field reference. ```json 200 OK theme={null} { "id": "f6a7b8c9-d0e1-2345-fabc-678901234567", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "outcome": "YES", "side": "BUY", "orderType": "LIMIT", "stpMode": "SKIP", "status": "partial_filled", "amount": 100, "price": 0.70, "size": 100, "filledSize": 45, "remainingSize": 55, "avgFillPrice": 0.70, "fee": 0.90, "currency": "USD", "createdAt": "2026-02-17T12:00:00Z", "updatedAt": "2026-02-17T12:03:00Z" } ``` ```json 404 Not Found theme={null} { "error": "not_found", "message": "Order not found", "statusCode": 404 } ``` # Get PnL Source: https://docs.bayse.markets/api-reference/pm/get-pnl GET /v1/pm/pnl Get your realized profit and loss over a time period ## Authentication Read authentication required — `X-Public-Key` header. See the [Authentication guide](/authentication). ## Query parameters Predefined time window. If omitted and no custom range is provided, defaults to all time. **Rolling windows:** `12H`, `24H`, `1W`, `1M`, `1Y` — relative to current time (e.g. `1M` = last 30 days). **Calendar windows:** `THIS_WEEK`, `THIS_MONTH`, `THIS_YEAR` — from the start of the current week/month/year to now. Custom start time in ISO 8601 format. Overrides `timePeriod`. Must be paired with `end`. Custom end time in ISO 8601 format. Overrides `timePeriod`. Must be paired with `start`. Currency filter — `USD` or `NGN`. Defaults to `USD` if omitted. PnL is computed per currency, so if you traded only in NGN and query USD, all values will be zero. Include per-event breakdown (top 30 events by most recent activity). Defaults to `false`. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/pnl?timePeriod=1M&breakdown=true" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/pnl?timePeriod=1M&breakdown=true', { headers: { 'X-Public-Key': 'pk_live_abcdef123456', }, } ); const pnl = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/pnl', params={'timePeriod': '1M', 'breakdown': 'true'}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) pnl = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/pnl?timePeriod=1M&breakdown=true", nil) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Total realized PnL — the sum of `settlementPnl` and `tradePnl`. Realized PnL as a percentage of total cost basis. `0` when no cost basis exists in the queried period. PnL from resolved markets. Computed as total payouts received minus total cost basis of settled positions. PnL from selling shares before market resolution. Computed as proceeds minus cost basis for each sell. Number of settled positions that received a payout. Number of settled positions that received zero payout. Currency the PnL is denominated in. Per-event PnL breakdown. Only present when `breakdown=true`. Event UUID. Title of the event. Aggregated realized PnL for this event (settlements + sells). Currency. ISO 8601 timestamp of the most recent settlement or sell in this event. ```json 200 OK theme={null} { "realizedPnl": 31.11, "realizedPnlPercent": 6.03, "settlementPnl": 24.50, "tradePnl": 6.61, "wins": 8, "losses": 6, "currency": "USD", "breakdown": [ { "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "eventTitle": "Will BTC close above $95k today?", "realizedPnl": 12.35, "currency": "USD", "lastActivity": "2026-03-20T14:30:00Z" }, { "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "eventTitle": "Will Super Eagles qualify for AFCON 2026?", "realizedPnl": -3.50, "currency": "USD", "lastActivity": "2026-03-19T09:15:00Z" } ] } ``` # Get portfolio Source: https://docs.bayse.markets/api-reference/pm/get-portfolio GET /v1/pm/portfolio Get your current positions across all markets ## Authentication Read authentication required — `X-Public-Key` header. See the [Authentication guide](/authentication). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/portfolio" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch('https://relay.bayse.markets/v1/pm/portfolio', { headers: { 'X-Public-Key': 'pk_live_abcdef123456', }, }); const portfolio = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/portfolio', headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) portfolio = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/portfolio", nil) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Your positions across all markets. Position UUID. `YES` or `NO`. UUID of the outcome. Asset identifier. Total shares held. Shares available to sell. Average price paid per share. Total amount invested. Current market value of position. Current price per share if sold now. Payout if this outcome resolves as the winner. Percentage gain or loss from average price. Currency this position is denominated in. Your user UUID. Summary of the market this position is in. Market UUID. Market question. Market image. Outcome 1 UUID. Outcome 2 UUID. Parent event summary. Event UUID. Event title. `single` or `combined`. `AMM` or `CLOB`. ISO 8601 timestamp. ISO 8601 timestamp. Total amount invested across all positions. Total current value of all positions. Overall portfolio gain or loss percentage. Pagination information. ```json 200 OK theme={null} { "outcomeBalances": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "outcome": "YES", "outcomeId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "balance": 138.21, "availableBalance": 138.21, "averagePrice": 0.7235, "cost": 100, "currentValue": 107.60, "sellPrice": 0.7786, "payoutIfOutcomeWins": 138.21, "percentageChange": 7.60, "currency": "USD", "market": { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Will Super Eagles qualify?", "event": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "Will Super Eagles qualify for AFCON 2026?", "type": "single", "engine": "AMM" } }, "createdAt": "2026-02-17T12:00:00Z", "updatedAt": "2026-02-17T12:05:00Z" } ], "portfolioCost": 100, "portfolioCurrentValue": 107.60, "portfolioPercentageChange": 7.60, "pagination": { "page": 1, "size": 20, "lastPage": 1, "totalCount": 1 } } ``` # Get quote Source: https://docs.bayse.markets/api-reference/pm/get-quote POST /v1/pm/events/{eventId}/markets/{marketId}/quote Get a price quote before placing an order Get the expected cost, shares, and fees for a potential trade without committing to it. Provide `X-Public-Key` to include profit estimates based on your existing position. ## Authentication Public — no authentication required. Provide `X-Public-Key` for personalized profit estimates. ## Path parameters UUID of the event. UUID of the market. ## Request body `BUY` or `SELL`. UUID of the outcome. Use the [Get Event](/api-reference/pm/get-event) endpoint to find outcome IDs. Amount to spend (buy) or receive (sell), in the specified currency. `USD` (default) or `NGN`. ## Example request ```bash cURL theme={null} curl -X POST \ "https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/quote" \ -H "Content-Type: application/json" \ -d '{"side":"BUY","outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","amount":100,"currency":"USD"}' ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/quote', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ side: 'BUY', outcomeId: 'c3d4e5f6-a7b8-9012-cdef-345678901234', amount: 100, currency: 'USD', }), } ); const quote = await response.json(); ``` ```python Python theme={null} import requests resp = requests.post( 'https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/quote', json={ 'side': 'BUY', 'outcomeId': 'c3d4e5f6-a7b8-9012-cdef-345678901234', 'amount': 100, 'currency': 'USD', }, ) quote = resp.json() ``` ```go Go theme={null} body := strings.NewReader(`{"side":"BUY","outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","amount":100,"currency":"USD"}`) req, _ := http.NewRequest( "POST", "https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/quote", body, ) req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response Average price per share for this trade (0.00–1.00). Current market price before this trade executes. Number of shares you will receive. Total amount spent (including fee). Cost of shares before fee. Trading fee charged. How much this trade moves the market price. Estimated profit percentage if the outcome wins. Multiplier applied to convert prices to the requested currency (1 for USD, 100 for NGN). Whether the full amount can be filled at the quoted price (relevant for CLOB markets). Whether this trade exceeds maximum liability limits. ```json 200 OK theme={null} { "price": 0.7235, "currentMarketPrice": 0.72, "quantity": 138.21, "amount": 100, "costOfShares": 98.04, "fee": 1.96, "priceImpactAbsolute": 0.0035, "profitPercentage": 38.21, "currencyBaseMultiplier": 1, "completeFill": true, "tradeGoesOverMaxLiability": false } ``` Always get a quote before placing an order to confirm the expected cost and shares. The quoted price is indicative — the actual fill price may differ slightly in fast-moving markets. # Get series events Source: https://docs.bayse.markets/api-reference/pm/get-series-events GET /v1/pm/events/series/{seriesSlug}/lean-events Get a lightweight list of events belonging to a series ## Authentication Public — no authentication required. ## Path parameters Slug of the event series (e.g. `crypto-btc-1h`). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/events/series/crypto-btc-1h/lean-events" ``` ```javascript Node.js theme={null} const seriesSlug = 'crypto-btc-1h'; const response = await fetch( `https://relay.bayse.markets/v1/pm/events/series/${seriesSlug}/lean-events` ); const events = await response.json(); ``` ```python Python theme={null} import requests series_slug = 'crypto-btc-1h' resp = requests.get( f'https://relay.bayse.markets/v1/pm/events/series/{series_slug}/lean-events' ) events = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "GET", "https://relay.bayse.markets/v1/pm/events/series/crypto-btc-1h/lean-events", nil, ) resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns an array of lightweight event summaries (max 20 events). UUID of the event. Event title. ISO 8601 — when the event opens for trading. ISO 8601 — when trading closes. ISO 8601 — when the outcome is determined. ```json 200 OK theme={null} [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "Bitcoin Hourly — Feb 24 11am GMT", "openingDate": "2025-02-24T11:00:00Z", "closingDate": "2025-02-24T12:00:00Z", "resolutionDate": "2025-02-24T12:01:00Z" }, { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Bitcoin Hourly — Feb 24 12pm GMT", "openingDate": "2025-02-24T12:00:00Z", "closingDate": "2025-02-24T13:00:00Z", "resolutionDate": "2025-02-24T13:01:00Z" } ] ``` ```json 404 Not Found theme={null} { "error": "not_found", "message": "Series not found", "statusCode": 404 } ``` # Get liquidity rewards Source: https://docs.bayse.markets/api-reference/pm/liquidity-rewards GET /v1/pm/liquidity-rewards Get paginated list of the authenticated user's liquidity reward payouts ## Authentication Requires API key authentication with read scope. ## Query parameters Page number. Page size (max 100). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/liquidity-rewards?page=1&size=10" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/liquidity-rewards?page=1&size=10', { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/liquidity-rewards', params={'page': 1, 'size': 10}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/liquidity-rewards", nil) q := req.URL.Query() q.Add("page", "1") q.Add("size", "10") req.URL.RawQuery = q.Encode() req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response List of liquidity reward records. UUID of the reward epoch. UUID of the event. UUID of the market. Total liquidity shares accumulated during the epoch. Number of sampling intervals the user qualified for. USD payout amount for this epoch. Whether the payout has been credited. ISO 8601 timestamp when the epoch started. ISO 8601 timestamp when the epoch ended. Epoch status (`active`, `completed`). Current page number. Page size. Total number of records. Last page number. ```json 200 OK theme={null} { "data": [ { "epochId": "f4d15ea1-813a-41dd-a84c-e95648974dd6", "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "accumulatedShares": 1523.45, "sampleCount": 12, "payout": 8.25, "isPaid": true, "epochStart": "2026-03-20T00:00:00Z", "epochEnd": "2026-03-21T00:00:00Z", "status": "completed" } ], "pagination": { "page": 1, "size": 20, "totalCount": 1, "lastPage": 1 } } ``` # Get active liquidity rewards Source: https://docs.bayse.markets/api-reference/pm/liquidity-rewards-active GET /v1/pm/liquidity-rewards/active Get the authenticated user's in-progress reward accumulation across active epochs ## Authentication Requires API key authentication with read scope. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/liquidity-rewards/active" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/liquidity-rewards/active', { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/liquidity-rewards/active', headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/liquidity-rewards/active", nil) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response List of active reward accumulations (one per active epoch the user participates in). UUID of the reward epoch. UUID of the event. UUID of the market. Liquidity shares accumulated so far in this epoch. Number of sampling intervals the user has qualified for so far. Estimated USD payout based on current accumulated shares and reward pool. ISO 8601 timestamp when the epoch started. ISO 8601 timestamp when the epoch ends. ```json 200 OK theme={null} { "data": [ { "epochId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "marketId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "accumulatedShares": 842.10, "sampleCount": 5, "estimatedPayout": 3.50, "epochStart": "2026-03-22T00:00:00Z", "epochEnd": "2026-03-23T00:00:00Z" } ] } ``` # List events Source: https://docs.bayse.markets/api-reference/pm/list-events GET /v1/pm/events Get a paginated list of prediction market events ## Authentication Public — no authentication required. Provide `X-Public-Key` to receive personalized data (e.g. watchlist status). ## Query parameters Page number. Results per page. Filter by category (e.g. `sports`, `crypto`, `politics`). Filter by subcategory. Filter by status: `open`, `paused`, `closed`, `resolved`, `cancelled`. Defaults to `open` (includes `paused`) when omitted. Search events by title keyword. Currency for prices: `USD` or `NGN`. Return trending events only. Return watchlisted events only (requires `X-Public-Key`). Filter by event series slug (e.g. `crypto-btc-1h`). Filter by sports game slug to fetch all related events (H2H, Goal Spread, Total Goals) for a match (e.g. `bm-game-20260524-mci-avl`). Only applies to sports events. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/events?category=sports&status=open¤cy=USD&page=1&size=10" ``` ```javascript Node.js theme={null} const params = new URLSearchParams({ category: 'sports', status: 'open', currency: 'USD', page: 1, size: 10, }); const response = await fetch( `https://relay.bayse.markets/v1/pm/events?${params}` ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/events', params={ 'category': 'sports', 'status': 'open', 'currency': 'USD', 'page': 1, 'size': 10, }, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/events", nil) q := req.URL.Query() q.Add("category", "sports") q.Add("status", "open") q.Add("currency", "USD") q.Add("page", "1") q.Add("size", "10") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) ``` ## Response List of event objects. UUID of the event. Human-readable URL slug for the event. The prediction question. Additional context. Event category. `single` or `combined`. `AMM` or `CLOB`. Current status: `open`, `paused`, `closed`, `resolved`, or `cancelled`. ISO 8601 — when the event opens for trading. Present on time-boxed events (e.g. crypto). ISO 8601 — when the outcome is determined. ISO 8601 — when trading closes. Cover image URL. Total liquidity in the event. Total trading volume. Total number of orders placed. Currencies available for trading. Whether the authenticated user has watchlisted this event. Trading pair symbol (e.g. `BTCUSDT`). Present on crypto events. Opening/reference price for the event. Present on crypto events. Threshold range in format `"100-200"`. Present when the event covers a price range. Closing price at resolution. Present on resolved crypto events. Slug of the event series this event belongs to (e.g. `crypto-btc-1h`). ID of the sports game. Present on sports events. Slug of the sports game (e.g. `bm-game-20260524-mci-avl`). Use this to fetch all related events for the match. Type of sports market. Present on sports events. One of: `TEAM_H2H_3WAY` (match winner), `BOTH_TEAMS_TO_SCORE` (both teams score?), `FIRST_TEAM_TO_SCORE` (which team scores first?), `GOAL_SPREAD` (team wins by X+ goals), `TOTAL_GOALS` (total goals by both teams), `TOTAL_GOALS_HOME` (home team total goals), `TOTAL_GOALS_AWAY` (away team total goals), `TOTAL_CORNERS` (total corners, full match), `FIRST_HALF_CORNERS` (corners in 1st half), `SECOND_HALF_CORNERS` (corners in 2nd half). Sub-markets within the event. UUID of the market. Sub-market question. Market status. UUID of outcome 1. Label for outcome 1 (e.g. `YES`). Current probability price for outcome 1 (0.00–1.00). UUID of outcome 2. Label for outcome 2 (e.g. `NO`). Current probability price for outcome 2 (0.00–1.00). UUID of the resolved outcome. Present when the market is resolved. Effective buy price for outcome 1. Effective buy price for outcome 2. Minimum order amount for that market in the requested `currency`. While most markets have a minimum order amount of 1.00 USD or 100.00 NGN, individual markets can specify their own minimum order amounts. Use this field instead of hardcoding a single value. Trading fee as a percentage. Number of orders in this market. Resolution criteria. Opening/reference price for the market. Present on crypto markets. Threshold range in format `"100-200"`. Present when the market covers a price range. Closing price at resolution. Present on resolved crypto markets. The threshold value (e.g. 1.5 goals, 9.5 corners). Present on goal and corner prop markets. Directional label: `OVER` for prop markets. Present on goal and corner prop markets. Team details for team-specific markets. Present on Goal Spread, Total Goals Home/Away, and H2H home/away markets; `null` for match-level markets (Total Goals, corners, Both Teams to Score, H2H Draw, First Team to Score "Neither"). Team UUID. Full team name (e.g. `Manchester City FC`). URL-safe team slug (e.g. `manchester-city-fc`). League name (e.g. `England - Premier League`). Sport type (e.g. `SOCCER`). Liquidity reward program info. Present when the market has an active reward program. Total USD reward pool for the current epoch. Maximum bid-ask spread in cents to qualify for rewards. Minimum order size in USD to qualify for rewards. Current page. Results per page. Last available page. Total matching events. ```json 200 OK theme={null} { "events": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "slug": "super-eagles-afcon-2026", "title": "Will Super Eagles qualify for AFCON 2026?", "description": "Nigeria national football team qualification.", "category": "sports", "type": "single", "engine": "AMM", "status": "open", "resolutionDate": "2025-11-20T00:00:00Z", "closingDate": "2025-11-19T18:00:00Z", "imageUrl": "https://cdn.bayse.markets/events/afcon2026.jpg", "liquidity": 50000, "totalVolume": 120000, "totalOrders": 843, "supportedCurrencies": ["USD", "NGN"], "userWatchlisted": false, "markets": [ { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Will Super Eagles qualify?", "status": "open", "outcome1Id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "outcome1Label": "YES", "outcome1Price": 0.72, "outcome2Id": "d4e5f6a7-b8c9-0123-defa-234567890123", "outcome2Label": "NO", "outcome2Price": 0.28, "yesBuyPrice": 0.72, "minimumOrderAmount": 1, "noBuyPrice": 0.28, "feePercentage": 2.0, "totalOrders": 843, "rules": "Resolves YES if Nigeria qualifies for AFCON 2026." } ] }, { "id": "e5f6a7b8-c9d0-1234-efab-cd567890abcd", "slug": "mci-vs-avl-2026-05-24", "title": "Manchester City vs Aston Villa", "description": "Premier League match", "category": "sports", "type": "combined", "engine": "CLOB", "status": "open", "sportMarketType": "TEAM_H2H_3WAY", "sportGameId": "game-12345", "sportGameSlug": "bm-game-20260524-mci-avl", "resolutionDate": "2026-05-24T20:00:00Z", "closingDate": "2026-05-24T15:00:00Z", "imageUrl": "https://cdn.bayse.markets/events/mci-avl.jpg", "liquidity": 75000, "totalVolume": 250000, "totalOrders": 1250, "supportedCurrencies": ["USD"], "userWatchlisted": true, "markets": [ { "id": "f6a7b8c9-d0e1-2345-fabc-678901234567", "title": "Manchester City to Win", "status": "open", "outcome1Id": "01234567-89ab-cdef-0123-456789abcdef", "outcome1Label": "YES", "outcome1Price": 0.63, "outcome2Id": "fedcba98-7654-3210-fedc-ba9876543210", "outcome2Label": "NO", "outcome2Price": 0.37, "propTeam": { "id": "team-uuid-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" }, "yesBuyPrice": 0.63, "noBuyPrice": 0.37, "feePercentage": 0.5, "totalOrders": 420, "rules": "Resolves YES if Manchester City wins the match." }, { "id": "a7b8c9d0-e1f2-3456-abcd-789012345678", "title": "Aston Villa to Win", "status": "open", "outcome1Id": "12345678-9abc-def0-1234-56789abcdef0", "outcome1Label": "YES", "outcome1Price": 0.63, "outcome2Id": "0fedcba9-8765-4321-0fed-cba987654321", "outcome2Label": "NO", "outcome2Price": 0.37, "propTeam": { "id": "team-uuid-2", "name": "Aston Villa FC", "slug": "aston-villa-fc", "league": "England - Premier League", "sport": "SOCCER" }, "yesBuyPrice": 0.63, "noBuyPrice": 0.37, "feePercentage": 0.5, "totalOrders": 420, "rules": "Resolves YES if Aston Villa wins the match." }, { "id": "b8c9d0e1-f234-5678-bcde-890123456789", "title": "Draw", "status": "open", "outcome1Id": "23456789-abcd-ef01-2345-6789abcdef01", "outcome1Label": "YES", "outcome1Price": 0.63, "outcome2Id": "1fedcba9-8765-4321-1fed-cba987654321", "outcome2Label": "NO", "outcome2Price": 0.37, "propTeam": null, "yesBuyPrice": 0.63, "noBuyPrice": 0.37, "feePercentage": 0.5, "totalOrders": 420, "rules": "Resolves YES if match ends in a draw." }, ] } ], "pagination": { "page": 1, "size": 10, "lastPage": 3, "totalCount": 28 } } ``` # List orders Source: https://docs.bayse.markets/api-reference/pm/list-orders GET /v1/pm/orders Get a paginated list of your orders ## Authentication Read authentication required — `X-Public-Key` header. ## Query parameters Filter by side: `BUY` or `SELL`. Filter by status: `open`, `filled`, `partial_filled`, `cancelled`, `expired`, `rejected`. Filter by event UUID. Filter by market UUID. Filter by outcome UUID. Filter by currency: `USD` or `NGN`. Page number. Results per page. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/orders?status=open&page=1&size=20" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/orders?status=open', { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/orders', params={'status': 'open', 'page': 1, 'size': 20}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/orders", nil) q := req.URL.Query() q.Add("status", "open") req.URL.RawQuery = q.Encode() req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a paginated list of orders. Each order has the same shape as the response from [Place order](/api-reference/pm/place-order) — either an AMM order or a CLOB order depending on the market. ```json 200 OK theme={null} { "orders": [ { "id": "f6a7b8c9-d0e1-2345-fabc-678901234567", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "outcome": "YES", "side": "BUY", "orderType": "LIMIT", "stpMode": "SKIP", "status": "open", "amount": 100, "price": 0.70, "size": 100, "filledSize": 0, "remainingSize": 100, "currency": "USD", "createdAt": "2026-02-17T12:00:00Z", "updatedAt": "2026-02-17T12:00:00Z" } ], "pagination": { "page": 1, "size": 20, "lastPage": 1, "totalCount": 1 } } ``` # List event series Source: https://docs.bayse.markets/api-reference/pm/list-series GET /v1/pm/events/series Get a paginated list of event series ## Authentication Public — no authentication required. ## Query parameters Page number. Results per page (max 100). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/events/series?page=1&size=20" ``` ```javascript Node.js theme={null} const params = new URLSearchParams({ page: 1, size: 20 }); const response = await fetch( `https://relay.bayse.markets/v1/pm/events/series?${params}` ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/events/series', params={'page': 1, 'size': 20}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/events/series", nil) q := req.URL.Query() q.Add("page", "1") q.Add("size", "20") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) ``` ## Response List of event series objects. UUID of the series. Unique slug identifier (e.g. `crypto-btc-1h`). Human-readable name for the series. Description of the series. Category (e.g. `CRYPTO`). Time interval between events: `FIFTEEN_MINUTE`, `HOURLY`, `SIX_HOURLY`, or `DAILY`. Asset symbol (e.g. `BTC`, `ETH`, `SOL`). Automation type identifier. URL to the series icon. Current page. Results per page. Last available page. Total number of series. ```json 200 OK theme={null} { "series": [ { "id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", "slug": "crypto-btc-1h", "displayName": "Bitcoin Hourly Markets", "description": "Bitcoin price prediction markets that run every hour.", "category": "CRYPTO", "intervalType": "HOURLY", "assetSymbol": "BTC", "automationType": "CRYPTO_PRICE_UP_DOWN_HOURLY" }, { "id": "e2d3c4b5-a697-8901-bcde-f12345678901", "slug": "crypto-eth-1d", "displayName": "Ethereum Daily Markets", "description": "Ethereum price prediction markets that run daily.", "category": "CRYPTO", "intervalType": "DAILY", "assetSymbol": "ETH", "automationType": "CRYPTO_PRICE_UP_DOWN_DAILY" } ], "pagination": { "page": 1, "size": 20, "lastPage": 1, "totalCount": 12 } } ``` # List sports games Source: https://docs.bayse.markets/api-reference/pm/list-sports-games GET /v1/pm/sports/games Get a paginated list of sports games, optionally filtered by league or sport ## Authentication Public — no authentication required. ## Query parameters Filter by league key (e.g., "England - Premier League", "Spain - La Liga"). Filter by sport (e.g., "soccer", "basketball"). Page number. Results per page (max 100). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/sports/games?league=epl&page=1&size=20" ``` ```javascript Node.js theme={null} const params = new URLSearchParams({ league: 'England - Premier League', page: 1, size: 20, }); const response = await fetch( `https://relay.bayse.markets/v1/pm/sports/games?${params}` ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/sports/games', params={'league': 'epl', 'page': 1, 'size': 20}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/sports/games", nil) q := req.URL.Query() q.Add("league", "epl") q.Add("page", "1") q.Add("size", "20") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() var data interface{} json.NewDecoder(resp.Body).Decode(&data) ``` ## Response Array of games matching the filter criteria. Unique identifier for the team. Sport type (e.g., "bm-game-20260413-rma-ath"). Sport type (e.g., "soccer", "basketball"). URL-friendly team identifier. URL-friendly team identifier. Start Date of the sport game League name. Boolean field to indicate if game is currently live. Whether this is a popular/featured team. Home team details. Unique identifier for the team. Sport type (e.g., "soccer", "basketball"). Full team name. URL-friendly team identifier. Team short code. League name. URL to team logo (if available). Whether this is a popular/featured team. Away team details. Unique identifier for the team. Sport type (e.g., "soccer", "basketball"). Full team name. URL-friendly team identifier. Team short code. League name. URL to team logo (if available). Whether this is a popular/featured team. Pagination information. Current page number. Results per page. Total number of games matching the filter. Last available page number. ```json 200 OK theme={null} { "games": [ { "id": "9cdc0e3e-2e72-4076-bf8c-d941272f1a4f", "slug": "bm-game-20260413-sea-rsl", "sport": "soccer", "homeTeamId": "44a72373-18df-4770-a475-32f4ade2b9bd", "awayTeamId": "c4c564c6-bc66-49c7-90be-3199aea1a593", "startDate": "2026-04-13T02:00:00+01:00", "status": "UNPLAYED", "isLive": false, "isPopular": false, "league": "USA - Major League Soccer", "homeTeam": { "id": "44a72373-18df-4770-a475-32f4ade2b9bd", "sport": "soccer", "name": "Seattle Sounders FC", "slug": "bm-mls-seattle-sounders-fc", "shortCode": "SEA", "league": "USA - Major League Soccer", "imageUrl": "https://cdn.opticodds.com/team-logos/soccer/5272.png", "isPopular": false }, "awayTeam": { "id": "c4c564c6-bc66-49c7-90be-3199aea1a593", "sport": "soccer", "name": "Real Salt Lake", "slug": "bm-mls-real-salt-lake", "shortCode": "RSL", "league": "USA - Major League Soccer", "imageUrl": "https://cdn.opticodds.com/team-logos/soccer/5270.png", "isPopular": false } } ], "pagination": { "page": 1, "size": 20, "totalCount": 120, "lastPage": 6 } } ``` # List sports leagues Source: https://docs.bayse.markets/api-reference/pm/list-sports-leagues GET /v1/pm/sports/leagues Get a list of all supported sports leagues ## Authentication Public — no authentication required. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/sports/leagues" ``` ```javascript Node.js theme={null} const response = await fetch( `https://relay.bayse.markets/v1/pm/sports/leagues` ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/sports/leagues', ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/sports/leagues", nil) resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() var data interface{} json.NewDecoder(resp.Body).Decode(&data) ``` ## Response Array of supported sports leagues. Full league name (e.g., "England - Premier League"). Short display name (e.g., "EPL"). URL to the league logo image. ```json 200 OK theme={null} { "leagues": [ { "name": "England - Premier League", "key": "epl", "shortName": "EPL", "imageUrl": "https://assets.bayse.markets/event-images/leagues/epl.png" }, { "name": "UEFA - Champions League", "key": "ucl", "shortName": "UCL", "imageUrl": "https://assets.bayse.markets/event-images/leagues/ucl.png" }, { "name": "Spain - La Liga", "key": "laliga", "shortName": "La Liga", "imageUrl": "https://assets.bayse.markets/event-images/leagues/laliga.png" } ] } ``` # List sports teams Source: https://docs.bayse.markets/api-reference/pm/list-sports-teams GET /v1/pm/sports/teams Get a paginated list of sports teams, optionally filtered by league or sport ## Authentication Public — no authentication required. ## Query parameters Filter by league key (e.g., "England - Premier League", "Spain - La Liga"). Filter by sport (e.g., "soccer", "basketball"). Page number. Results per page (max 100). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/sports/teams?league=epl&page=1&size=20" ``` ```javascript Node.js theme={null} const params = new URLSearchParams({ league: 'England - Premier League', page: 1, size: 20, }); const response = await fetch( `https://relay.bayse.markets/v1/pm/sports/teams?${params}` ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/sports/teams', params={'league': 'epl', 'page': 1, 'size': 20}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/sports/teams", nil) q := req.URL.Query() q.Add("league", "epl") q.Add("page", "1") q.Add("size", "20") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() var data interface{} json.NewDecoder(resp.Body).Decode(&data) ``` ## Response Array of teams matching the filter criteria. Unique identifier for the team. Sport type (e.g., "soccer", "basketball"). Full team name. URL-friendly team identifier. Team short code. League name. URL to team logo (if available). Whether this is a popular/featured team. Pagination information. Current page number. Results per page. Total number of teams matching the filter. Last available page number. ```json 200 OK theme={null} { "teams": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "sport": "soccer", "name": "Arsenal FC", "slug": "bm-arsenal-fc", "shortCode": "ARS", "league": "England - Premier League", "imageUrl": "https://assets.bayse.markets/teams/arsenal.png", "isPopular": true }, { "id": "223e4567-e89b-12d3-a456-426614174001", "sport": "soccer", "name": "Manchester City", "slug": "bm-manchester-city-fc", "shortCode": "MCI", "league": "England - Premier League", "imageUrl": "https://assets.bayse.markets/teams/manchester-city.png", "isPopular": true }, { "id": "323e4567-e89b-12d3-a456-426614174002", "sport": "soccer", "name": "Liverpool FC", "slug": "bm-liverpool-fc", "shortCode": "LIV", "league": "England - Premier League", "imageUrl": "https://assets.bayse.markets/teams/liverpool.png", "isPopular": true } ], "pagination": { "page": 1, "size": 20, "totalCount": 120, "lastPage": 6 } } ``` # Get maker rebates Source: https://docs.bayse.markets/api-reference/pm/maker-rebates GET /v1/pm/maker-rebates Get paginated list of the authenticated user's maker rebate payouts ## Authentication Requires API key authentication with read scope. ## Query parameters Page number. Page size (max 100). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/maker-rebates?page=1&size=10" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/maker-rebates?page=1&size=10', { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/maker-rebates', params={'page': 1, 'size': 10}, headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/maker-rebates", nil) q := req.URL.Query() q.Add("page", "1") q.Add("size", "10") req.URL.RawQuery = q.Encode() req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response List of maker rebate payout records. UUID of the rebate epoch. UUID of the event. UUID of the market. Your total maker volume (price x size) during the epoch. Number of trades where your resting orders were filled. USD rebate amount for this epoch. Whether the rebate has been credited to your wallet. ISO 8601 timestamp when the epoch started. ISO 8601 timestamp when the epoch ended. Epoch status (`ACTIVE` or `FINALIZED`). Current page number. Page size. Total number of records. Last page number. ```json 200 OK theme={null} { "data": [ { "epochId": "f4d15ea1-813a-41dd-a84c-e95648974dd6", "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "makerVolume": 5230.50, "tradeCount": 47, "rebateAmount": 12.75, "isPaid": true, "epochStart": "2026-04-14T00:00:00Z", "epochEnd": "2026-04-15T00:00:00Z", "status": "FINALIZED" } ], "pagination": { "page": 1, "size": 20, "totalCount": 1, "lastPage": 1 } } ``` # Get active maker rebates Source: https://docs.bayse.markets/api-reference/pm/maker-rebates-active GET /v1/pm/maker-rebates/active Get the authenticated user's in-progress maker rebate accumulation across active epochs ## Authentication Requires API key authentication with read scope. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/maker-rebates/active" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/maker-rebates/active', { headers: { 'X-Public-Key': 'pk_live_abcdef123456' } } ); const data = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/maker-rebates/active', headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/maker-rebates/active", nil) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response List of active rebate accumulations (one per active epoch the user has maker volume in). UUID of the rebate epoch. UUID of the event. UUID of the market. Your maker volume so far in this epoch. Number of trades where your resting orders were filled so far. Estimated USD rebate based on current maker volume share and rebate pool. ISO 8601 timestamp when the epoch started. ISO 8601 timestamp when the epoch ends. ```json 200 OK theme={null} { "data": [ { "epochId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "eventId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "marketId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "makerVolume": 2150.00, "tradeCount": 18, "rebateAmount": 5.40, "epochStart": "2026-04-15T00:00:00Z", "epochEnd": "2026-04-16T00:00:00Z" } ] } ``` # Mint shares Source: https://docs.bayse.markets/api-reference/pm/mint-shares POST /v1/pm/markets/{marketId}/mint Deposit funds and receive equal YES and NO shares for a market Minting creates new shares for a binary market. You deposit funds and receive an equal number of YES and NO shares (a complementary pair). The cost per pair equals the market's base unit (e.g., \$1.00 per YES+NO pair in USD markets). Minting does not affect market prices since it creates both sides equally. This is useful when you want to sell one side on the order book while keeping the other, or when you want to provide liquidity. ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Path parameters UUID of the market. ## Request body Amount to use for the mint operation in the selected `currency`. For example, `quantity: 100, currency: "NGN"` means "spend 100 NGN to mint". Must be greater than 0. `USD` (default) or `NGN`. Request `quantity` is a wallet amount in the provided currency. The response `quantity` is the normalized share quantity minted. ## Example request ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/mint" BODY='{"quantity":10,"currency":"USD"}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'POST'; const path = '/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/mint'; const body = JSON.stringify({ quantity: 10, currency: 'USD', }); const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Content-Type': 'application/json', }, body, }); const result = await response.json(); ``` ```python Python theme={null} import hmac, hashlib, base64, json, time, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'POST' path = '/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/mint' body = json.dumps({ 'quantity': 10, 'currency': 'USD', }) body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() resp = requests.post( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Content-Type': 'application/json', }, data=body, ) result = resp.json() ``` ```go Go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "strconv" "strings" "time" ) timestamp := time.Now().Unix() method := "POST" urlPath := "/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/mint" body := []byte(`{"quantity":10,"currency":"USD"}`) bodySum := sha256.Sum256(body) bodyHash := hex.EncodeToString(bodySum[:]) payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, urlPath, bodyHash) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("POST", "https://relay.bayse.markets"+urlPath, strings.NewReader(string(body))) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response UUID of the mint operation. UUID of the market. Normalized share quantity minted. Current price of outcome 1 (YES). Returned for convenience — minting does not change market prices. Current price of outcome 2 (NO). Returned for convenience — minting does not change market prices. ```json 200 OK theme={null} { "operationId": "d4e5f6a7-b8c9-0123-defa-456789012345", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "quantity": 10, "outcome1Price": 0.65, "outcome2Price": 0.35 } ``` # Order book Source: https://docs.bayse.markets/api-reference/pm/order-book GET /v1/pm/books Get the live order book for one or more outcomes (CLOB markets only) ## Authentication No authentication required. ## Query parameters One or more outcome UUIDs to fetch order books for (comma-separated). Outcome IDs are returned by [List events](/api-reference/pm/list-events) and [Get event](/api-reference/pm/get-event). Number of price levels to return on each side of the book. Currency for price display: `USD` or `NGN`. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/books?outcomeId[]=c3d4e5f6-a7b8-9012-cdef-123456789012&depth=5¤cy=USD" ``` ```javascript Node.js theme={null} const params = new URLSearchParams(); params.append('outcomeId[]', 'c3d4e5f6-a7b8-9012-cdef-123456789012'); params.append('outcomeId[]', 'd4e5f6a7-b8c9-0123-defa-234567890123'); params.append('depth', '5'); const response = await fetch( `https://relay.bayse.markets/v1/pm/books?${params}` ); const books = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/books', params={ 'outcomeId[]': [ 'c3d4e5f6-a7b8-9012-cdef-123456789012', 'd4e5f6a7-b8c9-0123-defa-234567890123', ], 'depth': 5, 'currency': 'USD', }, ) books = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/books", nil) q := req.URL.Query() q.Add("outcomeId[]", "c3d4e5f6-a7b8-9012-cdef-123456789012") q.Add("outcomeId[]", "d4e5f6a7-b8c9-0123-defa-234567890123") q.Add("depth", "5") q.Add("currency", "USD") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns an array of order books, one per requested outcome. Each book contains the top bids (buy orders) and asks (sell orders) at the requested depth. The market ID this order book belongs to. The outcome ID this order book is for. ISO 8601 timestamp of the order book snapshot. Buy orders sorted by price (highest first). Each entry has `price`, `quantity`, and `total`. Sell orders sorted by price (lowest first). Each entry has `price`, `quantity`, and `total`. The last traded price for this outcome (if available). The side of the last trade: `BUY` or `SELL` (if available). ```json 200 OK theme={null} [ { "marketId": "660e8400-e29b-41d4-a716-446655440001", "outcomeId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "timestamp": "2025-01-15T10:30:00Z", "bids": [ { "price": 0.70, "quantity": 200, "total": 140.0 }, { "price": 0.69, "quantity": 450, "total": 310.5 }, { "price": 0.68, "quantity": 300, "total": 204.0 }, { "price": 0.67, "quantity": 600, "total": 402.0 }, { "price": 0.65, "quantity": 1000, "total": 650.0 } ], "asks": [ { "price": 0.72, "quantity": 150, "total": 108.0 }, { "price": 0.73, "quantity": 300, "total": 219.0 }, { "price": 0.74, "quantity": 250, "total": 185.0 }, { "price": 0.75, "quantity": 500, "total": 375.0 }, { "price": 0.77, "quantity": 800, "total": 616.0 } ], "lastTradedPrice": 0.71, "lastTradedSide": "BUY" } ] ``` This endpoint is only meaningful for CLOB markets. AMM markets do not have an order book. # Place order Source: https://docs.bayse.markets/api-reference/pm/place-order POST /v1/pm/events/{eventId}/markets/{marketId}/orders Place a buy or sell order on a prediction market ## Authentication Write authentication required — `X-Public-Key`, `X-Timestamp`, and `X-Signature` headers. See the [Authentication guide](/authentication). ## Path parameters UUID of the event. UUID of the market. ## Request body `BUY` or `SELL`. UUID of the outcome. Use the [Get Event](/api-reference/pm/get-event) endpoint to find outcome IDs. Amount to spend (buy) or receive (sell), in the specified currency. Minimum: \$1.00 USD / ₦100.00 NGN. `LIMIT` or `MARKET`. `USD` (default) or `NGN`. Limit price per share (0.01–0.99). Required for `LIMIT` orders. `GTC` (good-til-cancelled, default for limit), `GTD` (good-til-date), `FAK` (fill-and-kill, default for market), or `FOK` (fill-or-kill). If `true`, the order is rejected instead of crossing the spread. Limit orders only. Default: `false`. Self-trade prevention mode. Controls how the engine resolves a match against another resting order from the same user. CLOB only. Default: `SKIP`. Unknown values fall back to `SKIP`. * `SKIP` — the match is silently skipped and both orders remain on the book. * `CANCEL_OLDEST` — the resting same-user maker is cancelled and refunded; the taker continues matching against other counterparties. * `CANCEL_NEWEST` — the incoming taker stops at the same-user match. If it had already filled against other users, those fills stand and the taker comes back as `cancelled`; otherwise it is `rejected`. * `CANCEL_BOTH` — the resting maker is cancelled and the taker is cancelled or rejected under the same rule as `CANCEL_NEWEST`. Maximum acceptable slippage for market orders (0.00–1.00). ISO 8601 expiration timestamp. Required for `GTD` orders. ## Example request ```bash cURL (market order) theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/orders" BODY='{"side":"BUY","outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","amount":100,"type":"MARKET","currency":"USD"}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```bash cURL (limit order) theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/orders" BODY='{"side":"BUY","outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","amount":100,"type":"LIMIT","price":0.70,"currency":"USD"}' BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const publicKey = 'pk_live_abcdef123456'; const secretKey = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'POST'; const path = '/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/orders'; const body = JSON.stringify({ side: 'BUY', outcomeId: 'c3d4e5f6-a7b8-9012-cdef-345678901234', amount: 100, type: 'LIMIT', price: 0.70, currency: 'USD', }); const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(payload) .digest('base64'); const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Content-Type': 'application/json', }, body, }); const order = await response.json(); ``` ```python Python theme={null} import hmac, hashlib, base64, json, time, requests public_key = 'pk_live_abcdef123456' secret_key = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'POST' path = '/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/orders' body = json.dumps({ 'side': 'BUY', 'outcomeId': 'c3d4e5f6-a7b8-9012-cdef-345678901234', 'amount': 100, 'type': 'LIMIT', 'price': 0.70, 'currency': 'USD', }) body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).digest() ).decode() resp = requests.post( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Content-Type': 'application/json', }, data=body, ) order = resp.json() ``` ```go Go theme={null} import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "strconv" "strings" "time" ) timestamp := time.Now().Unix() method := "POST" urlPath := "/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/orders" body := []byte(`{"side":"BUY","outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","amount":100,"type":"LIMIT","price":0.70,"currency":"USD"}`) bodySum := sha256.Sum256(body) bodyHash := hex.EncodeToString(bodySum[:]) payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, urlPath, bodyHash) mac := hmac.New(sha256.New, []byte("sk_live_secret789xyz")) mac.Write([]byte(payload)) signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest("POST", "https://relay.bayse.markets"+urlPath, strings.NewReader(string(body))) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") req.Header.Set("X-Timestamp", strconv.FormatInt(timestamp, 10)) req.Header.Set("X-Signature", signature) req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response The response contains an `engine` field indicating the market type, and an `order` object with the order details. `AMM` or `CLOB`. The placed order. Fields vary by engine type. Order UUID. `YES` or `NO`. `BUY` or `SELL`. Order type. Order status. Amount spent. Average fill price. Shares received. Currency used. ISO 8601 timestamp. ISO 8601 timestamp. Order UUID. Market UUID. User UUID. `YES` or `NO`. `BUY` or `SELL`. `LIMIT` or `MARKET`. Order type. Order status: `pending`, `open`, `partial_filled`, `filled`, `cancelled`, `rejected`, or `expired`. Requested amount. Limit price. Total size. Amount filled so far. Amount remaining on the book. Average price of fills so far. Fee charged. Whether the order is post-only. Self-trade prevention mode applied: `SKIP`, `CANCEL_OLDEST`, `CANCEL_NEWEST`, or `CANCEL_BOTH`. Shares received. ISO 8601 timestamp. ISO 8601 timestamp. ```json 200 OK — AMM theme={null} { "engine": "AMM", "order": { "id": "e5f6a7b8-c9d0-1234-efab-567890123456", "outcome": "YES", "side": "BUY", "type": "MARKET", "status": "filled", "amount": 100, "price": 0.7235, "quantity": 138.21, "currency": "USD", "createdAt": "2026-02-17T12:00:00Z", "updatedAt": "2026-02-17T12:00:00Z" } } ``` ```json 200 OK — CLOB theme={null} { "engine": "CLOB", "order": { "id": "f6a7b8c9-d0e1-2345-fabc-678901234567", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "userId": "9a8b7c6d-5e4f-3210-abcd-ef1234567890", "outcome": "YES", "side": "BUY", "orderType": "LIMIT", "stpMode": "SKIP", "type": "GTC", "status": "open", "amount": 100, "price": 0.70, "size": 100, "filledSize": 0, "remainingSize": 100, "avgFillPrice": 0, "fee": 0, "postOnly": false, "quantity": 0, "createdAt": "2026-02-17T12:00:00Z", "updatedAt": "2026-02-17T12:00:00Z" } } ``` ## Replacing market-maker quotes with `stpMode` A market maker re-quoting on both sides of a CLOB book risks crossing its own resting orders mid-update. Setting `stpMode` to `CANCEL_OLDEST` cancels the stale same-user maker server-side as the new quote arrives, so the new order can continue matching against external counterparties without sitting next to a duplicate of itself. ```bash cURL theme={null} BODY='{"side":"BUY","outcomeId":"c3d4e5f6-a7b8-9012-cdef-345678901234","amount":100,"type":"LIMIT","price":0.71,"timeInForce":"GTC","stpMode":"CANCEL_OLDEST","currency":"USD"}' curl -X POST "https://relay.bayse.markets/v1/pm/events/${EVENT_ID}/markets/${MARKET_ID}/orders" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` # Price history Source: https://docs.bayse.markets/api-reference/pm/price-history GET /v1/pm/events/{eventId}/price-history Get historical price data for a prediction market event ## Authentication No authentication required. ## Path parameters UUID of the event. ## Query parameters Time window for the history. One of: `12H`, `24H`, `1W`, `1M`, `1Y`. Filter to specific market UUIDs (comma-separated). Omit to return all markets in the event. Filter to a specific outcome: `YES` or `NO`. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/price-history?timePeriod=1W&outcome=YES" ``` ```javascript Node.js theme={null} const eventId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'; const response = await fetch( `https://relay.bayse.markets/v1/pm/events/${eventId}/price-history?timePeriod=1W&outcome=YES` ); const data = await response.json(); ``` ```python Python theme={null} import requests event_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' resp = requests.get( f'https://relay.bayse.markets/v1/pm/events/{event_id}/price-history', params={'timePeriod': '1W', 'outcome': 'YES'}, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "GET", "https://relay.bayse.markets/v1/pm/events/a1b2c3d4-e5f6-7890-abcd-ef1234567890/price-history", nil, ) q := req.URL.Query() q.Add("timePeriod", "1W") q.Add("outcome", "YES") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a map of market IDs to their price history arrays. ```json 200 OK theme={null} { "b2c3d4e5-f6a7-8901-bcde-f12345678901": [ { "outcome": "YES", "price": 0.65, "timestamp": "2026-02-10T00:00:00Z" }, { "outcome": "YES", "price": 0.68, "timestamp": "2026-02-11T00:00:00Z" }, { "outcome": "YES", "price": 0.72, "timestamp": "2026-02-17T00:00:00Z" } ] } ``` # Ticker Source: https://docs.bayse.markets/api-reference/pm/ticker GET /v1/pm/markets/{marketId}/ticker Get real-time price and volume statistics for a market outcome ## Authentication No authentication required. ## Path parameters UUID of the market. ## Query parameters The outcome label: `YES` or `NO`. Required if `outcomeId` is not provided. UUID of the outcome. Must belong to the specified market. Required if `outcome` is not provided. ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/ticker?outcome=YES" ``` ```javascript Node.js theme={null} const marketId = 'b2c3d4e5-f6a7-8901-bcde-f12345678901'; const response = await fetch( `https://relay.bayse.markets/v1/pm/markets/${marketId}/ticker?outcome=YES` ); const ticker = await response.json(); ``` ```python Python theme={null} import requests market_id = 'b2c3d4e5-f6a7-8901-bcde-f12345678901' resp = requests.get( f'https://relay.bayse.markets/v1/pm/markets/{market_id}/ticker', params={'outcome': 'YES'}, ) ticker = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "GET", "https://relay.bayse.markets/v1/pm/markets/b2c3d4e5-f6a7-8901-bcde-f12345678901/ticker", nil, ) q := req.URL.Query() q.Add("outcome", "YES") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns current market statistics for the requested outcome. ```json 200 OK theme={null} { "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "outcome": "YES", "lastPrice": 0.72, "bestBid": 0.70, "bestAsk": 0.72, "midPrice": 0.71, "spread": 0.02, "volume24h": 15420, "high24h": 0.74, "low24h": 0.65, "priceChange24h": 0.04, "tradeCount24h": 247, "timestamp": "2026-02-17T12:00:00Z" } ``` # Trades Source: https://docs.bayse.markets/api-reference/pm/trades GET /v1/pm/trades Get recent executed trades (CLOB markets only) ## Authentication No authentication required. ## Query parameters Filter by market UUID. Filter to a specific trade UUID. Filter by order UUID. Matches trades where the order is on either the taker or maker side. Filter by outcome UUID. Matches trades on either the taker or maker side. Filter by user UUID. Matches trades where the user was either the taker or the maker. Only return trades created at or after this RFC3339 timestamp (e.g. `2026-02-17T00:00:00Z`). Only return trades created at or before this RFC3339 timestamp (e.g. `2026-02-17T23:59:59Z`). Page number (1-indexed). Number of trades per page (max 100). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/pm/trades?marketId=b2c3d4e5-f6a7-8901-bcde-f12345678901&page=1&size=20" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/pm/trades?marketId=b2c3d4e5-f6a7-8901-bcde-f12345678901&page=1&size=20' ); const { data, pagination } = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/pm/trades', params={ 'marketId': 'b2c3d4e5-f6a7-8901-bcde-f12345678901', 'page': 1, 'size': 20, }, ) body = resp.json() trades = body['data'] pagination = body['pagination'] ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/pm/trades", nil) q := req.URL.Query() q.Add("marketId", "b2c3d4e5-f6a7-8901-bcde-f12345678901") q.Add("page", "1") q.Add("size", "20") req.URL.RawQuery = q.Encode() resp, _ := http.DefaultClient.Do(req) ``` ## Response Returns a paginated list of recently executed trades, most recent first. Trades are in `data`, with pagination metadata in `pagination`. ```json 200 OK theme={null} { "data": [ { "id": "t1a2b3c4-d5e6-7890-abcd-ef1234567890", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "outcome": "YES", "price": 0.72, "size": 100, "createdAt": "2026-02-17T12:00:01Z" }, { "id": "t2b3c4d5-e6f7-8901-bcde-f12345678901", "marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "outcome": "NO", "price": 0.28, "size": 250, "createdAt": "2026-02-17T11:59:45Z" } ], "pagination": { "page": 1, "size": 20, "totalCount": 2, "lastPage": 1 } } ``` This endpoint returns trades from CLOB markets only. AMM market trades are not included. # Health check Source: https://docs.bayse.markets/api-reference/system/health GET /health Check if the API is running No authentication required. ## Response Always `"ok"` when the service is running. ```json 200 OK theme={null} { "status": "ok" } ``` This endpoint checks that the API process is alive. It does not verify downstream service connectivity. # Version Source: https://docs.bayse.markets/api-reference/system/version GET /version Get the current deployed version No authentication required. ## Response Git commit SHA of the running build. Returns `"unknown"` if built without version info. ```json 200 OK theme={null} { "version": "a1b2c3d4e5f6789012345678901234567890abcd" } ``` # Create API key Source: https://docs.bayse.markets/api-reference/user/create-api-key POST /v1/user/me/api-keys Create a new API key pair for programmatic access ## Authentication Requires `x-auth-token` and `x-device-id` headers. Obtain these by calling the [login endpoint](/api-reference/user/login) with your Bayse account credentials. You can also create API keys in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys), or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. Use this endpoint when you want to create keys programmatically. ## Request body A descriptive label for this key (e.g. `"Production"`, `"Trading bot"`). Each API key must have a unique name in your account. ## Example request ```bash cURL theme={null} curl -X POST https://relay.bayse.markets/v1/user/me/api-keys \ -H "x-auth-token: YOUR_AUTH_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" \ -H "Content-Type: application/json" \ -d '{"name": "Trading bot"}' ``` ```javascript Node.js theme={null} const response = await fetch('https://relay.bayse.markets/v1/user/me/api-keys', { method: 'POST', headers: { 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Trading bot' }), }); const { publicKey, secretKey } = await response.json(); ``` ```python Python theme={null} import requests resp = requests.post( 'https://relay.bayse.markets/v1/user/me/api-keys', headers={ 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, json={'name': 'Trading bot'}, ) data = resp.json() ``` ```go Go theme={null} body := strings.NewReader(`{"name":"Trading bot"}`) req, _ := http.NewRequest("POST", "https://relay.bayse.markets/v1/user/me/api-keys", body) req.Header.Set("x-auth-token", "YOUR_AUTH_TOKEN") req.Header.Set("x-device-id", "YOUR_DEVICE_ID") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response Unique identifier for this API key. The name you provided. Public key (`pk_*`) — include this in the `X-Public-Key` header on all authenticated requests. Secret key (`sk_*`) — use this to generate HMAC signatures. **Only returned on creation.** Instructions for generating valid request signatures. Signing algorithm — `"HMAC-SHA256"`. Headers required for write authentication. Format of the value to sign — `"{timestamp}.{method}.{path}.{bodyHash}"`. Maximum age of a timestamp before the signature is rejected. ISO 8601 creation timestamp. ```json 201 Created theme={null} { "id": "3f7a1b2c-d4e5-6789-abcd-ef0123456789", "name": "Trading bot", "publicKey": "pk_live_abcdef123456", "secretKey": "sk_live_secret789xyz", "signingInstructions": { "algorithm": "HMAC-SHA256", "headers": ["X-Public-Key", "X-Timestamp", "X-Signature"], "payloadFormat": "{timestamp}.{method}.{path}.{bodyHash}", "timestampWindowSeconds": 300 }, "createdAt": "2026-02-17T10:30:00Z" } ``` The `secretKey` is only shown once. Store it securely — it cannot be retrieved again. If lost, rotate the key to generate a new one. # List API keys Source: https://docs.bayse.markets/api-reference/user/list-api-keys GET /v1/user/me/api-keys List all active API keys for your account ## Authentication Requires `x-auth-token` and `x-device-id` headers. Obtain these by calling the [login endpoint](/api-reference/user/login). ## Example request ```bash cURL theme={null} curl https://relay.bayse.markets/v1/user/me/api-keys \ -H "x-auth-token: YOUR_AUTH_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" ``` ```javascript Node.js theme={null} const response = await fetch('https://relay.bayse.markets/v1/user/me/api-keys', { headers: { 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, }); const { keys, total } = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/user/me/api-keys', headers={ 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/user/me/api-keys", nil) req.Header.Set("x-auth-token", "YOUR_AUTH_TOKEN") req.Header.Set("x-device-id", "YOUR_DEVICE_ID") resp, _ := http.DefaultClient.Do(req) ``` ## Response List of API key objects. Unique identifier for the key. Label assigned at creation. The public key (`pk_*`). Partial hint of the secret key for identification. The full secret key is never returned after creation. ISO 8601 creation timestamp. Total number of active keys. ```json 200 OK theme={null} { "keys": [ { "id": "3f7a1b2c-d4e5-6789-abcd-ef0123456789", "name": "Trading bot", "publicKey": "pk_live_abcdef123456", "secretKeyHint": "sk_live_...xyz", "createdAt": "2026-02-17T10:30:00Z" } ], "total": 1 } ``` # Login Source: https://docs.bayse.markets/api-reference/user/login POST /v1/user/login Authenticate with your Bayse account to get a session token ## Overview Log in with your Bayse account credentials to obtain a session token and device ID. These are required to manage API keys (create, list, revoke, rotate). Use this endpoint when you want to manage API keys programmatically. If you prefer a UI, you can also manage API keys in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys), or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. This endpoint is rate-limited to **1 request per 2 minutes** per email address. See [Rate limits](/rate-limits) for details. ## Request body Your Bayse account email address. Your Bayse account password (max 128 characters). ## Example request ```bash cURL theme={null} curl -X POST https://relay.bayse.markets/v1/user/login \ -H "Content-Type: application/json" \ -d '{ "email": "you@example.com", "password": "your-password" }' ``` ```javascript Node.js theme={null} const response = await fetch('https://relay.bayse.markets/v1/user/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'you@example.com', password: 'your-password', }), }); const { token, deviceId } = await response.json(); ``` ```python Python theme={null} import requests resp = requests.post( 'https://relay.bayse.markets/v1/user/login', json={ 'email': 'you@example.com', 'password': 'your-password', }, ) data = resp.json() token = data['token'] device_id = data['deviceId'] ``` ```go Go theme={null} body := strings.NewReader(`{"email":"you@example.com","password":"your-password"}`) req, _ := http.NewRequest("POST", "https://relay.bayse.markets/v1/user/login", body) req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) ``` ## Response Session token. Pass this as the `x-auth-token` header when managing API keys. Device identifier. Pass this as the `x-device-id` header when managing API keys. Your Bayse user ID. ```json 200 OK theme={null} { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "deviceId": "d_abc123", "userId": "usr_456def" } ``` ## Errors | Status | Description | | ------ | ---------------------------------------------------------------- | | 400 | Invalid request body (missing or malformed email/password). | | 401 | Invalid credentials. | | 429 | Rate limited. Retry after the number of seconds in `retryAfter`. | ```json 429 Too Many Requests theme={null} { "message": "Too many login attempts. Please try again later.", "retryAfter": 120 } ``` ## Next steps Use the `token` and `deviceId` from the response to [create an API key](/api-reference/user/create-api-key) programmatically: ```bash theme={null} curl -X POST https://relay.bayse.markets/v1/user/me/api-keys \ -H "x-auth-token: YOUR_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" \ -H "Content-Type: application/json" \ -d '{"name": "My API Key"}' ``` # Lookup user Source: https://docs.bayse.markets/api-reference/user/lookup GET /v1/user/lookup Resolve a user tag or ID to their public profile ## Overview Look up a user by their tag (username) or user ID to get their public profile info. Provide either `tag` or `userId`, not both. Requires [read authentication](/authentication). Include your `X-Public-Key` header. ## Query parameters The user's tag (username). Case-insensitive. The user's ID (UUID). ## Example request ```bash cURL (by tag) theme={null} curl https://relay.bayse.markets/v1/user/lookup?tag=mulumba \ -H "X-Public-Key: YOUR_PUBLIC_KEY" ``` ```bash cURL (by ID) theme={null} curl https://relay.bayse.markets/v1/user/lookup?userId=68eea9d8-a0fe-4534-ae88-b71e2f4f5c8f \ -H "X-Public-Key: YOUR_PUBLIC_KEY" ``` ```javascript Node.js theme={null} const response = await fetch('https://relay.bayse.markets/v1/user/lookup?tag=mulumba', { headers: { 'X-Public-Key': 'YOUR_PUBLIC_KEY' }, }); const user = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/user/lookup', params={'tag': 'mulumba'}, headers={'X-Public-Key': 'YOUR_PUBLIC_KEY'}, ) user = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/user/lookup?tag=mulumba", nil) req.Header.Set("X-Public-Key", "YOUR_PUBLIC_KEY") resp, _ := http.DefaultClient.Do(req) ``` ## Response The user's unique ID (UUID). The user's tag (username). URL of the user's profile image. May be empty if no image is set. ```json 200 OK theme={null} { "id": "68eea9d8-a0fe-4534-ae88-b71e2f4f5c8f", "tag": "mulumba", "imageUrl": "https://cdn.bayse.markets/profile-images/mulumba.jpg" } ``` ## Errors | Status | Description | | ------ | ------------------------------------------------------ | | 400 | Neither `tag` nor `userId` provided, or both provided. | | 401 | Missing or invalid API key. | | 404 | No user found with the given tag or ID. | # Revoke API key Source: https://docs.bayse.markets/api-reference/user/revoke-api-key DELETE /v1/user/me/api-keys/{keyId} Permanently deactivate an API key ## Authentication Requires `x-auth-token` and `x-device-id` headers. Obtain these by calling the [login endpoint](/api-reference/user/login). ## Path parameters The ID of the API key to revoke. ## Example request ```bash cURL theme={null} curl -X DELETE https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789 \ -H "x-auth-token: YOUR_AUTH_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" ``` ```javascript Node.js theme={null} await fetch( 'https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789', { method: 'DELETE', headers: { 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, } ); ``` ```python Python theme={null} import requests requests.delete( 'https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789', headers={ 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, ) ``` ```go Go theme={null} req, _ := http.NewRequest( "DELETE", "https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789", nil, ) req.Header.Set("x-auth-token", "YOUR_AUTH_TOKEN") req.Header.Set("x-device-id", "YOUR_DEVICE_ID") http.DefaultClient.Do(req) ``` ## Response ```json 200 OK theme={null} { "message": "API key revoked" } ``` Revoking a key is permanent. Any requests signed with the revoked key will immediately start returning 401. If you need to replace a key, use [Rotate API key](/api-reference/user/rotate-api-key) instead. # Rotate API key Source: https://docs.bayse.markets/api-reference/user/rotate-api-key POST /v1/user/me/api-keys/{keyId}/rotate Generate a new secret key while keeping the same key ID ## Authentication Requires `x-auth-token` and `x-device-id` headers. Obtain these by calling the [login endpoint](/api-reference/user/login). ## Path parameters The ID of the API key to rotate. ## Example request ```bash cURL theme={null} curl -X POST \ https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789/rotate \ -H "x-auth-token: YOUR_AUTH_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789/rotate', { method: 'POST', headers: { 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, } ); const { publicKey, secretKey } = await response.json(); ``` ```python Python theme={null} import requests resp = requests.post( 'https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789/rotate', headers={ 'x-auth-token': 'YOUR_AUTH_TOKEN', 'x-device-id': 'YOUR_DEVICE_ID', }, ) data = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest( "POST", "https://relay.bayse.markets/v1/user/me/api-keys/3f7a1b2c-d4e5-6789-abcd-ef0123456789/rotate", nil, ) req.Header.Set("x-auth-token", "YOUR_AUTH_TOKEN") req.Header.Set("x-device-id", "YOUR_DEVICE_ID") resp, _ := http.DefaultClient.Do(req) ``` ## Response The same key ID as before. The key's label. The public key — unchanged from before rotation. A new secret key. **Only returned once.** Updated signing instructions. ```json 200 OK theme={null} { "id": "3f7a1b2c-d4e5-6789-abcd-ef0123456789", "name": "Trading bot", "publicKey": "pk_live_abcdef123456", "secretKey": "sk_live_newsecret456def", "signingInstructions": { "algorithm": "HMAC-SHA256", "headers": ["X-Public-Key", "X-Timestamp", "X-Signature"], "payloadFormat": "{timestamp}.{method}.{path}.{bodyHash}", "timestampWindowSeconds": 300 } } ``` The old secret key stops working immediately upon rotation. Update all services using this key before rotating. # Get assets Source: https://docs.bayse.markets/api-reference/wallet/get-assets GET /v1/wallet/assets Get wallet assets and balances for the authenticated user ## Authentication Read authentication required — `X-Public-Key` header. See the [Authentication guide](/authentication). ## Example request ```bash cURL theme={null} curl "https://relay.bayse.markets/v1/wallet/assets" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```javascript Node.js theme={null} const response = await fetch('https://relay.bayse.markets/v1/wallet/assets', { headers: { 'X-Public-Key': 'pk_live_abcdef123456', }, }); const assets = await response.json(); ``` ```python Python theme={null} import requests resp = requests.get( 'https://relay.bayse.markets/v1/wallet/assets', headers={'X-Public-Key': 'pk_live_abcdef123456'}, ) assets = resp.json() ``` ```go Go theme={null} req, _ := http.NewRequest("GET", "https://relay.bayse.markets/v1/wallet/assets", nil) req.Header.Set("X-Public-Key", "pk_live_abcdef123456") resp, _ := http.DefaultClient.Do(req) ``` ## Response List of the user's wallet assets. Asset UUID. Currency symbol (e.g., `USD`, `NGN`). Owner's user UUID. Blockchain network (e.g., `bep20`, `tron`, `sol`). Available balance. Pending balance. `ACTIVE` or `SUSPENDED`. `ACTIVE` or `SUSPENDED`. `ACTIVE` or `SUSPENDED`. Whether this is the user's default asset. Whether this is a local currency asset. Deposit addresses for this asset. Address UUID. Wallet address. Token symbol (e.g., `USDT`). Payment provider. Blockchain network. ISO 8601 timestamp. ISO 8601 timestamp. ```json 200 OK theme={null} { "assets": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "symbol": "USD", "userId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "network": "bep20", "availableBalance": 1250.50, "pendingBalance": 0, "depositActivity": "ACTIVE", "withdrawalActivity": "ACTIVE", "wagerActivity": "ACTIVE", "isDefault": true, "isLocalCurrencyAsset": false, "addresses": [ { "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "address": "0x1234567890abcdef1234567890abcdef12345678", "symbol": "USDT", "provider": "hostcap", "network": "bep20" } ], "createdAt": "2026-01-13T08:38:32.454Z", "updatedAt": "2026-02-18T11:32:09.718Z" }, { "id": "d4e5f6a7-b8c9-0123-defa-456789012345", "symbol": "NGN", "userId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "network": "bep20", "availableBalance": 50000.00, "pendingBalance": 50000.00, "depositActivity": "ACTIVE", "withdrawalActivity": "ACTIVE", "wagerActivity": "ACTIVE", "isDefault": false, "isLocalCurrencyAsset": true, "addresses": [], "createdAt": "2026-06-04T08:47:59.086Z", "updatedAt": "2026-02-18T12:52:51.715Z" } ] } ``` # Authentication Source: https://docs.bayse.markets/authentication Learn how to authenticate your API requests with API keys and HMAC signatures ## Overview Bayse Markets API uses API key authentication with HMAC-SHA256 signatures for secure request verification. Authentication requirements vary by endpoint: * **Public endpoints**: No authentication required. * **Read endpoints**: API key only (`X-Public-Key` header). * **Write endpoints**: API key + timestamp + HMAC signature. ## API key structure API keys come in pairs: * **Public key** (`pk_*`): Identifies your API key (safe to expose in headers). * **Secret key** (`sk_*`): Used to sign requests (keep secure, never expose). ``` Public key: pk_live_abcdef123456 Secret key: sk_live_secret789xyz ``` Your secret key is only shown once during creation. Store it securely in environment variables or a secrets manager. ## Get your API keys You can create and manage API keys in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys). Or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. If you prefer, you can also manage API keys programmatically through the API. Log in with your email and password to get a session token and device ID, then create, list, revoke, or rotate keys. ## Authentication levels ### Public endpoints Some endpoints require no authentication: ```bash theme={null} curl https://relay.bayse.markets/health ``` ### Read authentication For read operations, include your public key in the `X-Public-Key` header: ```bash theme={null} curl -X GET "https://relay.bayse.markets/v1/pm/events" \ -H "X-Public-Key: pk_live_abcdef123456" ``` **Endpoints requiring read authentication:** * `GET /v1/pm/portfolio`. * `GET /v1/pm/orders`. * `GET /v1/pm/activities`. ### Write authentication Write operations require three headers: 1. **X-Public-Key**: Your public API key. 2. **X-Timestamp**: Current Unix timestamp (seconds). 3. **X-Signature**: HMAC-SHA256 signature of the request payload (base64-encoded). The signing payload format is: `{timestamp}.{METHOD}.{path}.{bodyHash}` * **timestamp**: The same Unix timestamp sent in `X-Timestamp`. * **METHOD**: The HTTP method in uppercase (e.g., `POST`, `DELETE`). * **path**: The request path (e.g., `/v1/pm/orders/abc123`). * **bodyHash**: SHA-256 hex digest of the request body. Empty string if there is no body. ```bash theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/events/evt_123/markets/mkt_456/orders" BODY='{"side":"BUY","outcome":"YES","amount":100,"currency":"USD"}' # Compute body hash (SHA-256 hex digest; empty string if no body) BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') # Build signing payload PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" # Create HMAC-SHA256 signature (base64-encoded) SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` **Endpoints requiring write authentication:** * `POST /v1/pm/events/{eventId}/markets/{marketId}/orders`. * `DELETE /v1/pm/orders/{orderId}`. ## Implementing HMAC signatures ### How it works 1. Get the current Unix timestamp (seconds since epoch). 2. Build the signing payload: `{timestamp}.{METHOD}.{path}.{bodyHash}`. * If the request has a JSON body, `bodyHash` is the SHA-256 hex digest of the raw body bytes. * If there is no body, `bodyHash` is an empty string (the payload ends with a trailing `.`). 3. Compute the HMAC-SHA256 of the payload using your secret key. 4. Base64-encode the result. 5. Send in the `X-Signature` header along with `X-Timestamp`. The server verifies the signature matches and the timestamp is within a 5-minute window (prevents replay attacks). ### Code examples ```bash cURL theme={null} PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/events/evt_123/markets/mkt_456/orders" BODY='{"side":"BUY","outcome":"YES","amount":100,"currency":"USD"}' # Compute body hash (SHA-256 hex digest) BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') # Build signing payload and create HMAC-SHA256 signature PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) # Make authenticated request curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; class BayseClient { constructor(publicKey, secretKey) { this.publicKey = publicKey; this.secretKey = secretKey; this.baseUrl = 'https://relay.bayse.markets'; } createSignature(timestamp, method, path, body = null) { let bodyHash = ''; if (body) { bodyHash = crypto.createHash('sha256').update(body).digest('hex'); } const payload = `${timestamp}.${method}.${path}.${bodyHash}`; return crypto .createHmac('sha256', this.secretKey) .update(payload) .digest('base64'); } async makeAuthenticatedRequest(method, path, body = null) { const timestamp = Math.floor(Date.now() / 1000); const bodyStr = body ? JSON.stringify(body) : null; const signature = this.createSignature(timestamp, method, path, bodyStr); const options = { method, headers: { 'X-Public-Key': this.publicKey, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Content-Type': 'application/json', }, }; if (bodyStr) { options.body = bodyStr; } const response = await fetch(`${this.baseUrl}${path}`, options); return response.json(); } async placeOrder(eventId, marketId, order) { return this.makeAuthenticatedRequest( 'POST', `/v1/pm/events/${eventId}/markets/${marketId}/orders`, order ); } } // Usage const client = new BayseClient( 'pk_live_abcdef123456', 'sk_live_secret789xyz' ); await client.placeOrder('evt_123', 'mkt_456', { side: 'BUY', outcome: 'YES', amount: 100, currency: 'USD', }); ``` ```python Python theme={null} import hmac import hashlib import base64 import json import time import requests class BayseClient: def __init__(self, public_key, secret_key): self.public_key = public_key self.secret_key = secret_key self.base_url = 'https://relay.bayse.markets' def create_signature(self, timestamp, method, path, body=None): """Create HMAC-SHA256 signature of the request payload""" body_hash = '' if body: body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' return base64.b64encode( hmac.new( self.secret_key.encode(), payload.encode(), hashlib.sha256 ).digest() ).decode() def make_authenticated_request(self, method, path, body=None): """Make authenticated API request""" timestamp = int(time.time()) body_str = json.dumps(body) if body else None signature = self.create_signature(timestamp, method, path, body_str) headers = { 'X-Public-Key': self.public_key, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Content-Type': 'application/json', } response = requests.request( method, f'{self.base_url}{path}', headers=headers, data=body_str, ) return response.json() def place_order(self, event_id, market_id, order): """Place a prediction market order""" return self.make_authenticated_request( 'POST', f'/v1/pm/events/{event_id}/markets/{market_id}/orders', body=order, ) # Usage client = BayseClient( 'pk_live_abcdef123456', 'sk_live_secret789xyz' ) result = client.place_order('evt_123', 'mkt_456', { 'side': 'BUY', 'outcome': 'YES', 'amount': 100, 'currency': 'USD', }) ``` ```go Go theme={null} package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "strconv" "time" ) type BayseClient struct { PublicKey string SecretKey string BaseURL string } func NewBayseClient(publicKey, secretKey string) *BayseClient { return &BayseClient{ PublicKey: publicKey, SecretKey: secretKey, BaseURL: "https://relay.bayse.markets", } } func (c *BayseClient) CreateSignature(timestamp int64, method, path string, body []byte) string { bodyHash := "" if len(body) > 0 { h := sha256.Sum256(body) bodyHash = hex.EncodeToString(h[:]) } payload := fmt.Sprintf("%d.%s.%s.%s", timestamp, method, path, bodyHash) mac := hmac.New(sha256.New, []byte(c.SecretKey)) mac.Write([]byte(payload)) return base64.StdEncoding.EncodeToString(mac.Sum(nil)) } func (c *BayseClient) GetAuthHeaders(method, path string, body []byte) map[string]string { timestamp := time.Now().Unix() signature := c.CreateSignature(timestamp, method, path, body) return map[string]string{ "X-Public-Key": c.PublicKey, "X-Timestamp": strconv.FormatInt(timestamp, 10), "X-Signature": signature, "Content-Type": "application/json", } } // Usage func main() { client := NewBayseClient( "pk_live_abcdef123456", "sk_live_secret789xyz", ) body := []byte(`{"side":"BUY","outcome":"YES","amount":100,"currency":"USD"}`) headers := client.GetAuthHeaders("POST", "/v1/pm/events/evt_123/markets/mkt_456/orders", body) fmt.Printf("Headers: %+v\n", headers) } ``` ## Social sign-in users If you signed up for Bayse using **Apple** or **Google**, your account doesn't have a password yet. The API requires email and password authentication to create and manage API keys. To set up a password: 1. Open the Bayse app and go to **Forgot Password** (or use the password reset flow). 2. Enter the email associated with your Apple/Google account. 3. Follow the instructions to create a password. Once you've set a password, you can use it with the [login endpoint](#getting-a-session-token) to get a session token and start managing API keys. Setting a password does **not** change or remove your existing sign-in method. You can continue using Apple or Google to sign in to the Bayse app as usual. The password is only needed for API access. You can manage API keys in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys), or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. ## Managing API keys You can manage API keys from the Bayse web app or programmatically through the endpoints below. ### Getting a session token Before you can create or manage API keys, you need to log in with your Bayse account credentials to get a session token and device ID: ```bash theme={null} curl -X POST https://relay.bayse.markets/v1/user/login \ -H "Content-Type: application/json" \ -d '{ "email": "you@example.com", "password": "your-password" }' ``` ```json theme={null} { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "deviceId": "d_abc123", "userId": "usr_456def" } ``` Use the `token` and `deviceId` from the response as the `x-auth-token` and `x-device-id` headers for all API key management requests below. The login endpoint is rate-limited to 1 request per 2 minutes per email address. Cache your session token and reuse it. See [Rate limits](/rate-limits) for details. ### Creating API keys With your session token and device ID, create an API key: ```bash theme={null} curl -X POST https://relay.bayse.markets/v1/user/me/api-keys \ -H "x-auth-token: YOUR_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" \ -H "Content-Type: application/json" \ -d '{"name": "Production API Key"}' ``` ```json theme={null} { "id": "key_abc123", "publicKey": "pk_live_abcdef123456", "secretKey": "sk_live_secret789xyz", "name": "Production API Key", "createdAt": "2026-02-16T10:30:00Z" } ``` ### Listing API keys ```bash theme={null} curl -X GET https://relay.bayse.markets/v1/user/me/api-keys \ -H "x-auth-token: YOUR_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" ``` ### Revoking API keys ```bash theme={null} curl -X DELETE https://relay.bayse.markets/v1/user/me/api-keys/key_abc123 \ -H "x-auth-token: YOUR_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" ``` ### Rotating API keys Generate a new secret key while keeping the same public key: ```bash theme={null} curl -X POST https://relay.bayse.markets/v1/user/me/api-keys/key_abc123/rotate \ -H "x-auth-token: YOUR_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" ``` ```json theme={null} { "id": "key_abc123", "publicKey": "pk_live_abcdef123456", "secretKey": "sk_live_newsecret456def", "name": "Production API Key", "rotatedAt": "2026-02-16T11:00:00Z" } ``` ## Security best practices * Never commit API keys to version control * Use environment variables or secrets managers * Rotate keys regularly * Use separate keys for development and production * Never expose secret keys in client-side code * Don't log secret keys * Revoke compromised keys immediately * The secret key is only shown once - save it during creation * Don't expose secret keys in error messages * Handle authentication errors gracefully * Implement retry logic with exponential backoff * Monitor for suspicious authentication patterns * Always use HTTPS in production * Verify SSL certificates * Never send credentials over HTTP ## Common errors ### Invalid signature ```json theme={null} { "error": "invalid_signature", "message": "The provided signature does not match the expected signature" } ``` **Causes:** * Incorrect secret key. * Timestamp mismatch (signed different timestamp than sent in `X-Timestamp`). * Wrong payload format — must be `{timestamp}.{METHOD}.{path}.{bodyHash}`. * Body hash mismatch — the exact bytes sent in the request body must match what was hashed when signing. Avoid trimming or reformatting the body after signing. * Incorrect HMAC algorithm (must be SHA-256). * Incorrect encoding (signature must be base64, body hash must be hex). ### Timestamp too old ```json theme={null} { "error": "timestamp_expired", "message": "Request timestamp is too old" } ``` **Cause:** The timestamp in `X-Timestamp` is too far in the past. Ensure your system clock is synchronized. ### Missing API key ```json theme={null} { "error": "unauthorized", "message": "API key is required for this endpoint" } ``` **Cause:** The `X-Public-Key` header is missing or invalid. ## Next steps Explore all available endpoints Learn about prediction market operations Manage your API keys programmatically # v0.1.6 — March 13, 2026 Source: https://docs.bayse.markets/changelog/2026-03-12 Breaking changes to Place Order and Get Quote endpoints ## Breaking changes ### Place Order — `POST /v1/pm/events/{eventId}/markets/{marketId}/orders` This is a **breaking change**. All existing integrations must be updated. #### Request body changes | Change | Before | After | | ------------- | ------------------------------------- | ----------------------------------------------- | | Outcome field | `outcome` (`YES` / `NO`) | `outcomeId` (UUID of the outcome) | | Order type | Implicit (market order if no `price`) | `type` field **required** (`LIMIT` or `MARKET`) | #### New optional fields (CLOB markets) | Field | Type | Description | | ------------- | --------- | ------------------------------------------------------------------------------------------- | | `timeInForce` | `string` | `GTC`, `GTD`, `FAK`, or `FOK`. Defaults to `GTC` for limit orders, `FAK` for market orders. | | `postOnly` | `boolean` | If `true`, the order is rejected instead of crossing the spread. Limit orders only. | | `maxSlippage` | `number` | Maximum acceptable slippage for market orders (0.00–1.00). | | `expiresAt` | `string` | ISO 8601 expiration timestamp. Required for `GTD` orders. | #### Migration guide **Before:** ```json theme={null} { "side": "BUY", "outcome": "YES", "amount": 100, "currency": "USD", "price": 0.70 } ``` **After:** ```json theme={null} { "side": "BUY", "outcomeId": "c3d4e5f6-a7b8-9012-cdef-345678901234", "amount": 100, "type": "LIMIT", "currency": "USD", "price": 0.70, "timeInForce": "GTC" } ``` To get the `outcomeId`, use the [Get Event](/api-reference/pm/get-event) endpoint — each outcome in a market has an `id` field. *** ### Get Quote — `POST /v1/pm/events/{eventId}/markets/{marketId}/quote` #### Request body changes | Change | Before | After | | ------------- | ------------------------ | --------------------------------- | | Outcome field | `outcome` (`YES` / `NO`) | `outcomeId` (UUID of the outcome) | #### Migration guide **Before:** ```json theme={null} { "side": "BUY", "outcome": "YES", "amount": 100, "currency": "USD" } ``` **After:** ```json theme={null} { "side": "BUY", "outcomeId": "c3d4e5f6-a7b8-9012-cdef-345678901234", "amount": 100, "currency": "USD" } ``` *** ### Response format changes The Place Order response now uses a unified `order` key instead of separate `ammOrder` / `clobOrder` keys: **Before:** ```json theme={null} { "engine": "CLOB", "clobOrder": { ... } } ``` **After:** ```json theme={null} { "engine": "CLOB", "order": { ... } } ``` The `engine` field (`AMM` or `CLOB`) remains unchanged. # v0.1.7 — March 22, 2026 Source: https://docs.bayse.markets/changelog/2026-03-22 New PnL endpoint and mint/burn share operations ## New endpoints ### Get PnL — `GET /v1/pm/pnl` Track your realized profit and loss across all markets. Filter by time period (`12H`, `24H`, `1W`, `1M`, `1Y`) or a custom date range, and optionally get a per-event breakdown of your top 30 most recent events. PnL is computed per currency — if you query `USD` but only traded in `NGN`, all values will be zero. ```json theme={null} { "realizedPnl": 31.11, "settlementPnl": 24.50, "tradePnl": 6.61, "wins": 8, "losses": 6, "currency": "USD" } ``` See [Get PnL](/api-reference/pm/get-pnl) for full documentation. *** ### Mint shares — `POST /v1/pm/markets/{marketId}/mint` Deposit funds and receive equal YES and NO shares for a market. Each pair costs the market's base unit (\$1.00 in USD, ₦100 in NGN). Minting does not affect market prices. See [Mint shares](/api-reference/pm/mint-shares) for full documentation. *** ### Burn shares — `POST /v1/pm/markets/{marketId}/burn` Surrender equal YES and NO shares and receive funds back. The reverse of minting. You must hold sufficient shares of both outcomes. See [Burn shares](/api-reference/pm/burn-shares) for full documentation. # v0.1.8 — April 10, 2026 Source: https://docs.bayse.markets/changelog/2026-04-10 API key management is now available in the Bayse web app ## Improvements ### API key management in the web app You can now create and manage API keys in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys). Or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. If you prefer, you can still manage API keys programmatically through the API by logging in with your email and password to get a session token and device ID. See the [Authentication](/authentication) guide for the full setup flow and the [Create API key](/api-reference/user/create-api-key) reference for programmatic key management. # v0.1.9 — April 15, 2026 Source: https://docs.bayse.markets/changelog/2026-04-15 Market maker rebates and a new Market makers documentation section ## New features ### Market maker rebates Makers who provide liquidity on CLOB markets now earn a share of taker fees. Every time a taker fills your resting limit order, a portion of their fee is allocated to a daily rebate pool. Your share is proportional to your maker volume. Rebates are calculated per UTC calendar day and credited to your wallet automatically. See the [Maker rebates](/market-makers/maker-rebates) guide for details, or track your earnings via the API: * [GET /v1/pm/maker-rebates](/api-reference/pm/maker-rebates) — completed payout history * [GET /v1/pm/maker-rebates/active](/api-reference/pm/maker-rebates-active) — in-progress accumulation ## Documentation ### Market makers section Liquidity rewards and maker rebates documentation has been consolidated into a dedicated [Market makers](/market-makers/liquidity-rewards) section. All existing links to the liquidity rewards page continue to work. # v0.1.11 — May 5, 2026 Source: https://docs.bayse.markets/changelog/2026-05-05 Batch order endpoints and self-trade prevention for CLOB markets ## New endpoints ### Batch place orders — `POST /v1/pm/orders/batch` Submit up to **50 CLOB orders** in a single round-trip. Orders may span multiple markets and events — each item carries only `outcomeId`, and the server resolves market and event from the outcome. Per-order best-effort: a bad item does not abort the rest of the batch. ```json theme={null} { "orders": [ { "outcomeId": "...", "side": "BUY", "type": "LIMIT", "amount": 100, "price": 0.70 }, { "outcomeId": "...", "side": "SELL", "type": "LIMIT", "amount": 50, "price": 0.32 } ] } ``` See [Batch place orders](/api-reference/pm/batch-place-orders) for the full schema. *** ### Batch cancel orders — `DELETE /v1/pm/orders/batch` Cancel up to **100 CLOB orders** in a single round-trip. Order IDs may belong to different markets and events. IDs the caller does not own return `ORDER_NOT_FOUND`. ```json theme={null} { "orderIds": [ "f6a7b8c9-d0e1-2345-fabc-678901234567", "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ] } ``` See [Batch cancel orders](/api-reference/pm/batch-cancel-orders) for the full schema. *** ## New features ### Self-trade prevention — `stpMode` on CLOB orders CLOB order placement now accepts an optional `stpMode` field that controls how the matching engine resolves a match against another resting order from the same user. Previously, both same-user orders sat on the book until one was manually cancelled — a sharp edge for market makers replacing quotes. ```json theme={null} { "side": "BUY", "outcomeId": "...", "type": "LIMIT", "amount": 100, "price": 0.71, "stpMode": "CANCEL_OLDEST" } ``` Valid values: * `SKIP` *(default)* — the match is silently skipped and both orders remain on the book. Backward-compatible behavior. * `CANCEL_OLDEST` — the resting same-user maker is cancelled and refunded; the taker continues matching against other counterparties. * `CANCEL_NEWEST` — the incoming taker stops at the same-user match. If it had already filled against other users, those fills stand and the taker comes back as `cancelled`; otherwise it is `rejected`. * `CANCEL_BOTH` — the resting maker is cancelled and the taker is cancelled or rejected under the same rule as `CANCEL_NEWEST`. Unknown values fall back to `SKIP`. The applied `stpMode` is echoed back on `OrderDto` / `OrderResponse`, so the client can confirm what the server applied without round-tripping through the request body. AMM orders ignore `stpMode`. *** ## Behavior to know * **CLOB-only.** Both batch endpoints and `stpMode` apply only to CLOB markets. AMM orders in a batch are rejected per-item with `UNSUPPORTED_ENGINE`; AMM orders ignore `stpMode` entirely. * **Per-order best-effort.** Batch HTTP responses are `200 OK` whenever the batch was processed. Inspect the per-item `success` flag and the `summary` to see what landed. * **Weighted rate limiting.** Batches are charged **per item** against your write rate-limit bucket — a 50-order batch costs 50 tokens, not 1. Over-budget batches are rejected with `429` before any orders reach the matching engine. * **Idempotent retries.** Both batch endpoints accept an optional `Idempotency-Key` header. Retries within 24 hours that share the same key, body, and route replay the original response with `Idempotent-Replayed: true`. Reusing a key with a different body returns `422`; a concurrent retry (sent before the first is finished) returns `409`. Transient responses (`5xx`, `429`, `408`) are not cached so you can recover by retrying. ## Documentation * New concept page: [Batch orders](/concepts/batch-orders). * New API references: [Batch place orders](/api-reference/pm/batch-place-orders) and [Batch cancel orders](/api-reference/pm/batch-cancel-orders). * [Place order](/api-reference/pm/place-order) request and response schemas updated with `stpMode`, including a short market-maker quote-replacement example. # v0.1.12 — May 9, 2026 Source: https://docs.bayse.markets/changelog/2026-05-09 User lookup endpoint, user trades WebSocket channel, and engine/order type in activity payloads ## New endpoints ### Lookup user — `GET /v1/user/lookup` Resolve a user's tag (username) or ID to their public profile. Returns the user's ID, tag, and avatar URL. Accepts either `?tag=mulumba` or `?userId=`. Requires [read authentication](/authentication). See [Lookup user](/api-reference/user/lookup) for the full schema. *** ## New WebSocket channel ### User trades — `user_trades` Subscribe to any user's filled trade activity across all markets via `/ws/v1/markets`. No authentication required. ```json theme={null} { "type": "subscribe", "channel": "user_trades", "userId": "68eea9d8-a0fe-4534-ae88-b71e2f4f5c8f" } ``` Broadcasts `buy_order` and `sell_order` events whenever the user's orders are filled, regardless of the market engine (CLOB or AMM). The payload matches the existing activity feed format. See [User trades](/websocket/market-data#user-trades) for details. *** ## Enhancements ### `engine` and `orderType` in activity payloads WebSocket activity broadcasts (`buy_order`, `sell_order`) now include two new fields in the `order` object: * **`engine`** — `"CLOB"` or `"AMM"`. Indicates which market engine executed the trade. * **`orderType`** — `"MARKET"` or `"LIMIT"`. Indicates the order type that produced the fill. These fields appear on both the existing `activity` channel and the new `user_trades` channel. # v0.1.13 — May 18, 2026 Source: https://docs.bayse.markets/changelog/2026-05-18 Batch amend orders endpoint, batch place / amend cap reduced to 20 ## New endpoint ### Batch amend orders — `POST /v1/pm/orders/batch/amend` Modify the price and/or size of up to 20 open CLOB orders in a single round-trip. The natural complement to batch place and batch cancel — designed for market makers running cancel-and-replace ladders who want to keep time priority where possible instead of always going to the tail of the queue. ```json theme={null} { "items": [ { "orderId": "f6a7b8c9-d0e1-2345-fabc-678901234567", "newPrice": 0.50, "newSize": 15 }, { "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "newPrice": 0.40, "newSize": 8 } ] } ``` Key behavior: * **`newPrice` and `newSize` are independently optional** — supply one or both. The omitted field keeps its current value. Items with neither field are rejected per-item with `BAD_REQUEST`. * **Total size, not delta** — `newSize` is the new TOTAL size in the order's original currency. For partial-filled orders the new remaining becomes `newSize − filledSize`. * **Per-item failure isolation** — bad items return their own error without aborting siblings. Failures use the same per-item shape as the place/cancel batches. * **Funding rejection codes** — `INSUFFICIENT_BALANCE` (BID amend exceeds wallet) and `INSUFFICIENT_SHARES` (ASK amend exceeds free shares) surface per-item rather than failing the whole call. * **Cross-market** — items may target orders in different markets and events. * **Time priority** — preserved when the new `(price, size)` is unchanged or shrunk at the same price level. Price moves go to the new tail. * **Self-trade prevention** — fixed server policy: amend always uses `CANCEL_OLDEST` to resolve same-user crossers at the new price. The order's resting `stpMode` (set at placement) governs matching, not amend. Crossers being amended in the same batch are excluded from the cancel set. * **Idempotency** — supports the standard `Idempotency-Key` header with the same 24-hour replay semantics as the other batch endpoints. Requires [write authentication](/authentication). Charged one rate-limit token per item against the write bucket. See [Batch amend orders](/api-reference/pm/batch-amend-orders) for the full schema and [Order lifecycle](/concepts/order-lifecycle#amending-an-open-order) for the funding and time-priority semantics. *** ## Breaking changes ### Batch place + amend max items: **50 → 20** Both `POST /v1/pm/orders/batch` and `POST /v1/pm/orders/batch/amend` now accept at most **20 items per call** (was 50 for batch place). Larger payloads are rejected with `400 BAD_REQUEST` before any items reach the matching engine. The cap on `DELETE /v1/pm/orders/batch` (batch cancel) is unchanged at **100**. Rationale: bound wallet round-trips and DB statement sizes under high-frequency cancel-and-replace ladders. Existing integrations sending up to 20 items per batch are unaffected. *** ## Enhancements ### Cancel-and-replace cycle guidance The [Batch orders](/concepts/batch-orders) concept page documents the canonical three-step cycle for re-quoting: `cancel → amend → place`. Each step's wallet/balance state is visible to the next, so size-up amends in step 2 can use capacity freed by step 1. ### Amend section in order lifecycle [Order lifecycle](/concepts/order-lifecycle) now describes amend semantics, time-priority preservation, and per-side funding checks. # v0.1.14 — May 29, 2026 Source: https://docs.bayse.markets/changelog/2026-05-29 Soccer sports markets linked via sportGameSlug ## New feature ### Soccer sports markets — H2H + Goal Spread + Total Goals Three individual but interconnected event types for soccer matches, linked through a common `sportGameSlug`: #### 1. Head-to-Head (H2H) 3-Way — `TEAM_H2H_3WAY` The primary match winner event with three mutually exclusive markets: * Home team win * Away team win * Draw #### 2. Goal Spread — `GOAL_SPREAD` Grouped event with independent markets for each team winning by a specific goal margin (e.g., 1.5, 2.5, 3.5 goals). **New fields:** * `propLine` (number) — The goal threshold * `propDirection` (string) — `"OVER"` (indicates the team must win by more than the threshold) * `propTeam` (object) — Team details (id, name, slug, league, sport) #### 3. Total Goals — `TOTAL_GOALS` Grouped event for total goals scored in the match (e.g., Over/Under 2.5, 3.5 goals). **New fields:** * `propLine` (number) — The goal threshold * `propDirection` (string) — `"OVER" (indicates total goals must be over the threshold)` * `propTeam` — `null` (applies to entire match) *** ## New Endpoints ### List sports teams — `GET /v1/pm/sports/teams` Fetch soccer teams. Supports filtering by league and pagination. ```bash theme={null} GET /v1/pm/sports/teams?league=epl&page=1&size=20 ``` ### List sports games — `GET /v1/pm/sports/games` Fetch upcoming and live matches with details like teams, league, and scheduled start time. Supports filtering by team, league, and pagination. ```bash theme={null} GET /v1/pm/sports/games?league=epl&page=1&size=20 ``` ### List sports leagues — `GET /v1/pm/sports/leagues` Fetch soccer leagues to use for filtering teams and games. ```bash theme={null} GET /v1/pm/sports/leagues ``` *** ## New query parameter ### Get Events endpoint — `GET /v1/pm/events` Filter sports events by: * **`sportGameSlug`** (string) — Fetch all three event types for a match ```bash theme={null} GET /v1/pm/events?sportGameSlug=bm-game-20260524-mci-avl # Returns H2H, Goal Spread, and Total Goals events ``` ### Get Trades endpoint — `GET /v1/pm/trades` New optional filters for trade history: * **`orderId`** (string) — Trades where the order is on either the taker or maker side * **`outcomeId`** (string) — Trades on either the taker or maker side * **`userId`** (string) — Trades where the user was either the taker or the maker * **`fromDate`** (string) — Trades created at or after this RFC3339 timestamp * **`toDate`** (string) — Trades created at or before this RFC3339 timestamp ```bash theme={null} GET /v1/pm/trades?userId=9f8c1b2a-3d4e-5f60-7180-90abcdef1234&fromDate=2026-05-01T00:00:00Z&toDate=2026-05-29T23:59:59Z # Returns a user's trades within the date range ``` **Breaking change.** `GET /v1/pm/trades` is now paginated. The `limit` and `cursor` parameters are replaced by **`page`** (default `1`) and **`size`** (default `50`, max `100`), and the response is wrapped in `{ data, pagination }` instead of a bare array. ```bash theme={null} GET /v1/pm/trades?marketId=b2c3d4e5-f6a7-8901-bcde-f12345678901&page=1&size=20 ``` *** ## Response schema changes ### Market response New fields for prop markets: | Field | Type | Present on | | --------------- | -------------- | -------------------------------------------------------------- | | `propLine` | number \| null | Goal Spread markets, Total Goals markets | | `propDirection` | string \| null | Goal Spread markets (`"OVER"`), Total Goals markets (`"OVER"`) | | `propTeam` | object \| null | Goal Spread markets (team details), Total Goals markets (null) | ### Event response New fields: | Field | Type | Present on | Notes | | ----------------- | ------ | ----------------- | ------------------------------------------------- | | `sportGameSlug` | string | All sports events | Use for fetching related events | | `sportMarketType` | string | All sports events | `TEAM_H2H_3WAY` \| `GOAL_SPREAD` \| `TOTAL_GOALS` | *** ## Documentation See [Sports markets](/concepts/sports-markets) for: * Complete market structure examples * Discovery flow for linked events * Terminology and data reference *** ## Technical notes * H2H events also include `propTeam` for home/away markets (enables team-specific UI differentiation) * Database: new `propLine`, `propDirection`, and `propTeamId` columns on `markets` table; new `sportGameSlug` and `sportMarketType` column on `events` table # v0.1.15 — June 23, 2026 Source: https://docs.bayse.markets/changelog/2026-06-23 Seven new soccer sports market types: score, goal, and corner markets ## New features ### Additional soccer sports market types Seven new `sportMarketType` values are now available on sports events, all linked to the same match via `sportGameSlug`. #### Both Teams to Score — `BOTH_TEAMS_TO_SCORE` A single binary market on whether both teams score at least one goal within 90 minutes of regular play plus stoppage time. * Resolves YES if both teams score * Resolves NO if either team fails to score * No `propLine`, `propDirection`, or `propTeam` #### First Team to Score — `FIRST_TEAM_TO_SCORE` A combined event (only one market resolves YES) with three mutually exclusive markets: * Home team scores first * Away team scores first * Neither (game ends 0-0) `propTeam` is populated for the home and away markets; `null` for "Neither". #### Total Goals (Home / Away) — `TOTAL_GOALS_HOME` / `TOTAL_GOALS_AWAY` Grouped events for the goals scored by a specific team. * `TOTAL_GOALS_HOME` tracks the home team's goals * `TOTAL_GOALS_AWAY` tracks the away team's goals Each market has a `propLine` (e.g., 0.5, 1.5) and `propTeam` set to the relevant team. #### Total Corners — `TOTAL_CORNERS` Grouped event for total corners in the full match (first half + second half, including stoppage time). Markets have a `propLine` corner threshold and `propTeam: null`. #### 1st Half Corners / 2nd Half Corners — `FIRST_HALF_CORNERS` / `SECOND_HALF_CORNERS` Grouped events for corners in each half respectively. Same structure as `TOTAL_CORNERS`, scoped to the relevant half. *** ## Updated response schema ### Event `sportMarketType` field Now one of ten values (previously three): | Value | Description | Event type | | --------------------- | ------------------------- | ---------- | | `TEAM_H2H_3WAY` | Match winner | combined | | `BOTH_TEAMS_TO_SCORE` | Both teams score? | single | | `FIRST_TEAM_TO_SCORE` | Which team scores first? | combined | | `GOAL_SPREAD` | Team wins by X+ goals | grouped | | `TOTAL_GOALS` | Total goals by both teams | grouped | | `TOTAL_GOALS_HOME` | Home team total goals | grouped | | `TOTAL_GOALS_AWAY` | Away team total goals | grouped | | `TOTAL_CORNERS` | Total corners, full match | grouped | | `FIRST_HALF_CORNERS` | Corners in 1st half | grouped | | `SECOND_HALF_CORNERS` | Corners in 2nd half | grouped | *** ## Documentation See [Sports markets](/concepts/sports-markets) for full structure examples, `propTeam` semantics per market type, and the discovery flow. # Batch orders Source: https://docs.bayse.markets/concepts/batch-orders Place, amend, or cancel many CLOB orders in a single round-trip Batch endpoints let you submit many orders, amendments, or cancellations against the API in a single HTTP request. They are designed for market makers re-quoting on multiple books, latency-sensitive strategies that need to fan out activity in lockstep, and scripts that rebalance positions across many markets at once. Batch order endpoints are **CLOB-only**. AMM markets are rejected per-order with `UNSUPPORTED_ENGINE` since AMM orders execute instantly and cannot be batched against an order book. ## Endpoints | Operation | Endpoint | Max items per call | | -------------- | -------------------------------- | ------------------ | | Place a batch | `POST /v1/pm/orders/batch` | **20 orders** | | Cancel a batch | `DELETE /v1/pm/orders/batch` | **100 orders** | | Amend a batch | `POST /v1/pm/orders/batch/amend` | **20 orders** | All three endpoints require write authentication (`X-Public-Key`, `X-Timestamp`, `X-Signature`). See [Authentication](/authentication). ## Cross-market batches Batches can span multiple markets and events in a single call. * **Place** — each item carries only `outcomeId`. The server resolves the parent market and event from the outcome, so a batch can mix orders across different events. * **Cancel** — each item is a bare `orderId` (UUID). The server looks up the owning market and enforces ownership before cancelling. * **Amend** — each item is `(orderId, newPrice, newSize)`. The server resolves the market from the order and enforces ownership before applying the change. You don't need to group items by market or send one batch per book. One call handles the whole fan-out. ## Per-order best-effort semantics A batch is **not all-or-nothing**. Each item is evaluated independently, so one bad input does not roll back the rest of the batch. * A bad `outcomeId`, an AMM market, an insufficient balance, or a missing required field on **one** item fails just that item. * The other items continue to execute and return their normal success or failure outcomes. * The HTTP response is `200 OK` whenever the batch was processed, even if some items failed. Inspect the `summary` and per-item `success` flags to know what landed. ```json theme={null} { "engine": "CLOB", "results": [ { "index": 0, "success": true, "order": { "id": "...", "status": "open" } }, { "index": 1, "success": false, "error": { "code": "INSUFFICIENT_BALANCE", "message": "..." } }, { "index": 2, "success": true, "order": { "id": "...", "status": "open" } } ], "summary": { "total": 3, "succeeded": 2, "failed": 1 } } ``` ## Rate limiting Batch calls are charged **per item** against your API key's write rate-limit bucket — a 20-order place batch costs 20 write tokens, not 1. * The check runs **after** the batch body is bound, so an over-budget batch returns `429 Too Many Requests` with a `Retry-After` header before any orders are placed upstream. * Sizing batches close to the cap will exhaust your write budget more quickly. If you submit at the cap continuously, you will be limited by your tier's write throughput just as if you'd placed the orders one by one. See [Rate limits](/rate-limits) for tier limits. ## Idempotency Both endpoints accept an optional `Idempotency-Key` header. When set, a retry of the same `(API key, key, method, path)` within **24 hours** replays the original response and sets `Idempotent-Replayed: true` on the response — useful for safe retries after a network blip or a 5xx. * The key must be 1–255 characters of `[A-Za-z0-9_-]` (UUIDs and ULIDs are good defaults). * A retry with the **same key but a different request body** is rejected with `422 Unprocessable Entity`. Generate a fresh key for a different payload. * A **concurrent** retry — sent before the first request has finished — is rejected with `409 Conflict`. Back off briefly and retry; once the first call completes you'll either get the cached response (success or stable client error) or be allowed to re-execute (transient errors are not cached). * Transient errors are **not cached**: `5xx`, `429`, and `408` are excluded so you can recover by retrying after the underlying condition clears. Stable `2xx` and `4xx` responses are cached for the full 24-hour window. ## Cancel-and-replace cycles When re-quoting, the canonical pattern is three sibling batches in sequence: 1. `DELETE /v1/pm/orders/batch` — cancel stale orders. Frees their locked USD (BIDs) and shares (ASKs) back to the wallet and balance sheet. 2. `POST /v1/pm/orders/batch/amend` — modify in-place orders whose price or size moved. Uses the capacity just freed in step 1 for any size-up / price-up debits. 3. `POST /v1/pm/orders/batch` — place brand-new orders for bands without an existing order to amend. Uses whatever capacity remains. Each call returns before the next starts, so the funding state visible to step N+1 reflects step N's effects. Amend is the natural middle step because it keeps time priority where possible — an in-place price/size tweak retains queue position at the same level, whereas a cancel + re-place always goes to the tail. See [Order lifecycle](/concepts/order-lifecycle) for the underlying status transitions. ## Choosing between single and batch endpoints Use the [single place](/api-reference/pm/place-order) and [single cancel](/api-reference/pm/cancel-order) endpoints for ad-hoc trading and any AMM activity. Reach for batch when: * You're a market maker re-quoting both sides of one or more books. * You need orders on multiple markets to land together (e.g. a hedged pair). * You're cancelling a working set after a strategy switch and want one round-trip instead of N. * You need to shift multiple resting orders' prices or sizes without giving up queue priority. See [Batch place orders](/api-reference/pm/batch-place-orders), [Batch cancel orders](/api-reference/pm/batch-cancel-orders), and [Batch amend orders](/api-reference/pm/batch-amend-orders) for the request and response schemas. # Event series Source: https://docs.bayse.markets/concepts/event-series Recurring prediction markets on the same asset or topic An **event series** is a collection of recurring events that follow the same pattern — same asset, same question structure, repeated on a schedule. Instead of a one-off "Will BTC close above \$70k?", a series produces a new event every hour, every 6 hours, or every day. ## How series work Each series defines: * **Asset** — the underlying asset or topic (e.g. BTC, ETH, XAUUSD, EUR/USD). * **Interval** — how often a new event is created: `FIFTEEN_MINUTE`, `HOURLY`, `SIX_HOURLY`, or `DAILY`. * **Category** — such as `CRYPTO`, `CURRENCY`, or `FINANCE`. When a series interval elapses, a new event is automatically created with its own markets and outcomes. Previous events in the series continue through their normal lifecycle (trading closes, then the outcome is resolved). ## Series vs events | | Series | Event | | ---------------------- | ------------------------------------ | -------------------------------- | | **What it represents** | A recurring template | A single tradeable question | | **Lifespan** | Ongoing | Opens → closes → resolves | | **Tradeable?** | No — you trade the individual events | Yes | | **Example** | "Bitcoin Hourly Markets" | "Bitcoin Hourly — Apr 4 2pm GMT" | A series is not tradeable on its own. It groups events so you can browse and follow a specific asset's recurring markets. ## Available series | Asset type | `category` field | Examples | | ---------------- | ----------------------- | ----------------------------------------------------------------------------------------------- | | Crypto | `CRYPTO` | BTC, ETH, SOL — 15-min, hourly, 6-hourly, and daily intervals | | Commodities & FX | `CURRENCY` or `FINANCE` | Gold (XAUUSD), Silver (XAGUSD), Oil (WTI), EUR/USD, GBP/USD, USD/NGN, USD/JPY, EUR/GBP — hourly | Use the `assetSymbol` and `intervalType` fields to find the series you need rather than relying on `category` alone. ## Browsing series Use the [List series](/api-reference/pm/list-series) endpoint to get all available series: ```bash theme={null} GET /v1/pm/events/series ``` Each series has a `slug` (e.g. `crypto-btc-1h`) that you use to fetch its recent events: ```bash theme={null} GET /v1/pm/events/series/crypto-btc-1h/lean-events ``` This returns up to 20 recent events in the series with their opening, closing, and resolution times. See the [Get series events](/api-reference/pm/get-series-events) reference for the full response shape. You can also filter the main [List events](/api-reference/pm/list-events) endpoint by `seriesSlug` to get full event details for a specific series: ```bash theme={null} GET /v1/pm/events?seriesSlug=crypto-btc-1h&status=open ``` ## Event lifecycle within a series Events in a series follow the standard [event lifecycle](/concepts/events-markets-outcomes): 1. **Open** — the event is created and trading begins. 2. **Closed** — trading stops (e.g. at the end of the hourly window). 3. **Resolved** — the outcome is determined based on the real-world result. For an hourly series, a typical event opens at the top of the hour, closes at the end of the hour, and resolves shortly after. # Events, markets & outcomes Source: https://docs.bayse.markets/concepts/events-markets-outcomes The data model behind Bayse prediction markets ## Hierarchy Bayse uses a three-level hierarchy: **Event → Market → Outcome** * An **event** is the top-level container. It represents a real-world question or topic — for example, "NBA Finals 2025" or "Will it rain in Lagos tomorrow?". * A **market** is a tradeable question within an event. Each market has exactly **two outcomes** (e.g. YES/NO, or two named options). Prices for the two outcomes always sum to approximately 1.00. * An **outcome** is one side of a market that you can buy or sell shares in. Each outcome has a unique UUID (`outcomeId`) that you use when placing orders or getting quotes. ## Events Events group one or more related markets together. Every event has a `type` that determines how its markets relate to each other. **Key fields:** * `title` — the headline question or topic. * `type` — `single`, `combined`, or `grouped` (see [Event types](#event-types)). * `category` — sports, politics, crypto, entertainment, etc. * `status` — `open`, `closed`, `resolved`, `cancelled`, `paused`, or `draft`. * `engine` — `AMM` or `CLOB` (see [Market engines](/concepts/market-engines)). * `closingDate` — when trading closes. * `resolutionDate` — when the outcome is determined. ## Markets and outcomes Each market is a binary question with two outcomes. You trade by buying or selling shares in one of the outcomes. **Key fields:** * `title` — the specific question (e.g. "Will Lakers win?"). * `outcome1Id` / `outcome2Id` — UUIDs that uniquely identify each outcome. * `outcome1Label` / `outcome2Label` — labels for each side (e.g. "YES" / "NO"). * `outcome1Price` / `outcome2Price` — current probability prices (0.00–1.00). * `rules` — resolution criteria for this market. * `status` — same set as events. The `outcomeId` is the canonical way to reference an outcome. When you fetch a market, note the `outcome1Id` and `outcome2Id` — you'll use one of these when placing orders or requesting quotes. ## Event types One event, one market, two outcomes. The event title and market title are typically the same. ```json theme={null} { "title": "Will it rain in Lagos tomorrow?", "type": "single", "markets": [ { "title": "Will it rain in Lagos tomorrow?", "outcome1Id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "outcome1Label": "YES", "outcome1Price": 0.65, "outcome2Id": "d4e5f6a7-b8c9-0123-defa-234567890123", "outcome2Label": "NO", "outcome2Price": 0.35 } ] } ``` Multiple **mutually exclusive** markets under one event. Only one market can resolve YES — when one wins, the others automatically resolve NO. ```json theme={null} { "title": "NBA Finals 2025 Winner", "type": "combined", "markets": [ { "title": "Will Lakers win?", "outcome1Id": "...", "outcome1Label": "YES", "outcome1Price": 0.35, "outcome2Id": "...", "outcome2Label": "NO", "outcome2Price": 0.65 }, { "title": "Will Celtics win?", "outcome1Id": "...", "outcome1Label": "YES", "outcome1Price": 0.40, "outcome2Id": "...", "outcome2Label": "NO", "outcome2Price": 0.60 }, { "title": "Will Nuggets win?", "outcome1Id": "...", "outcome1Label": "YES", "outcome1Price": 0.25, "outcome2Id": "...", "outcome2Label": "NO", "outcome2Price": 0.75 } ] } ``` Multiple **independent** markets under one event. Each market is resolved on its own — multiple markets can resolve YES. ```json theme={null} { "title": "Player stats: Lakers vs Celtics", "type": "grouped", "markets": [ { "title": "Will LeBron score 30+ points?", "outcome1Id": "...", "outcome1Label": "YES", "outcome1Price": 0.45, "outcome2Id": "...", "outcome2Label": "NO", "outcome2Price": 0.55 }, { "title": "Will Tatum score 25+ points?", "outcome1Id": "...", "outcome1Label": "YES", "outcome1Price": 0.60, "outcome2Id": "...", "outcome2Label": "NO", "outcome2Price": 0.40 } ] } ``` ## Finding markets Use the `keyword` query parameter on the [List events](/api-reference/pm/list-events) endpoint to search for events by title: ```bash theme={null} GET /v1/pm/events?keyword=lagos&status=open ``` You can also filter by `category`, `status`, `trending`, and more. See the [List events](/api-reference/pm/list-events) reference for all available filters. # Fees Source: https://docs.bayse.markets/concepts/fees How trading fees work on Bayse Markets Bayse Markets charges a trading fee on each executed trade. Fees are always included in [quote](/api-reference/pm/get-quote) responses so you can review the estimated total cost before placing an order. ## How fees are calculated Fees use a **variance-based formula** that scales with price uncertainty: $$ \text{fee} = \text{feeRate} \times C \times P \times \max(1 - P,\; 0.5) $$ Where: * **feeRate** — the market's configured fee rate (e.g. 0.10 for 10%). * **C** — the number of shares (quantity). * **P** — the execution price per share (between 0 and 1). * **0.5** — the floor factor, ensuring the effective rate never drops below 50% of feeRate. The fee is also capped at a maximum of **\$1,000 per trade**. ### How the formula behaves The term `P × max(1 − P, 0.5)` controls how the fee scales with price. When `1 − P ≥ 0.5` (i.e. P ≤ 0.50), the variance branch `P × (1 − P)` applies. Above P = 0.50 the floor branch `P × 0.5` takes over, keeping fee behavior predictable at extreme prices. | Price (P) | Multiplier `P × max(1 − P, 0.5)` | Fee as % of trade value (feeRate = 0.10) | | --------- | ----------------------------------- | ---------------------------------------- | | 0.30 | `0.30 × 0.70 = 0.21` | **7.0%** | | 0.50 | `0.50 × 0.50 = 0.25` | **5.0%** | | 0.70 | `0.70 × 0.50 = 0.35` (floor begins) | **5.0%** | | 0.90 | `0.90 × 0.50 = 0.45` (floor) | **5.0%** | The "fee as % of trade value" column is `fee / (C × P)`, which simplifies to `feeRate × max(1 − P, 0.5)`. * Fees are **highest** when the price is near 0.50 (maximum uncertainty). * Fees are **lowest** at extreme prices (high certainty), but the floor ensures the rate never drops below `feeRate × 0.5` (5% at feeRate = 0.10). This means you pay proportionally less when trading outcomes the market is already confident about. ## Fees by market engine ### AMM markets On AMM markets, the fee is **built into the execution price** — it is not broken out as a separate `fee` field. The quote response shows: ```json theme={null} { "price": 0.7235, "currentMarketPrice": 0.72, "quantity": 138.21, "costOfShares": 100, "amount": 100 } ``` * `amount` — total you pay. * `costOfShares` — same as `amount` (fee is embedded). * `price` — effective per-share price, which already includes the fee. The difference between `price` and `currentMarketPrice` reflects the fee and any price impact. Orders execute instantly at the quoted price. ### CLOB markets On CLOB markets, fees apply only to **takers** (orders that match against resting orders on the book). Makers — orders that add liquidity to the book — pay no fee. | Role | Fee | | --------- | -------------------------------- | | **Taker** | Variance-based fee at fill price | | **Maker** | Free | Fees are calculated at the time each fill occurs. If your order fills in multiple parts at different prices, each fill's fee is calculated independently. The quote endpoint shows the estimated fee for the full order. How the fee is applied depends on the order side: **Buy orders** — the fee reduces shares received, not the amount you pay: ```json theme={null} { "price": 0.65, "quantity": 150.00, "costOfShares": 100, "fee": 2.50, "amount": 100, "completeFill": true } ``` * `amount` — total you pay. * `fee` — deducted from shares, not from the amount. * `quantity` — shares received after the fee. **Sell orders** — the fee reduces the proceeds you receive: ```json theme={null} { "price": 0.65, "quantity": 100, "costOfShares": 62.50, "fee": 2.50, "amount": 62.50, "completeFill": true } ``` * `amount` — net proceeds after the fee is deducted. * `fee` — deducted from proceeds, not from shares. * `quantity` — shares you are selling. `completeFill` tells you whether the full amount can be filled at the quoted price. If `false`, only partial liquidity is available on the book. ## Fees in quotes vs execution Always [get a quote](/api-reference/pm/get-quote) before placing an order. The quote gives you: * The expected fee. * The number of shares you will receive. * The estimated total cost including the fee. For **AMM markets**, the quoted price is exact — orders execute instantly at the quoted price. For **CLOB markets**, the actual fill price (and therefore fee) may differ from the quote if the order book changes between quoting and execution. ## Multi-currency Fees are automatically shown in the same currency as the quote. If you request a quote in NGN, the fee and all amounts in the response are in NGN. See [Multi-currency](/concepts/multi-currency) for more on how currencies work across the API. # Market data Source: https://docs.bayse.markets/concepts/market-data Price history, order books, trades, and ticker data ## Price history Historical prices for market outcomes: ```bash theme={null} GET /v1/pm/events/{eventId}/price-history?interval=1h ``` ## Order book Current bids and asks (CLOB markets only): ```bash theme={null} GET /v1/pm/books?marketId={marketId} ``` ## Recent trades Executed trades (CLOB markets only): ```bash theme={null} GET /v1/pm/trades?marketId={marketId} ``` ## Ticker Real-time market statistics (CLOB markets only): ```bash theme={null} GET /v1/pm/markets/{marketId}/ticker ``` For real-time streaming data, see the [WebSocket](/websocket/introduction) documentation. # Market engines Source: https://docs.bayse.markets/concepts/market-engines AMM and CLOB execution on Bayse Bayse supports two types of market engines. Each event uses one engine for all of its markets. ## AMM (Automated Market Maker) * Algorithmic pricing based on supply/demand. * No order book — instant execution. * Price adjusts automatically after each trade. * Liquidity is always available, however depth depends on individual markets. **Example trade:** ```json theme={null} { "side": "BUY", "outcome": "YES", "amount": 100, "currency": "USD" } ``` ## CLOB (Central Limit Order Book) * Traditional order book with bids and asks. * Users place limit orders at specific prices. * Orders match when prices overlap. * Liquidity depth is determined by market participants. **Example trade:** ```json theme={null} { "side": "BUY", "outcome": "YES", "amount": 100, "price": 0.65, "currency": "USD" } ``` The `engine` field on an event tells you which engine it uses. See [Order lifecycle](/concepts/order-lifecycle) for how orders behave differently under each engine. # Multi-currency support Source: https://docs.bayse.markets/concepts/multi-currency Trading in USD and NGN on Bayse All market prices are quoted as probabilities (0.00 to 1.00), but amounts are in your chosen currency. ## Supported currencies * **USD** — US Dollar (default). * **NGN** — Nigerian Naira. ## Currency base multiplier Each currency has a base multiplier that determines how probabilities convert to costs: | Currency | Base multiplier | Price of 0.65 | Winning payout | | -------- | --------------- | ------------- | -------------- | | USD | 1 | \$0.65/share | \$1.00/share | | NGN | 100 | ₦65.00/share | ₦100.00/share | The cost per share is: `price × base multiplier`. Losing shares pay out **0.00**. ## Specifying currency Add the `currency` query parameter to your requests: ```bash theme={null} # Get events with prices in NGN GET /v1/pm/events?currency=NGN # Get a specific event in USD (default) GET /v1/pm/events/evt_123?currency=USD # Place an order in NGN POST /v1/pm/events/evt_123/markets/mkt_456/orders { "side": "BUY", "outcome": "YES", "amount": 10000, "currency": "NGN" } ``` If you don't specify a currency, USD is used by default. ## Base multiplier vs. exchange rate The base multiplier applies to **trading amounts** — share prices, order costs, and display values. It converts probabilities to currency-denominated costs (e.g., a price of 0.65 costs 65.00 NGN per share). The following are converted from USD using the **live exchange rate**, not the base multiplier: * [Liquidity reward](/market-makers/liquidity-rewards) and [maker rebate](/market-makers/maker-rebates) payouts * Eligibility thresholds like `minNotionalOrderSize` and `minPayoutUsd` (evaluated in USD internally, then converted at the current rate) # Order lifecycle Source: https://docs.bayse.markets/concepts/order-lifecycle How orders execute and progress through statuses ## AMM orders ```mermaid theme={null} graph LR A[Place Order] --> B[Calculate Price] B --> C[Execute Immediately] C --> D[Update Position] D --> E[Return Confirmation] ``` AMM orders execute instantly at the calculated price. ## CLOB orders ```mermaid theme={null} graph LR A[Place Order] --> B{Time-in-Force?} B -->|GTC/GTD| C[Add to Book] B -->|FAK/FOK| D{Match Found?} C --> E{Match Found?} E -->|Yes| F[Execute] E -->|No| G[Wait for Match] D -->|Yes| H[Execute] D -->|No FAK| I[Cancel Unfilled] D -->|No FOK| J[Cancel All] F --> K[Update Position] H --> K G --> L[Can Cancel] ``` CLOB orders support different execution strategies through time-in-force options. ## Time-in-force options * **GTC (Good Till Cancel)** — stays on the book until filled or manually cancelled. Default for limit orders. * **FAK (Fill and Kill)** — execute as much as possible immediately, cancel any unfilled portion. * **FOK (Fill or Kill)** — execute entirely or cancel completely. No partial fills. * **GTD (Good Till Date)** — remains active until a specified expiry time. ## Order types * **Limit orders** — specify the max price you'll pay (buy) or min price you'll accept (sell). Execute at your price or better. * **Market orders** — execute immediately at the best available price. ## Order statuses 1. **pending** — order received and being validated. 2. **open** — active on the book, waiting for matches. 3. **partial\_filled** — some shares executed, remainder still on book (GTC/GTD only). 4. **filled** — completely executed. 5. **cancelled** — cancelled by user or system. 6. **rejected** — failed validation. 7. **expired** — GTD order reached expiry time without filling. ## Amending an open order CLOB orders in `open` or `partial_filled` status can have their `price`, their `size`, or both mutated in place via [batch amend](/api-reference/pm/batch-amend-orders). The order keeps its identity (`orderId` stays the same) and its current `filledSize` — the new `size` is the new TOTAL, so the new remaining becomes `newSize − filledSize`. * **Independently optional fields** — supply `newPrice` only, `newSize` only, or both. The omitted field keeps its current value. Items with neither field are rejected with `BAD_REQUEST`. * **Time priority** — preserved when the new `(price, size)` is unchanged or shrunk at the same price level. A price move (up or down) puts the order at the new tail of the new level, identical to a cancel-and-replace. * **Funding** — BID amends that grow the lock check wallet balance; ASK amends that grow the lock check the user's free share balance. Either can be rejected per-item with `INSUFFICIENT_BALANCE` or `INSUFFICIENT_SHARES`. * **Self-trade prevention** — fixed server policy: any same-user resting crosser at the new price is auto-cancelled (`CANCEL_OLDEST`). The order's resting `stpMode` set at placement governs matching, not amend. Crossers being amended in the same batch are skipped (no self-cancel). If you want an amend to fail rather than cancel a crosser, cancel + re-place instead. * **Terminal orders** — amend is rejected with `NOT_FOUND` once the order reaches `filled`, `cancelled`, `expired`, or `rejected`. * **AMM orders** — cannot be amended; cancel and re-place to change price/size. # Prediction markets Source: https://docs.bayse.markets/concepts/prediction-markets Understanding prediction markets on Bayse ## What are prediction markets? Prediction markets allow users to trade on the outcomes of future events. Users buy and sell shares in different outcomes, and the market prices reflect the collective wisdom of all participants. **Example:** **Event**: "Will it rain in Lagos tomorrow?" **Outcomes**: * Yes (currently trading at \$0.65) * No (currently trading at \$0.35) If you think rain is more likely than 65%, you might buy "Yes" shares. If it does rain, your shares are worth \$1.00 each. If it doesn't, they're worth \$0.00. ## Learn more The data model — events, markets, outcomes, and event types AMM vs CLOB execution Trading in USD and NGN Browse, quote, order, and portfolio Time-in-force, order types, and statuses Price history, order book, trades, and ticker Head-to-head, goal spread, and total goals markets for soccer # Sports markets (Soccer) Source: https://docs.bayse.markets/concepts/sports-markets Understanding sports market types, prop markets, and how they're linked ## Overview Sports markets on Bayse for soccer games come in ten types, all linked through the `sportGameSlug` field. | Value | Description | Event type | `propTeam` | | --------------------- | --------------------------------- | ---------- | ------------------------------------------ | | `TEAM_H2H_3WAY` | Match winner (home / draw / away) | combined | Home/away markets; `null` for draw | | `BOTH_TEAMS_TO_SCORE` | Did both teams score? | single | `null` | | `FIRST_TEAM_TO_SCORE` | Which team scored first? | combined | Home/away markets; `null` for "Neither" | | `GOAL_SPREAD` | Team wins by X+ goals | grouped | Populated (the team the spread applies to) | | `TOTAL_GOALS` | Total goals by both teams | grouped | `null` | | `TOTAL_GOALS_HOME` | Total goals by home team | grouped | Populated (home team) | | `TOTAL_GOALS_AWAY` | Total goals by away team | grouped | Populated (away team) | | `TOTAL_CORNERS` | Total corners, full match | grouped | `null` | | `FIRST_HALF_CORNERS` | Total corners, 1st half | grouped | `null` | | `SECOND_HALF_CORNERS` | Total corners, 2nd half | grouped | `null` | All event types for a single match share the same **`sportGameSlug`**, making it easy to discover related markets: ```bash theme={null} GET /v1/pm/events?sportGameSlug=bm-game-20260524-mci-avl # Returns all event types for this match ``` *** ## Market types **Combined event** — only one market resolves YES. Three mutually exclusive markets: * **Home Win** — The home team wins the match * **Away Win** — The away team wins the match * **Draw** — The match ends in a draw `propTeam` is populated for the home and away markets; `null` for the draw market. No `propLine` or `propDirection`. ```json theme={null} { "id": "event-123", "title": "Manchester City vs Aston Villa", "sportMarketType": "TEAM_H2H_3WAY", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "combined", "markets": [ { "id": "market-1", "title": "Manchester City to Win", "outcome1Price": 0.63, "propLine": null, "propDirection": null, "propTeam": { "id": "team-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" } }, { "id": "market-2", "title": "Draw", "outcome1Price": 0.25, "propLine": null, "propDirection": null, "propTeam": null }, { "id": "market-3", "title": "Aston Villa to Win", "outcome1Price": 0.12, "propLine": null, "propDirection": null, "propTeam": { "id": "team-2", "name": "Aston Villa FC", "slug": "aston-villa-fc", "league": "England - Premier League", "sport": "SOCCER" } } ] } ``` **Single market event** — one binary market. * Resolves **YES** if both teams score at least one goal * Resolves **NO** if either team fails to score * Covers the first 90 minutes of regular play plus stoppage time No `propLine`, `propDirection`, or `propTeam`. ```json theme={null} { "id": "event-124", "title": "Manchester City vs Aston Villa — Both Teams to Score", "sportMarketType": "BOTH_TEAMS_TO_SCORE", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "single", "markets": [ { "id": "market-10", "title": "Both Teams to Score", "outcome1Price": 0.65, "outcome2Price": 0.35, "propLine": null, "propDirection": null, "propTeam": null } ] } ``` **Combined event** — only one market resolves YES. Three mutually exclusive markets: * Home team scores first * Away team scores first * **Neither** — no goals scored (game ends 0-0) `propTeam` is populated for the home and away markets; `null` for "Neither". No `propLine` or `propDirection`. ```json theme={null} { "id": "event-125", "title": "Manchester City vs Aston Villa — First Team to Score", "sportMarketType": "FIRST_TEAM_TO_SCORE", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "combined", "markets": [ { "id": "market-11", "title": "Manchester City", "outcome1Price": 0.58, "propLine": null, "propDirection": null, "propTeam": { "id": "team-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" } }, { "id": "market-12", "title": "Aston Villa", "outcome1Price": 0.38, "propLine": null, "propDirection": null, "propTeam": { "id": "team-2", "name": "Aston Villa FC", "slug": "aston-villa-fc", "league": "England - Premier League", "sport": "SOCCER" } }, { "id": "market-13", "title": "Neither", "outcome1Price": 0.04, "propLine": null, "propDirection": null, "propTeam": null } ] } ``` **Grouped event** — markets resolve independently. Each market represents a team winning by more than a specific goal margin. Multiple markets can resolve YES. * `propLine` — The goal margin threshold (e.g., `1.5`, `2.5`) * `propDirection` — `"OVER"` * `propTeam` — The team this spread applies to ```json theme={null} { "id": "event-456", "title": "Manchester City vs Aston Villa - Goal Spread", "sportMarketType": "GOAL_SPREAD", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "grouped", "markets": [ { "id": "market-201", "title": "Manchester City wins by over 1.5 Goals", "propLine": 1.5, "propDirection": "OVER", "propTeam": { "id": "team-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" }, "outcome1Price": 0.58 }, { "id": "market-202", "title": "Manchester City wins by over 2.5 Goals", "propLine": 2.5, "propDirection": "OVER", "propTeam": { "id": "team-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" }, "outcome1Price": 0.35 }, { "id": "market-203", "title": "Aston Villa wins by over 1.5 Goals", "propLine": 1.5, "propDirection": "OVER", "propTeam": { "id": "team-2", "name": "Aston Villa FC", "slug": "aston-villa-fc", "league": "England - Premier League", "sport": "SOCCER" }, "outcome1Price": 0.15 } ] } ``` **Grouped event** — markets resolve independently. Each market is an over/under on the total goals scored by both teams combined. Covers the first 90 minutes of regular play plus stoppage time. * `propLine` — The goal total threshold (e.g., `2.5`, `3.5`) * `propDirection` — `"OVER"` * `propTeam` — `null` ```json theme={null} { "id": "event-789", "title": "Manchester City vs Aston Villa — Total Goals", "sportMarketType": "TOTAL_GOALS", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "grouped", "markets": [ { "id": "market-301", "title": "Over 1.5 goals", "propLine": 1.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.72 }, { "id": "market-302", "title": "Over 2.5 goals", "propLine": 2.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.58 }, { "id": "market-303", "title": "Over 3.5 goals", "propLine": 3.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.35 } ] } ``` **Grouped events** — markets resolve independently. * `TOTAL_GOALS_HOME` tracks goals scored by the home team only * `TOTAL_GOALS_AWAY` tracks goals scored by the away team only Each market has a `propLine` threshold and `propTeam` set to the relevant team. Structure is otherwise identical to `TOTAL_GOALS`. ```json theme={null} { "id": "event-790", "title": "Manchester City vs Aston Villa — Manchester City Total Goals", "sportMarketType": "TOTAL_GOALS_HOME", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "grouped", "markets": [ { "id": "market-401", "title": "1 or more", "propLine": 0.5, "propDirection": "OVER", "propTeam": { "id": "team-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" }, "outcome1Price": 0.80 }, { "id": "market-402", "title": "2 or more", "propLine": 1.5, "propDirection": "OVER", "propTeam": { "id": "team-1", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" }, "outcome1Price": 0.55 } ] } ``` **Grouped event** — markets resolve independently. Each market is an over/under on the total corners awarded to both teams across the full match (first half + second half, including stoppage time). * `propLine` — The corner total threshold (e.g., `7.5`, `9.5`) * `propDirection` — `"OVER"` * `propTeam` — `null` ```json theme={null} { "id": "event-850", "title": "Manchester City vs Aston Villa — Total Corners", "sportMarketType": "TOTAL_CORNERS", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "grouped", "markets": [ { "id": "market-501", "title": "8 or more", "propLine": 7.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.68 }, { "id": "market-502", "title": "10 or more", "propLine": 9.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.45 } ] } ``` **Grouped events** — markets resolve independently. * `FIRST_HALF_CORNERS` — corners in the first 45 minutes plus stoppage time * `SECOND_HALF_CORNERS` — corners in the second 45 minutes plus stoppage time Structure is identical to `TOTAL_CORNERS` but scoped to the relevant half. `propTeam` is always `null`. ```json theme={null} { "id": "event-851", "title": "Manchester City vs Aston Villa — 1st Half Corners", "sportMarketType": "FIRST_HALF_CORNERS", "sportGameSlug": "bm-game-20260524-mci-avl", "type": "grouped", "markets": [ { "id": "market-601", "title": "4 or more", "propLine": 3.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.55 }, { "id": "market-602", "title": "6 or more", "propLine": 5.5, "propDirection": "OVER", "propTeam": null, "outcome1Price": 0.28 } ] } ``` *** ## Market-level prop fields Present on markets within goal and corner prop events: | Field | Type | Example | Notes | | --------------- | -------------- | ------------ | ---------------------------------------------------------- | | `propLine` | number \| null | `2.5`, `9.5` | The threshold value | | `propDirection` | string \| null | `"OVER"` | Direction relative to threshold | | `propTeam` | object \| null | See below | Team the market applies to; `null` for match-level markets | ### propTeam object ```json theme={null} { "id": "uuid-string", "name": "Manchester City FC", "slug": "manchester-city-fc", "league": "England - Premier League", "sport": "SOCCER" } ``` # Trading flow Source: https://docs.bayse.markets/concepts/trading-flow How to browse, quote, trade, and manage positions on Bayse ## 1. Browse events List available events filtered by category, status, or search terms: ```bash theme={null} GET /v1/pm/events?category=sports&status=open ``` See [Finding markets](/concepts/events-markets-outcomes#finding-markets) for more filtering options. ## 2. Get quote Before placing an order, get a quote to see the expected price: ```bash theme={null} POST /v1/pm/events/{eventId}/markets/{marketId}/quote { "side": "BUY", "outcome": "YES", "amount": 100 } ``` **Response:** ```json theme={null} { "expectedPrice": 0.6532, "expectedShares": 153.12, "fee": 2.50, "total": 102.50 } ``` ## 3. Place order Execute the trade: ```bash theme={null} POST /v1/pm/events/{eventId}/markets/{marketId}/orders { "side": "BUY", "outcome": "YES", "amount": 100 } ``` ## 4. View portfolio Check your positions: ```bash theme={null} GET /v1/pm/portfolio ``` ## Positions and orders **Positions** are your holdings in a specific market outcome: * **Shares** — number of shares owned. * **Average price** — average price paid per share. * **Current value** — current market value. * **Unrealized P\&L** — profit/loss if sold at current price. **Orders** are instructions to buy or sell shares: * **Side** — buy or sell. * **Outcome** — which outcome to trade. * **Amount** — how much to spend/receive. * **Status** — pending, filled, cancelled. See [Order lifecycle](/concepts/order-lifecycle) for details on how orders execute and progress through statuses. ## Minting and burning In a binary market, a YES share and a NO share form a **complementary pair** — exactly one of them will pay out. This means a pair is always worth \$1.00 (or ₦100 in NGN markets). **Minting** lets you deposit funds and receive equal YES and NO shares. For example, depositing \$10 gives you 10 YES shares and 10 NO shares. This is useful when you want to sell one side on the order book while keeping the other. **Burning** is the reverse — you surrender equal YES and NO shares and receive funds back. For example, burning 10 shares returns \$10. This is useful for converting positions back to cash when you hold both outcomes. Neither operation affects market prices, since both sides are created or destroyed equally. See [Mint shares](/api-reference/pm/mint-shares) and [Burn shares](/api-reference/pm/burn-shares) for the API endpoints. # Examples Source: https://docs.bayse.markets/examples Real-world examples of what you can build with Bayse Markets API ## Trading and market access Explore practical examples of building trading applications and accessing market data. ### List and search events Browse available prediction markets with filters and search. Complete example with code samples ### Get real-time trade quotes Fetch current prices and trading costs before placing orders. Complete example with code samples ### Place limit orders on CLOB Submit limit orders to the Central Limit Order Book. Complete example with code samples ### Trade instantly on AMM Execute trades immediately using the Automated Market Maker. Complete example with code samples ### View portfolio positions Check your current positions and unrealized P\&L. Complete example with code samples ### Access historical price data Retrieve price history and market analytics. Complete example with code samples ## What's next? Each example will include: * Complete working code in multiple languages * Step-by-step explanations * Common pitfalls and how to avoid them * Best practices and optimization tips These examples are being developed. Check back soon for complete implementation guides. # FAQ Source: https://docs.bayse.markets/faq Frequently asked questions about Bayse Markets ## Trading Each market has exactly two outcomes. "Yes" shares represent a belief that the event will happen; "No" shares represent the opposite. When a market resolves, winning shares pay out `1.00 × currency base multiplier` (e.g., \$1.00 USD or ₦100.00 NGN) and losing shares pay out nothing. No. Prices are dynamic and adjust continuously based on trading activity and market sentiment. On AMM markets, prices update automatically after every trade. On CLOB markets, prices are set by the bids and asks of market participants. While most markets have a minimum order amount of 1.00 USD or 100.00 NGN, individual markets can specify their own minimum order amounts. Check each market's `minimumOrderAmount` from [List events](/api-reference/pm/list-events) or [Get event](/api-reference/pm/get-event) instead of hardcoding a single minimum. Yes. You can hold positions in as many markets as you want simultaneously, as long as you have sufficient funds in your wallet. Yes. You can buy or sell shares at any time while a market is open. Markets close at the closing date specified on the event, after which no new trades are accepted. It depends on the market engine: * **AMM markets**: Trades execute instantly and cannot be cancelled. To exit, sell your shares back to the market. * **CLOB markets**: Limit orders that have not yet been filled can be cancelled via `DELETE /v1/pm/orders/{orderId}`. Once an order is fully or partially filled, the filled portion cannot be reversed. Losing shares are worth nothing at resolution. The amount you invested in those shares is not returned. You can reduce your exposure before the market closes by selling your shares, though the sale price depends on current market conditions. ## Wallets and currencies Bayse Markets supports two currencies: * **USD** (US Dollar) — base multiplier of 1. Shares cost `price × 1` and win pays `$1.00`. * **NGN** (Nigerian Naira) — base multiplier of 100. Shares cost `price × 100` and win pays `₦100.00`. Specify the currency in your API requests using the `currency` field. USD is the default if omitted. You can hold balances in both USD and NGN wallets. However, you can only trade from one wallet at a time. If you have open positions in one currency, you must resolve or sell them before switching to place trades from the other currency. No. Withdrawals must go to an account registered under the same first and last name as your Bayse Markets account. Name mismatches will cause the withdrawal to fail. ## Accounts No. Each person may hold only one Bayse Markets account. Creating multiple accounts may result in the suspension or termination of all associated accounts. ## API There are two authentication flows: 1. **Session auth** (for managing API keys programmatically): Call `POST /v1/user/login` with your email and password to get a session `token` and `deviceId`. Use these as headers when creating, listing, revoking, or rotating API keys through the API. 2. **API key auth** (for trading and data): * **Read endpoints**: Include your public key in the `X-Public-Key` header. * **Write endpoints**: Include `X-Public-Key`, `X-Timestamp` (Unix seconds), and `X-Signature` (HMAC-SHA256 of `{timestamp}.{METHOD}.{path}.{bodyHash}`, base64-encoded using your secret key). You can also create and manage API keys in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys), or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. If you prefer, you can still manage them programmatically through the API. See [Authentication](/authentication) for full details and code examples. Yes. The login endpoint (`POST /v1/user/login`) is rate-limited to 1 request per 2 minutes per email address. Cache your session token and reuse it for API key operations. See [Rate limits](/rate-limits) for details. An **event** is the top-level prediction question (e.g., "NBA Finals 2024"). A **market** is a specific tradeable sub-question within that event (e.g., "Will the Lakers win?"). Single events contain one market; combined events contain multiple related markets. In the Bayse app, events display as "markets" and markets display as "sub-markets." * **AMM (Automated Market Maker)**: Algorithmic pricing with instant execution. No order book. Liquidity is always available. * **CLOB (Central Limit Order Book)**: Traditional limit order matching. You specify a price and the order fills when a counterparty matches. Supports GTC, FAK, FOK, and GTD time-in-force options. Subscribe to the Bayse Markets WebSocket feed at `wss://relay.bayse.markets/v1/ws`. Available channels are `orderbook`, `trades`, and `ticker`. See the [WebSocket](/websocket) documentation for details. # Introduction Source: https://docs.bayse.markets/index The culturally-native prediction market platform for Africa. Follow our quickstart guide to make your first API call. ## Key features CLOB for limit orders and AMM for instant execution. Choose your trading style. Trade in USD, NGN, and more with automatic collateral management. Live orderbook depth, trades, and ticker updates via WebSocket. HMAC-SHA256 signature-based authentication with API key management. ## Quick links See practical examples of what you can build with the API. Explore all available endpoints and their parameters. Learn about market engines, trading, and core concepts. Understand how to authenticate your API requests. ## Base URL ``` Production: https://relay.bayse.markets ``` # Liquidity rewards Source: https://docs.bayse.markets/market-makers/liquidity-rewards Earn rewards by providing liquidity on CLOB markets Bayse rewards users who maintain resting limit orders on CLOB markets. If you keep tight, two-sided quotes near the midpoint, you earn a share of a per-market reward pool paid out at the end of each epoch. Liquidity rewards are currently only available for limit orders placed via the [Bayse Relay](/quickstart). Orders placed through the Bayse mobile or web apps are not eligible at this time. ## How it works 1. **Place limit orders** on both sides (bid and ask) of a CLOB market. 2. **Keep orders resting** — the system samples the orderbook every minute and scores qualifying orders. 3. **Get paid** — at the end of each epoch, your share of the reward pool is credited to your wallet. Markets with active liquidity rewards include a `liquidityReward` field in their event data: ```json theme={null} { "liquidityReward": { "configId": "aa9024a6-fca5-4ed8-a5b9-124584d61f04", "rewardPool": 100.0, "maxSpreadCents": 5, "minNotionalOrderSize": 5.0 } } ``` If the field is `null` or absent, the market has no active reward program. `maxSpreadCents` is a price distance in cents (e.g., 5 = 0.05). In NGN, this appears as ₦5.00 (0.05 × 100 base multiplier). `rewardPool` and `minNotionalOrderSize` are denominated in USD. Payouts are converted to your trading currency using the live exchange rate at payout time. See [Multi-currency support](/concepts/multi-currency) for details. ## Eligibility For an order to count toward your score in a given sample: * **Minimum notional value** — `remaining shares * price` must meet the market's `minNotionalOrderSize`. * **Maximum spread** — the order must be within `maxSpreadCents` cents of the current midpoint (e.g., 5 cents = 0.05). * **Minimum rest time** — the order must have been resting on the book for at least a few seconds (typically 3s). This prevents rapid place-cancel cycling. ## Scoring Each qualifying order is scored based on how close it is to the midpoint: * **Tighter orders score higher.** An order right at the midpoint gets full weight; an order at the edge of the qualifying spread gets zero. * **Larger orders score higher.** Score is proportional to remaining order size multiplied by the tightness weight. Your per-sample score is the sum of all your qualifying orders' scores. ### Two-sided quoting bonus Users who quote both sides (bid and ask) earn significantly more than single-sided quoters. * If you only quote one side, your score is divided by a penalty factor (e.g., 3x). * If you quote both sides roughly equally, you earn close to your full score. * At extreme prices (below 0.10 or above 0.90), only balanced two-sided quoting is rewarded — single-sided liquidity earns zero. ## Epochs and payouts Rewards are distributed in **epochs** — fixed time windows (commonly 24 hours). At the end of each epoch: 1. Your time-weighted average share is computed from all the samples you participated in. 2. Your payout is `rewardPool * (your average share)`. 3. The amount is converted to your trading currency and credited to your wallet. If you were active for only part of an epoch, your payout scales proportionally. For example, if you maintained a 10% share but were only present for half the sampling periods, you receive approximately 5% of the pool. ## Tracking your rewards Use the API to monitor your rewards: * **[GET /v1/pm/liquidity-rewards](/api-reference/pm/liquidity-rewards)** — paginated history of completed epoch payouts. * **[GET /v1/pm/liquidity-rewards/active](/api-reference/pm/liquidity-rewards-active)** — in-progress accumulation with estimated payouts that update in real time. ## Tips for maximizing rewards * **Quote both sides.** The two-sided bonus is substantial — even small orders on your weaker side improve your score. * **Stay tight.** Orders closer to the midpoint are weighted much more heavily than those near the edge. * **Stay consistent.** Rewards are sampled every minute, so orders that remain on the book accumulate more samples. * **Size matters.** Larger resting orders earn proportionally more, but only if they remain within the qualifying spread. # Maker rebates Source: https://docs.bayse.markets/market-makers/maker-rebates Earn rebates from taker fees by providing maker liquidity on CLOB markets Bayse shares a portion of taker fees with makers who provide liquidity on CLOB markets. Every time a taker executes against your resting limit order, you earn a share of the fees they paid. Rebates are calculated daily and credited to your wallet automatically. Maker rebates are currently only available for limit orders placed via the [Bayse Relay](/quickstart). Orders placed through the Bayse mobile or web apps are not eligible at this time. ## How it works 1. **Place limit orders** on a CLOB market. Orders that rest on the book make you a *maker*. 2. **Earn when takers fill your orders** — each time another trader matches against your resting order, a portion of the taker fee is allocated to the rebate pool. 3. **Get paid daily** — at the end of each epoch (UTC calendar day), your share of the rebate pool is credited to your wallet. Markets with active maker rebates include a `makerRebate` field in their market data: ```json theme={null} { "makerRebate": { "configId": "aa9024a6-fca5-4ed8-a5b9-124584d61f04", "rebatePercentage": 0.5, "minPayoutUsd": 0.10 } } ``` If the field is `null` or absent, the market has no active rebate program. * `rebatePercentage` — the fraction of taker fees allocated to the rebate pool (e.g., 0.5 = 50%). * `minPayoutUsd` — the minimum USD payout threshold. Payouts below this amount are forfeited. All configuration values are denominated in USD. At payout time, amounts are converted to your trading currency using the current exchange rate. ## How rebates are calculated ### Rebate pool The rebate pool for each epoch is: ``` rebatePool = totalTakerFees * rebatePercentage ``` For example, if a market collects 200 USD in taker fees during an epoch and the rebate percentage is 50%, the rebate pool is 100 USD. ### Your share Your share of the rebate pool is proportional to your maker volume: ``` yourRebate = rebatePool * (yourMakerVolume / totalMakerVolume) ``` Maker volume for each trade is `price * size` — the notional value of the fill where your resting order was the maker side. ### Example | | Maker volume | Share | Rebate | | --------- | ------------ | -------- | ---------- | | You | 5,000 | 50% | 50.00 | | Trader B | 3,000 | 30% | 30.00 | | Trader C | 2,000 | 20% | 20.00 | | **Total** | **10,000** | **100%** | **100.00** | ## Epochs and payouts Rebates are distributed in **epochs** — fixed 24-hour windows aligned to UTC calendar days (00:00 UTC to 00:00 UTC). At the end of each epoch: 1. All trades where your resting orders were filled are aggregated. 2. Your maker volume share is computed against the total maker volume for the market. 3. Your payout is calculated and, if it meets the minimum payout threshold, credited to your wallet in your trading currency. If your calculated rebate is below the minimum payout threshold (typically 0.10 USD), it will not be credited. ## Tracking your rebates Use the API to monitor your rebates: * **[GET /v1/pm/maker-rebates](/api-reference/pm/maker-rebates)** — paginated history of completed epoch payouts. * **[GET /v1/pm/maker-rebates/active](/api-reference/pm/maker-rebates-active)** — in-progress accumulation with estimated payouts that update as trades occur. ## Maker rebates vs. liquidity rewards Both programs reward liquidity providers, but they work differently: | | Maker rebates | Liquidity rewards | | ------------------ | ------------------------------------------ | ---------------------------------------------------- | | **What earns** | Your resting orders being filled by takers | Keeping resting orders on the book (even unfilled) | | **Funding source** | Taker fees | Fixed reward pool per market | | **Scoring basis** | Maker volume (price x size of fills) | Tightness to midpoint, order size, two-sided quoting | | **Epoch length** | 24 hours (UTC day) | Configurable (commonly 24 hours) | A market can have both programs active simultaneously — you can earn liquidity rewards for keeping orders on the book *and* maker rebates when those orders get filled. ## Tips for maximizing rebates * **Size your orders well.** Larger fills generate more maker volume and a bigger share of the rebate pool. * **Stay competitive.** Orders closer to the midpoint are more likely to be filled by takers, generating more volume. * **Be consistent.** Maintaining resting orders throughout the day ensures you capture fills across the full epoch. * **Trade active markets.** Markets with higher taker volume generate larger rebate pools. # Quickstart Source: https://docs.bayse.markets/quickstart Make your first API call to Bayse Markets in minutes ## Get your API credentials Before you can trade on Bayse Markets programmatically, you need an API key pair. This quickstart assumes you already have a Bayse Markets account. If not, sign up at [link.bayse.markets](https://link.bayse.markets/app). ### Step 1: Create an API key Create an API key in the Bayse web app at [app.bayse.markets/settings/api-keys](https://app.bayse.markets/settings/api-keys). Or in the web app, via **More** > **Account Settings** > **API Keys** in the **Developer Tool** section. If you prefer to manage API keys programmatically, see [Manage API keys programmatically](#manage-api-keys-programmatically). Save your **secret key** securely. You need it to sign write requests, and you may not be able to view it again later. ### Step 2: Make a read request Try a simple read request to list prediction market events: ```bash theme={null} curl -X GET "https://relay.bayse.markets/v1/pm/events?limit=10" \ -H "X-Public-Key: pk_live_abcdef123456" ``` ```json theme={null} { "events": [ { "id": "evt_123", "title": "Will it rain tomorrow?", "category": "weather", "status": "active", "markets": [ { "id": "mkt_456", "question": "Yes or No?", "outcomes": ["Yes", "No"], "engine": "AMM" } ] } ], "pagination": { "total": 50, "limit": 10, "offset": 0 } } ``` ### Step 3: Make a signed write request For write operations (like placing orders), you need to sign your request with HMAC-SHA256. The signing payload format is `{timestamp}.{METHOD}.{path}.{bodyHash}`: ```bash cURL theme={null} # Set your credentials PUBLIC_KEY="pk_live_abcdef123456" SECRET_KEY="sk_live_secret789xyz" TIMESTAMP=$(date +%s) METHOD="POST" URL_PATH="/v1/pm/events/evt_123/markets/mkt_456/orders" BODY='{"side":"BUY","outcome":"YES","amount":100,"currency":"USD"}' # Compute body hash and create signature BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/.*= //') PAYLOAD="${TIMESTAMP}.${METHOD}.${URL_PATH}.${BODY_HASH}" SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET_KEY" -binary | base64) # Make the request curl -X POST "https://relay.bayse.markets${URL_PATH}" \ -H "X-Public-Key: ${PUBLIC_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Signature: ${SIGNATURE}" \ -H "Content-Type: application/json" \ -d "$BODY" ``` ```javascript Node.js theme={null} import crypto from 'crypto'; const PUBLIC_KEY = 'pk_live_abcdef123456'; const SECRET_KEY = 'sk_live_secret789xyz'; const timestamp = Math.floor(Date.now() / 1000); const method = 'POST'; const path = '/v1/pm/events/evt_123/markets/mkt_456/orders'; const body = JSON.stringify({ side: 'BUY', outcome: 'YES', amount: 100, currency: 'USD' }); // Compute body hash (SHA-256 hex digest) const bodyHash = crypto.createHash('sha256').update(body).digest('hex'); // Create HMAC signature of the full payload const payload = `${timestamp}.${method}.${path}.${bodyHash}`; const signature = crypto .createHmac('sha256', SECRET_KEY) .update(payload) .digest('base64'); // Make the request const response = await fetch(`https://relay.bayse.markets${path}`, { method, headers: { 'X-Public-Key': PUBLIC_KEY, 'X-Timestamp': timestamp.toString(), 'X-Signature': signature, 'Content-Type': 'application/json', }, body, }); ``` ```python Python theme={null} import hmac import hashlib import base64 import json import time import requests PUBLIC_KEY = 'pk_live_abcdef123456' SECRET_KEY = 'sk_live_secret789xyz' timestamp = int(time.time()) method = 'POST' path = '/v1/pm/events/evt_123/markets/mkt_456/orders' body = json.dumps({'side': 'BUY', 'outcome': 'YES', 'amount': 100, 'currency': 'USD'}) # Compute body hash and create signature body_hash = hashlib.sha256(body.encode()).hexdigest() payload = f'{timestamp}.{method}.{path}.{body_hash}' signature = base64.b64encode( hmac.new(SECRET_KEY.encode(), payload.encode(), hashlib.sha256).digest() ).decode() # Make the request response = requests.post( f'https://relay.bayse.markets{path}', headers={ 'X-Public-Key': PUBLIC_KEY, 'X-Timestamp': str(timestamp), 'X-Signature': signature, 'Content-Type': 'application/json', }, data=body, ) ``` ```json theme={null} { "id": "ord_789", "eventId": "evt_123", "marketId": "mkt_456", "side": "buy", "outcomeIndex": 0, "amount": 100, "status": "filled", "filledAt": "2026-02-16T10:35:00Z" } ``` ## Manage API keys programmatically Use this flow if you want to create, rotate, revoke, or list API keys through the API. ### Log in Authenticate with your Bayse account to get a session token: ```bash theme={null} curl -X POST https://relay.bayse.markets/v1/user/login \ -H "Content-Type: application/json" \ -d '{ "email": "you@example.com", "password": "your-password" }' ``` ```json theme={null} { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "deviceId": "d_abc123", "userId": "usr_456def" } ``` Save the `token` and `deviceId` for API key management requests. ### Create an API key with the API Use your session token and device ID to create an API key: ```bash theme={null} curl -X POST https://relay.bayse.markets/v1/user/me/api-keys \ -H "x-auth-token: YOUR_TOKEN" \ -H "x-device-id: YOUR_DEVICE_ID" \ -H "Content-Type: application/json" \ -d '{"name": "My API Key"}' ``` Each API key must have a unique name. If you already have a key called `"My API Key"`, choose a different name. ```json theme={null} { "id": "key_abc123", "publicKey": "pk_live_abcdef123456", "secretKey": "sk_live_secret789xyz", "name": "My API Key", "createdAt": "2026-02-16T10:30:00Z" } ``` Save your **secretKey** securely — it's only shown once. ## Next steps Learn about API key authentication and HMAC signing in detail. Explore all available endpoints and parameters. Understand how prediction markets work on Bayse. Learn how to handle API errors gracefully. # Rate limits Source: https://docs.bayse.markets/rate-limits Understand the rate limits applied to Bayse Markets API endpoints ## Overview Rate limits protect the API from abuse and ensure fair usage for all users. When you exceed a rate limit, the API returns a `429 Too Many Requests` response with a `Retry-After` header and a `retryAfter` field indicating how many seconds to wait before retrying. ## API key rate limits Authenticated endpoints are rate-limited **per API key** based on whether the endpoint is a read or write operation. | Type | Limit | Scope | | --------------- | ------------------ | ----------- | | Read endpoints | 30 requests/second | Per API key | | Write endpoints | 20 requests/second | Per API key | Read endpoints are those that require `X-Public-Key` only. Write endpoints are those that require `X-Public-Key`, `X-Timestamp`, and `X-Signature`. See the [Authentication guide](/authentication) for details. When the limit is exceeded: ```json theme={null} { "message": "Rate limit exceeded. Please try again later.", "retryAfter": 1 } ``` The response also includes a `Retry-After` header with the number of seconds to wait. ## API key management API key management endpoints are rate-limited **per session token** to prevent abuse. Each user can have a maximum of **2 active API keys** at any time. | Operation | Limit | Window | Scope | | -------------- | ---------- | ---------- | ----------- | | Create API key | 2 requests | 30 minutes | Per session | | Revoke API key | 2 requests | 30 minutes | Per session | | Rotate API key | 2 requests | 30 minutes | Per session | Rotation shares the revoke limit since it revokes the existing key and creates a new one. When the limit is exceeded: ```json theme={null} { "message": "Rate limit exceeded. Please try again later.", "retryAfter": 900 } ``` The `retryAfter` value reflects the actual time remaining until the next request is allowed. ## Login endpoint The login endpoint (`POST /v1/user/login`) is rate-limited to **1 request per 2 minutes** per email address. | Endpoint | Limit | Window | Scope | | --------------------- | --------- | --------- | ----------------- | | `POST /v1/user/login` | 1 request | 2 minutes | Per email address | When the limit is exceeded: ```json theme={null} { "message": "Too many login attempts. Please try again later.", "retryAfter": 120 } ``` ## Handling rate limit errors ```javascript Node.js theme={null} async function fetchWithRetry(url, options) { const response = await fetch(url, options); if (response.status === 429) { const { retryAfter } = await response.json(); console.log(`Rate limited. Retrying in ${retryAfter} seconds...`); await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); return fetchWithRetry(url, options); } return response.json(); } ``` ### Best practices * **Handle 429 gracefully** — if you receive a rate limit response, wait for the `retryAfter` duration before retrying. * **Cache your session token** — after a successful login, store the returned `token` and `deviceId` and reuse them for subsequent API key operations. There's no need to log in again for each request. * **Don't poll the login endpoint** — the login endpoint is intended for one-time session creation, not repeated calls. * **Batch where possible** — if you need data for multiple markets, use list endpoints instead of making individual requests. # Connection Source: https://docs.bayse.markets/websocket/connection How to connect, message format, keepalive, and reconnection ## Connecting Open a WebSocket connection to one of the available endpoints: ``` wss://socket.bayse.markets/ws/v1/markets wss://socket.bayse.markets/ws/v1/user wss://socket.bayse.markets/ws/v1/realtime ``` The `/ws/v1/user` endpoint requires per-message authentication. See [User orders](/websocket/user-orders) for details. On a successful connection, the server sends a `connected` message: ```json theme={null} { "type": "connected", "status": "connected", "clientId": "a1b2c3d4-...", "message": "Successfully connected to WebSocket server", "timestamp": 1700000000000 } ``` ```javascript JavaScript theme={null} const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/markets"); ws.addEventListener("open", () => { console.log("connected"); }); ``` ```python Python theme={null} import asyncio import websockets async def connect(): async with websockets.connect("wss://socket.bayse.markets/ws/v1/markets") as ws: print("connected") async for message in ws: print(message) asyncio.run(connect()) ``` ```go Go theme={null} package main import ( "fmt" "log" "github.com/gorilla/websocket" ) func main() { conn, _, err := websocket.DefaultDialer.Dial("wss://socket.bayse.markets/ws/v1/markets", nil) if err != nil { log.Fatal(err) } defer conn.Close() fmt.Println("connected") } ``` ## Message format All messages are JSON. The server may batch multiple JSON objects into a single WebSocket frame separated by newlines (`\n`). Clients must split on newlines before parsing each line: ```javascript theme={null} ws.addEventListener("message", (event) => { for (const line of event.data.split("\n")) { if (line.trim()) { const msg = JSON.parse(line); // handle msg } } }); ``` ### Client messages Messages sent from the client to the server follow this structure: ```json theme={null} { "type": "subscribe", "channel": "prices", "eventId": "EVENT_ID", "marketId": "MARKET_ID", "marketIds": ["MARKET_ID"], "currency": "USD", "symbols": ["BTCUSDT"], "room": "ROOM_NAME" } ``` Only include the fields relevant to the channel you are using. The `type` field is always required. | Field | Type | Description | | ----------- | --------- | ------------------------------------------------------------------------------- | | `type` | string | **Required.** One of `subscribe`, `unsubscribe`, `ping`. | | `channel` | string | Subscription channel (e.g., `activity`, `prices`, `orderbook`, `asset_prices`). | | `eventId` | string | Prediction event UUID. Required for `activity` and `prices`. | | `marketId` | string | Market UUID. Optional filter for `activity`. | | `marketIds` | string\[] | Market UUIDs (max 10). Required for `orderbook`. | | `currency` | string | `USD` or `NGN`. Optional for `orderbook`. | | `symbols` | string\[] | Asset symbols (e.g., `BTCUSDT`). Required for `asset_prices`. | | `room` | string | Room name. Required for `unsubscribe`. | ### Server messages Messages sent from the server to the client: ```json theme={null} { "type": "price_update", "data": { ... }, "timestamp": 1700000000000 } ``` | Field | Type | Description | | ----------- | ------ | ---------------------------------------------- | | `type` | string | The event type (e.g., `price_update`). | | `data` | object | Event payload. Varies by event type. | | `timestamp` | number | Unix timestamp (milliseconds). | | `status` | string | Present on the `connected` message. | | `clientId` | string | Your assigned client ID. | | `message` | string | Human-readable text when provided. | | `room` | string | Room name for subscribe/unsubscribe responses. | ## Subscribing and unsubscribing Subscribe to a channel by sending a `subscribe` message with the required fields: ```json theme={null} { "type": "subscribe", "channel": "prices", "eventId": "EVENT_ID" } ``` Unsubscribe by sending an `unsubscribe` message with the room name: ```json theme={null} { "type": "unsubscribe", "room": "prices:EVENT_ID" } ``` The server confirms with an `unsubscribed` message: ```json theme={null} { "type": "unsubscribed", "room": "prices:EVENT_ID", "message": "Unsubscribed from: prices:EVENT_ID", "timestamp": 1700000000000 } ``` ### Room naming Room names are constructed from the channel and subscription parameters: | Subscription | Room name | | ------------------------------- | ------------------------------- | | Activity feed | `activity:` | | Activity feed (market-specific) | `activity::` | | Price updates | `prices:` | | Orderbook (USD) | `orderbook:` | | Orderbook (NGN) | `orderbook::NGN` | | Orders | `orders::` | | Asset prices | `asset_prices:` | ## Keepalive The server sends WebSocket-level ping frames every \~54 seconds. Most WebSocket libraries handle pong replies automatically. You can also send an application-level ping to verify the connection is alive: ```json theme={null} { "type": "ping" } ``` The server replies with: ```json theme={null} { "type": "pong", "timestamp": 1700000000000 } ``` If the server does not receive a pong within 60 seconds, the connection is closed. Ensure your client handles WebSocket ping/pong frames. ## Reconnection Connections can drop due to network issues, server restarts, or idle timeouts. Implement reconnection with exponential backoff: ```javascript JavaScript theme={null} function connect() { const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/markets"); let attempt = 0; ws.addEventListener("open", () => { attempt = 0; // re-subscribe to channels }); ws.addEventListener("close", () => { const delay = Math.min(1000 * 2 ** attempt, 30000); attempt++; setTimeout(connect, delay); }); return ws; } ``` ```python Python theme={null} import asyncio import websockets async def connect(): attempt = 0 while True: try: async with websockets.connect("wss://socket.bayse.markets/ws/v1/markets") as ws: attempt = 0 # re-subscribe to channels async for message in ws: print(message) except websockets.ConnectionClosed: delay = min(2 ** attempt, 30) attempt += 1 await asyncio.sleep(delay) ``` ```go Go theme={null} func connectWithRetry() { attempt := 0 for { conn, _, err := websocket.DefaultDialer.Dial( "wss://socket.bayse.markets/ws/v1/markets", nil, ) if err != nil { delay := time.Duration(math.Min(math.Pow(2, float64(attempt)), 30)) * time.Second attempt++ time.Sleep(delay) continue } attempt = 0 // re-subscribe to channels handleConnection(conn) } } ``` ## Limits | Limit | Value | Scope | | ------------ | ------------------ | -------------- | | Message rate | 10 messages/second | Per connection | WebSocket upgrades are rate-limited per IP. # Asset prices Source: https://docs.bayse.markets/websocket/crypto-prices Live crypto and FX price feeds ## Overview The `/ws/v1/realtime` endpoint streams live asset prices. No authentication is required. Prices update approximately every second per symbol. ``` wss://socket.bayse.markets/ws/v1/realtime ``` ## Available symbols | Source | Symbols | | ---------- | -------------------------------------- | | Binance | `BTCUSDT`, `ETHUSDT`, `SOLUSDT` | | TwelveData | `XAUUSD`, `EURUSD`, `GBPUSD`, `USDNGN` | ## Subscribe Send a `subscribe` message with the symbols you want to track: ```json theme={null} { "type": "subscribe", "channel": "asset_prices", "symbols": ["BTCUSDT", "ETHUSDT", "SOLUSDT"] } ``` You can subscribe to one or more symbols in a single message. ## Events received **`asset_price`** — A price tick for a subscribed symbol. ```json theme={null} { "type": "asset_price", "data": { "symbol": "BTCUSDT", "price": 67432.15, "timestamp": 1700000000000 }, "timestamp": 1700000000000 } ``` | Field | Description | | ---------------- | ---------------------------------- | | `data.symbol` | The ticker symbol. | | `data.price` | Current price. | | `data.timestamp` | Source event time in milliseconds. | | `timestamp` | Server timestamp in milliseconds. | ## Unsubscribe Each symbol has its own room. To unsubscribe from a specific symbol: ```json theme={null} { "type": "unsubscribe", "room": "asset_prices:BTCUSDT" } ``` ## Full example ```javascript JavaScript theme={null} const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/realtime"); ws.addEventListener("open", () => { ws.send(JSON.stringify({ type: "subscribe", channel: "asset_prices", symbols: ["BTCUSDT", "ETHUSDT", "SOLUSDT"] })); }); ws.addEventListener("message", (event) => { for (const line of event.data.split("\n")) { if (!line.trim()) continue; const msg = JSON.parse(line); if (msg.type === "asset_price") { console.log(`${msg.data.symbol}: $${msg.data.price}`); } } }); ``` ```python Python theme={null} import asyncio import json import websockets async def stream_prices(): async with websockets.connect("wss://socket.bayse.markets/ws/v1/realtime") as ws: await ws.send(json.dumps({ "type": "subscribe", "channel": "asset_prices", "symbols": ["BTCUSDT", "ETHUSDT", "SOLUSDT"] })) async for message in ws: for line in message.split("\n"): if not line.strip(): continue msg = json.loads(line) if msg["type"] == "asset_price": data = msg["data"] print(f"{data['symbol']}: ${data['price']}") asyncio.run(stream_prices()) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "log" "strings" "github.com/gorilla/websocket" ) func main() { conn, _, err := websocket.DefaultDialer.Dial( "wss://socket.bayse.markets/ws/v1/realtime", nil, ) if err != nil { log.Fatal(err) } defer conn.Close() conn.WriteJSON(map[string]any{ "type": "subscribe", "channel": "asset_prices", "symbols": []string{"BTCUSDT", "ETHUSDT", "SOLUSDT"}, }) for { _, data, err := conn.ReadMessage() if err != nil { log.Fatal(err) } for _, line := range strings.Split(string(data), "\n") { if strings.TrimSpace(line) == "" { continue } var msg map[string]any json.Unmarshal([]byte(line), &msg) if msg["type"] == "asset_price" { d := msg["data"].(map[string]any) fmt.Printf("%s: %v\n", d["symbol"], d["price"]) } } } } ``` # Errors & rate limits Source: https://docs.bayse.markets/websocket/errors Error handling and rate limits for WebSocket connections ## Error format When the server encounters an error processing a message, it sends an error event: ```json theme={null} { "type": "error", "data": { "message": "Event ID is required" }, "timestamp": 1700000000000 } ``` The `data.message` field contains a human-readable description of the error. ## Common errors ### Invalid message format Sent when the message is not valid JSON. ```json theme={null} { "type": "error", "data": { "message": "Invalid message format" }, "timestamp": 1700000000000 } ``` ### Missing required fields Sent when a required field is missing from the message. ```json theme={null} { "type": "error", "data": { "message": "Event ID is required" }, "timestamp": 1700000000000 } ``` Other examples: * "Symbols required to subscribe to asset prices" * "Room name is required" ### Unknown message type Sent when the `type` field does not match any known message type. ```json theme={null} { "type": "error", "data": { "message": "unknown message type: INVALID_TYPE" }, "timestamp": 1700000000000 } ``` ### Unsupported values Sent when a field value is not supported. ```json theme={null} { "type": "error", "data": { "message": "Unsupported currency. Supported currencies are USD and NGN" }, "timestamp": 1700000000000 } ``` ```json theme={null} { "type": "error", "data": { "message": "Unsupported symbol: DOGEUSDT. Supported symbols are BTCUSDT, ETHUSDT, SOLUSDT, XAUUSD, EURUSD, GBPUSD, USDNGN" }, "timestamp": 1700000000000 } ``` ### Wrong endpoint Sent when you try to subscribe to a channel on the wrong endpoint. ```json theme={null} { "type": "error", "data": { "message": "channel \"asset_prices\" is not available on /ws/v1/markets; use /ws/v1/realtime instead" }, "timestamp": 1700000000000 } ``` ### Rate limit exceeded Sent when the client sends too many messages. ```json theme={null} { "type": "error", "data": { "message": "rate limit exceeded: too many messages" }, "timestamp": 1700000000000 } ``` ## Rate limits | Limit | Value | Scope | | ------------ | ------------------ | -------------- | | Message rate | 10 messages/second | Per connection | WebSocket upgrades are rate-limited per IP. When the message rate is exceeded, the server sends a rate limit error but keeps the connection open. Slow down your message rate to resume normal operation. ## Best practices * **Always handle `error` events.** Check `msg.type === "error"` and log the message for debugging. * **Don't retry immediately on errors.** If you receive a rate limit error, slow down before sending more messages. * **Use the right endpoint.** Each endpoint only accepts specific channels. See the error message for which endpoint to use. * **Validate before sending.** Check that required fields like `eventId`, `marketIds`, and `symbols` are present before sending to avoid unnecessary round-trips. # WebSocket Source: https://docs.bayse.markets/websocket/introduction Real-time data feeds for Bayse Markets ## Overview The Bayse Markets WebSocket API provides real-time streaming data for market activity, price changes, order book updates, and live asset prices. **Base URL:** `wss://socket.bayse.markets` ## Endpoints | Endpoint | Auth | Description | | ----------------- | ----------- | --------------------------------------------------------------- | | `/ws/v1/markets` | None | Market activity feeds, price updates, and order book snapshots. | | `/ws/v1/user` | Per-message | Fill updates for your orders. | | `/ws/v1/realtime` | None | Live asset prices (crypto and FX). | ## Quick example Connect to the markets endpoint and subscribe to price updates: ```javascript theme={null} const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/markets"); ws.addEventListener("open", () => { ws.send(JSON.stringify({ type: "subscribe", channel: "prices", eventId: "EVENT_ID" })); }); ws.addEventListener("message", (event) => { for (const line of event.data.split("\n")) { if (line.trim()) { const msg = JSON.parse(line); console.log(msg.type, msg.data); } } }); ``` The server may batch multiple JSON messages into a single WebSocket frame separated by newlines. Always split on `\n` before parsing. See [Connection](/websocket/connection) for details. ## Next steps Message format, keepalive, and reconnection. Activity feeds, price updates, and order book snapshots. Real-time fill updates for your orders. Live crypto and FX price feeds. Error handling and rate limits. # Market data Source: https://docs.bayse.markets/websocket/market-data Real-time activity feeds, price updates, and order book snapshots ## Overview The `/ws/v1/markets` endpoint streams real-time market data. No authentication is required. ``` wss://socket.bayse.markets/ws/v1/markets ``` Four subscription types are available: | Subscription | Channel | Server event type | Description | | ------------- | ------------- | ------------------------- | ------------------------------------------------------ | | Activity feed | `activity` | `buy_order`, `sell_order` | Buy and sell order activity for an event. | | Price updates | `prices` | `price_update` | Market price changes for an event. | | Orderbook | `orderbook` | `orderbook_update` | Order book snapshots for a market. | | User trades | `user_trades` | `buy_order`, `sell_order` | Trade activity for a specific user across all markets. | ## Activity feed Subscribe to trade activity for a prediction event. Optionally filter by a specific market. ### Subscribe ```json theme={null} { "type": "subscribe", "channel": "activity", "eventId": "EVENT_ID" } ``` To filter activity to a specific market within the event: ```json theme={null} { "type": "subscribe", "channel": "activity", "eventId": "EVENT_ID", "marketId": "MARKET_ID" } ``` Room names: `activity:` or `activity::` ### Events received **`buy_order`** — A buy order was filled. ```json theme={null} { "type": "buy_order", "data": { "user": { "id": "usr_8b5c3c3a", "tag": "prediction_pro", "imageUrl": "https://cdn.bayse.markets/users/8b5c3c3a.png" }, "order": { "id": "ord_7f5e2a1c", "amount": 100.00, "quantity": 150.0, "price": 0.65, "status": "FILLED", "type": "BUY", "outcome": "YES", "outcomeLabel": "Yes", "currency": "USD", "createdAt": "2026-02-17T10:30:00Z", "updatedAt": "2026-02-17T10:30:01Z" }, "event": { "id": "evt_123", "slug": "us-fed-rate-cut-2026", "title": "Will the Fed cut rates in 2026?", "type": "SINGLE_MARKET", "createdAt": "2026-01-10T09:00:00Z", "imageUrl": "https://cdn.bayse.markets/events/fed.png" }, "market": { "id": "mkt_456", "title": "Yes or No", "imageUrl": null } }, "timestamp": 1700000000000 } ``` **`sell_order`** — A sell order was filled. ```json theme={null} { "type": "sell_order", "data": { "user": { "id": "usr_9a1d2f4b", "tag": null, "imageUrl": null }, "order": { "id": "ord_aa12bb34", "amount": 50.00, "quantity": 80.0, "price": 0.70, "status": "FILLED", "type": "SELL", "outcome": "YES", "outcomeLabel": "Yes", "currency": "USD", "createdAt": "2026-02-17T10:35:00Z", "updatedAt": "2026-02-17T10:35:01Z" }, "event": { "id": "evt_123", "slug": "us-fed-rate-cut-2026", "title": "Will the Fed cut rates in 2026?", "type": "SINGLE_MARKET", "createdAt": "2026-01-10T09:00:00Z", "imageUrl": "https://cdn.bayse.markets/events/fed.png" }, "market": { "id": "mkt_456", "title": "Yes or No", "imageUrl": null } }, "timestamp": 1700000000000 } ``` `quantity` is floored. `createdAt` and `updatedAt` are ISO 8601 strings. ### Unsubscribe ```json theme={null} { "type": "unsubscribe", "room": "activity:EVENT_ID" } ``` For a market-specific subscription: ```json theme={null} { "type": "unsubscribe", "room": "activity:EVENT_ID:MARKET_ID" } ``` ## Price updates Subscribe to price changes for all markets in a prediction event. ### Subscribe ```json theme={null} { "type": "subscribe", "channel": "prices", "eventId": "EVENT_ID" } ``` Room name: `prices:` ### Events received **`price_update`** — A market price changed. The `data` field contains the full event snapshot. Example (trimmed): ```json theme={null} { "type": "price_update", "data": { "id": "evt_123", "slug": "us-fed-rate-cut-2026", "title": "Will the Fed cut rates in 2026?", "status": "open", "type": "SINGLE_MARKET", "markets": [ { "id": "mkt_456", "question": "Yes or No?", "outcomes": ["Yes", "No"], "engine": "CLOB", "prices": { "YES": 0.65, "NO": 0.35 } } ] }, "timestamp": 1700000000000 } ``` ### Unsubscribe ```json theme={null} { "type": "unsubscribe", "room": "prices:EVENT_ID" } ``` ## Orderbook updates Subscribe to order book snapshots for specific markets. Snapshots reflect the current state of bids and asks. ### Subscribe ```json theme={null} { "type": "subscribe", "channel": "orderbook", "marketIds": ["MARKET_ID"] } ``` To receive orderbook data in a specific currency: ```json theme={null} { "type": "subscribe", "channel": "orderbook", "marketIds": ["MARKET_ID"], "currency": "NGN" } ``` `marketIds` supports up to 10 markets per subscription. The `currency` field accepts `USD` or `NGN`. Room names: `orderbook:` or `orderbook::` ### Events received **`orderbook_update`** — The order book snapshot was updated. ```json theme={null} { "type": "orderbook_update", "data": { "orderbook": { "marketId": "mkt_456", "outcomeId": "out_789", "timestamp": "2026-02-17T10:40:00Z", "bids": [ { "price": 0.60, "quantity": 500, "total": 300.0 }, { "price": 0.55, "quantity": 300, "total": 165.0 } ], "asks": [ { "price": 0.65, "quantity": 200, "total": 130.0 }, { "price": 0.70, "quantity": 400, "total": 280.0 } ], "lastTradedPrice": 0.65, "lastTradedSide": "BUY" } }, "timestamp": 1700000000000 } ``` ### Unsubscribe For USD (default): ```json theme={null} { "type": "unsubscribe", "room": "orderbook:MARKET_ID" } ``` For NGN: ```json theme={null} { "type": "unsubscribe", "room": "orderbook:MARKET_ID:NGN" } ``` ## User trades Subscribe to a specific user's trade activity across all markets. Receives `buy_order` and `sell_order` events whenever the user's orders are filled, regardless of the market engine (CLOB or AMM). To get a user's ID from their tag, use the [Lookup user](/api-reference/user/lookup) endpoint. ### Subscribe ```json theme={null} { "type": "subscribe", "channel": "user_trades", "userId": "USER_ID" } ``` Room name: `user_trades:` ### Events received **`buy_order`** / **`sell_order`** — The user's order was filled. ```json theme={null} { "type": "buy_order", "data": { "payload": { "user": { "id": "68eea9d8-a0fe-4534-ae88-b71e2f4f5c8f", "tag": "mulumba", "imageUrl": "https://cdn.bayse.markets/profile-images/mulumba.png" }, "order": { "id": "7f5e2a1c-3b4d-4e6f-8a9b-1c2d3e4f5a6b", "amount": 100.00, "quantity": 150, "price": 0.65, "status": "FILLED", "type": "BUY", "outcome": "YES", "outcomeLabel": "Yes", "currency": "USD", "engine": "CLOB", "orderType": "MARKET", "createdAt": "2026-02-17T10:30:00Z", "updatedAt": "2026-02-17T10:30:01Z" }, "event": { "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "slug": "arsenal-trophyless-2026", "title": "Will Arsenal Go Trophyless This Season?", "type": "SINGLE_MARKET", "createdAt": "2026-01-10T09:00:00Z", "imageUrl": "https://cdn.bayse.markets/events/arsenal.png" }, "market": { "id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "title": "Yes or No", "imageUrl": null } } }, "timestamp": 1700000000000 } ``` ### Unsubscribe ```json theme={null} { "type": "unsubscribe", "channel": "user_trades", "userId": "USER_ID" } ``` ## Full example ```javascript JavaScript theme={null} const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/markets"); ws.addEventListener("open", () => { // Subscribe to multiple channels ws.send(JSON.stringify({ type: "subscribe", channel: "activity", eventId: "EVENT_ID" })); ws.send(JSON.stringify({ type: "subscribe", channel: "prices", eventId: "EVENT_ID" })); ws.send(JSON.stringify({ type: "subscribe", channel: "orderbook", marketIds: ["MARKET_ID"], currency: "USD" })); }); ws.addEventListener("message", (event) => { for (const line of event.data.split("\n")) { if (!line.trim()) continue; const msg = JSON.parse(line); switch (msg.type) { case "buy_order": case "sell_order": console.log("Trade:", msg.data); break; case "price_update": console.log("Price:", msg.data); break; case "orderbook_update": console.log("Orderbook:", msg.data); break; } } }); ``` ```python Python theme={null} import asyncio import json import websockets async def stream_market_data(event_id, market_id): async with websockets.connect("wss://socket.bayse.markets/ws/v1/markets") as ws: # Subscribe to multiple channels await ws.send(json.dumps({ "type": "subscribe", "channel": "activity", "eventId": event_id, })) await ws.send(json.dumps({ "type": "subscribe", "channel": "prices", "eventId": event_id, })) await ws.send(json.dumps({ "type": "subscribe", "channel": "orderbook", "marketIds": [market_id], "currency": "USD", })) async for message in ws: for line in message.split("\n"): if not line.strip(): continue msg = json.loads(line) if msg["type"] in ("buy_order", "sell_order"): print("Trade:", msg["data"]) elif msg["type"] == "price_update": print("Price:", msg["data"]) elif msg["type"] == "orderbook_update": print("Orderbook:", msg["data"]) asyncio.run(stream_market_data("EVENT_ID", "MARKET_ID")) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "log" "strings" "github.com/gorilla/websocket" ) func main() { conn, _, err := websocket.DefaultDialer.Dial( "wss://socket.bayse.markets/ws/v1/markets", nil, ) if err != nil { log.Fatal(err) } defer conn.Close() eventID := "EVENT_ID" marketID := "MARKET_ID" conn.WriteJSON(map[string]any{ "type": "subscribe", "channel": "activity", "eventId": eventID, }) conn.WriteJSON(map[string]any{ "type": "subscribe", "channel": "prices", "eventId": eventID, }) conn.WriteJSON(map[string]any{ "type": "subscribe", "channel": "orderbook", "marketIds": []string{marketID}, "currency": "USD", }) for { _, data, err := conn.ReadMessage() if err != nil { log.Fatal(err) } for _, line := range strings.Split(string(data), "\n") { if strings.TrimSpace(line) == "" { continue } var msg map[string]any json.Unmarshal([]byte(line), &msg) fmt.Printf("%s: %v\n", msg["type"], msg["data"]) } } } ``` # User orders Source: https://docs.bayse.markets/websocket/user-orders Real-time fill updates for authenticated users ## Overview The `/ws/v1/user` endpoint streams real-time updates for your own orders. Unlike the public market data endpoint, this requires authentication on every message. ``` wss://socket.bayse.markets/ws/v1/user ``` | Subscription | Channel | Server event types | Description | | ------------ | -------- | ------------------ | ------------------------------------------------- | | Orders | `orders` | `order_updated` | Fill updates for your orders on specific markets. | ## Authentication Every message you send to `/ws/v1/user` must include an `auth` field with either your API key or access token. The connection upgrade itself does not require credentials; authentication is verified per message. ```json theme={null} { "auth": { "apiKey": "pk_live_..." } } ``` Or with an access token: ```json theme={null} { "auth": { "accessToken": "eyJhbGciOiJIUzI1NiIs..." } } ``` | Field | Type | Description | | ------------------ | ------ | --------------------------------------------------------------- | | `auth.accessToken` | string | Your JWT access token. Required if `apiKey` is not provided. | | `auth.apiKey` | string | Your API public key. Required if `accessToken` is not provided. | | `auth.deviceId` | string | Optional device identifier. Sent alongside `accessToken`. | Prefer `apiKey` for relay trading clients. If both `accessToken` and `apiKey` are provided, the access token takes precedence. The server caches your last verified credential per connection, so repeated messages with the same value skip the auth service call. If `auth` is missing or invalid, the server returns an error: ```json theme={null} { "type": "error", "data": { "message": "auth required: include {\"auth\":{\"accessToken\":\"...\"}} or {\"auth\":{\"apiKey\":\"...\"}} in every message" }, "timestamp": 1700000000000 } ``` ## Orders Subscribe to your own order events for specific markets. You receive `order_updated` events when your order is partially or fully filled. ### Subscribe ```json theme={null} { "type": "subscribe", "channel": "orders", "marketIds": ["MARKET_ID_1", "MARKET_ID_2"], "auth": { "apiKey": "pk_live_..." } } ``` `marketIds` supports up to 10 markets per subscription message. To track more markets, send multiple subscribe messages. For relay API clients, use the same public API key you use for authenticated HTTP requests. Room names: `orders::` (one room per market) ### Events received **`order_updated`** — Your order was partially or fully filled. ```json theme={null} { "type": "order_updated", "data": { "orderId": "7f5e2a1c-3b4d-4e6f-8a9b-1c2d3e4f5a6b", "eventId": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "marketId": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "order": { "id": "7f5e2a1c-3b4d-4e6f-8a9b-1c2d3e4f5a6b", "userId": "68eea9d8-a0fe-4534-ae88-b71e2f4f5c8f", "marketId": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "outcomeId": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", "outcomeLabel": "Yes", "side": "BUY", "price": 0.65, "quantity": 150.0, "filledQuantity": 100.0, "remainingQuantity": 50.0, "avgFillPrice": 0.64, "status": "PARTIAL_FILLED", "timeInForce": "GTC", "createdAt": 1700000000, "updatedAt": 1700000050 }, "timestamp": 1700000050 }, "timestamp": 1700000050000 } ``` ### Order fields | Field | Type | Present on | Description | | ------------------- | ------ | --------------- | --------------------------------------------------- | | `id` | string | All | Order UUID. | | `userId` | string | All | Your user UUID. | | `marketId` | string | All | Market UUID. | | `outcomeId` | string | All | Outcome UUID. | | `outcomeLabel` | string | All | Outcome label (e.g. "Yes", "No"). | | `side` | string | All | `BUY` or `SELL`. | | `price` | number | All | Limit price (0.01 to 0.99). | | `quantity` | number | All | Original order quantity. | | `filledQuantity` | number | All | Quantity filled so far. | | `remainingQuantity` | number | All | Quantity remaining. | | `avgFillPrice` | number | `order_updated` | Volume-weighted average fill price. | | `status` | string | All | `OPEN`, `PARTIAL_FILLED`, `FILLED`, or `CANCELLED`. | | `timeInForce` | string | When present | `GTC`, `IOC`, or `FOK`. Omitted if empty. | | `createdAt` | number | All | Unix timestamp (seconds). | | `updatedAt` | number | All | Unix timestamp (seconds). | Timestamps inside `data.order` and `data.timestamp` are Unix seconds. The outer `timestamp` field is Unix milliseconds. ### Unsubscribe ```json theme={null} { "type": "unsubscribe", "room": "orders:USER_ID:MARKET_ID" } ``` ## Event series and market rotation For events that belong to a [series](/concepts/event-series) (e.g. hourly BTC markets), each interval produces a new event with new market IDs. When the current event closes and the next one opens, you must unsubscribe from the old market IDs and subscribe to the new market IDs. The server does not automatically migrate your subscription to the next market in the series. A subscription to `orders::` does not follow the series forward. If you keep listening to the previous market after the next event opens, you will stop receiving new order updates for the series until you rotate to the new market IDs. A recommended approach: 1. Use the [Get series events](/api-reference/pm/get-series-events) endpoint to find the currently open event. 2. Subscribe to its market IDs. 3. When you receive the last fill or detect that the event has closed, unsubscribe from the old market IDs. 4. Fetch the newly opened event in the series and subscribe to its market IDs. Forgetting to rotate subscriptions is a common issue. If you stop receiving order events after a series interval elapses, check that you are subscribed to the new market's IDs, not the previous one's. ## Full example ```javascript JavaScript theme={null} const ws = new WebSocket("wss://socket.bayse.markets/ws/v1/user"); const auth = { apiKey: "pk_live_..." }; ws.addEventListener("open", () => { ws.send(JSON.stringify({ type: "subscribe", channel: "orders", marketIds: ["MARKET_ID"], auth, })); }); ws.addEventListener("message", (event) => { for (const line of event.data.split("\n")) { if (!line.trim()) continue; const msg = JSON.parse(line); switch (msg.type) { case "order_updated": console.log("Fill:", msg.data.order.filledQuantity, "at", msg.data.order.avgFillPrice); break; } } }); ``` ```python Python theme={null} import asyncio import json import websockets async def stream_orders(api_key, market_ids): async with websockets.connect("wss://socket.bayse.markets/ws/v1/user") as ws: await ws.send(json.dumps({ "type": "subscribe", "channel": "orders", "marketIds": market_ids, "auth": {"apiKey": api_key}, })) async for message in ws: for line in message.split("\n"): if not line.strip(): continue msg = json.loads(line) if msg["type"] == "order_updated": order = msg["data"]["order"] print(f"Fill: {order['filledQuantity']} at {order['avgFillPrice']}") asyncio.run(stream_orders("pk_live_...", ["MARKET_ID"])) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "log" "strings" "github.com/gorilla/websocket" ) func main() { conn, _, err := websocket.DefaultDialer.Dial( "wss://socket.bayse.markets/ws/v1/user", nil, ) if err != nil { log.Fatal(err) } defer conn.Close() conn.WriteJSON(map[string]any{ "type": "subscribe", "channel": "orders", "marketIds": []string{"MARKET_ID"}, "auth": map[string]string{ "apiKey": "pk_live_...", }, }) for { _, data, err := conn.ReadMessage() if err != nil { log.Fatal(err) } for _, line := range strings.Split(string(data), "\n") { if strings.TrimSpace(line) == "" { continue } var msg map[string]any json.Unmarshal([]byte(line), &msg) fmt.Printf("%s: %v\n", msg["type"], msg["data"]) } } } ```