> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bayse.markets/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch place orders

> 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

<ParamField header="Idempotency-Key" type="string">
  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.
</ParamField>

## Request body

<ParamField body="orders" type="array" required>
  1–20 order items. Each item is processed independently — one bad item does not abort the others.

  <Expandable title="Order item fields">
    <ParamField body="outcomeId" type="string" required>
      UUID of the outcome to trade. Use [Get Event](/api-reference/pm/get-event) to find outcome IDs.
    </ParamField>

    <ParamField body="side" type="string" required>
      `BUY` or `SELL`.
    </ParamField>

    <ParamField body="type" type="string" required>
      `LIMIT` or `MARKET`.
    </ParamField>

    <ParamField body="amount" type="number" required>
      Amount to spend (buy) or receive (sell). Must be greater than 0.
    </ParamField>

    <ParamField body="currency" type="string">
      `USD` (default) or `NGN`.
    </ParamField>

    <ParamField body="price" type="number">
      Limit price per share (0.01–0.99). Required for `LIMIT` orders.
    </ParamField>

    <ParamField body="timeInForce" type="string">
      `GTC`, `GTD`, `FAK`, or `FOK`. Defaults to `GTC` for limit, `FAK` for market.
    </ParamField>

    <ParamField body="postOnly" type="boolean">
      If `true`, the order is rejected instead of crossing the spread. Limit orders only.
    </ParamField>

    <ParamField body="maxSlippage" type="number">
      Maximum acceptable slippage for market orders (0.00–1.00).
    </ParamField>

    <ParamField body="expiresAt" type="string">
      ISO 8601 expiration timestamp. Required for `GTD` orders.
    </ParamField>

    <ParamField body="stpMode" type="string">
      Self-trade prevention mode: `SKIP` (default), `CANCEL_OLDEST`, `CANCEL_NEWEST`, or `CANCEL_BOTH`. See [Place order](/api-reference/pm/place-order) for full semantics.
    </ParamField>

    <ParamField body="clientOrderId" type="string">
      Optional client-supplied identifier. Echoed back in the matching result so you can correlate items with their source ticket without relying on array order.
    </ParamField>
  </Expandable>
</ParamField>

## Example request

<CodeGroup>
  ```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)
  ```
</CodeGroup>

## Response

<ResponseField name="engine" type="string">
  Always `CLOB` for batch endpoints today.
</ResponseField>

<ResponseField name="results" type="array">
  Per-order outcomes, in the same order as the request.

  <Expandable title="Result fields">
    <ResponseField name="index" type="integer">
      Position of this item in the request `orders` array (zero-based).
    </ResponseField>

    <ResponseField name="clientOrderId" type="string">
      Echoed from the request, if provided.
    </ResponseField>

    <ResponseField name="success" type="boolean">
      `true` if the order was accepted upstream, `false` if it failed.
    </ResponseField>

    <ResponseField name="order" type="object">
      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.
    </ResponseField>

    <ResponseField name="error" type="object">
      Present when `success` is `false`.

      <Expandable title="Error fields">
        <ResponseField name="code" type="string">
          Machine-readable code (e.g. `UNSUPPORTED_ENGINE`, `INSUFFICIENT_BALANCE`, `INVALID_OUTCOME`).
        </ResponseField>

        <ResponseField name="message" type="string">
          Human-readable description.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="summary" type="object">
  <Expandable title="Summary fields">
    <ResponseField name="total" type="integer">Total items submitted.</ResponseField>
    <ResponseField name="succeeded" type="integer">Items that placed successfully.</ResponseField>
    <ResponseField name="failed" type="integer">Items that failed.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseExample>
  ```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
  }
  ```
</ResponseExample>

<Note>
  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).
</Note>
