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

# Fetch candles for up to 200 contracts in one request

> Batched form of `GET /candles`. Accepts up to 200 per-contract requests and returns results in the same order, index-tagged.

**Auth**: same as `GET /candles` — `require_auth_or_admin_or_apikey`, no scope required.

**Cache / data source**: each item is checked against the cache in parallel first; only uncached items are fetched fresh. Items with `rebuild: true` always skip the cache regardless of a hit.

**Fail-fast, partial-failure semantics**: an empty series for an item is returned as `"candles": []` (not an error) — the client renders an empty chart and expects later bars via the live WebSocket feed; there is no server-side ingestion retry. If fetching fails for some items but others were already served from cache, those cached items are still returned; every failed item instead gets `"candles": [], "error": "<message>"` so the client can distinguish a genuine failure from a legitimately empty window. Only if **no** item could be served at all does the whole request fail with 500.

**Validation** (all 400): body must contain a JSON array under `requests` (or the legacy alias `items`); the array must be non-empty and at most 200 items; each item must be an object with `provider`, `contract_id`, `timeframe_seconds`, `start`, and `end` present; the same per-field checks as `GET /candles` apply per item (unknown provider, invalid contract_id, disallowed timeframe, unparseable/out-of-order timestamps, negative or non-integer `outcome`).




## OpenAPI

````yaml /openapi/data-api.yaml post /candles/batch
openapi: 3.1.0
info:
  title: Kairos Data API
  version: 1.0.0
  summary: >-
    The full Kairos data plane — markets, candles, trades, search, discover,
    sports, trader analytics, and PnL.
  description: |
    The Kairos Data API at `data.kairos.trade` is the platform's primary data
    plane: market metadata and prices, OHLCV candles, live venue trade proxies,
    normalized trade history, full-text search, discovery feeds, the sports
    catalog, public trader analytics, and account PnL.

    ## Authentication

    Most endpoints accept a Kairos **API key** (`X-Client-Id` + `X-Api-Key` +
    `X-Api-Secret`, all three) or a first-party session JWT. Endpoints tagged
    `public` (trader analytics, provider configs, several sports feeds) need
    no credentials at all. Some endpoints additionally require an API-key
    **scope** (`trade:read`, `position:read`) — stated per operation.

    For high-volume market-data consumption (candles/trades/metadata at
    scale), prefer the dedicated **Market Data API** at `md.kairos.trade` —
    it has higher budgets, ETag caching, and a binary candle format.

    ## Rate limits

    Rate limits use a sliding window keyed on the authenticated user (JWT
    `sub`) or, for anonymous callers, the trusted client IP. Routes belong to
    a named **bucket** (`x-kairos-bucket`) whose per-minute budget is shared
    by every route in it; routes with no bucket run under the service default
    of 100/minute. `x-kairos-rate-limit` states the bucket's compile-time
    default — operators can raise or lower a bucket at runtime, so treat the
    documented number as the baseline, not a contract. 429 responses carry
    `Retry-After` and `X-RateLimit-*`.

    ## Conventions

    - Prices are on the **0–100 cents scale** unless a field says otherwise
      (trader-analytics position/trade prices use the 0–1 scale; each field's
      description states its scale).
    - Errors: `{"detail": "<message>"}` for handler errors; FastAPI's
      standard validation envelope for 422s. Errors surfaced from a venue
      adapter instead use `{"error", "provider", "operation", "message"}` —
      see `DataProviderError`. Unhandled failures always return the generic
      500 body; internal details are logged, never returned.
    - Every request body is capped at 8 MiB service-wide (413).
  contact:
    name: Kairos
    url: https://app.kairos.trade/docs/api-reference
  termsOfService: https://kairos.trade/terms
servers:
  - url: https://data.kairos.trade
    description: Production
  - url: https://staging-data.kairos.trade
    description: Staging
security:
  - apiKeyClientId: []
    apiKeyKey: []
    apiKeySecret: []
paths:
  /candles/batch:
    post:
      tags:
        - Candles
      summary: Fetch candles for up to 200 contracts in one request
      description: >
        Batched form of `GET /candles`. Accepts up to 200 per-contract requests
        and returns results in the same order, index-tagged.


        **Auth**: same as `GET /candles` — `require_auth_or_admin_or_apikey`, no
        scope required.


        **Cache / data source**: each item is checked against the cache in
        parallel first; only uncached items are fetched fresh. Items with
        `rebuild: true` always skip the cache regardless of a hit.


        **Fail-fast, partial-failure semantics**: an empty series for an item is
        returned as `"candles": []` (not an error) — the client renders an empty
        chart and expects later bars via the live WebSocket feed; there is no
        server-side ingestion retry. If fetching fails for some items but others
        were already served from cache, those cached items are still returned;
        every failed item instead gets `"candles": [], "error": "<message>"` so
        the client can distinguish a genuine failure from a legitimately empty
        window. Only if **no** item could be served at all does the whole
        request fail with 500.


        **Validation** (all 400): body must contain a JSON array under
        `requests` (or the legacy alias `items`); the array must be non-empty
        and at most 200 items; each item must be an object with `provider`,
        `contract_id`, `timeframe_seconds`, `start`, and `end` present; the same
        per-field checks as `GET /candles` apply per item (unknown provider,
        invalid contract_id, disallowed timeframe, unparseable/out-of-order
        timestamps, negative or non-integer `outcome`).
      operationId: postCandlesBatch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CandlesBatchRequest'
            example:
              requests:
                - provider: kalshi
                  contract_id: KXPRESPOLAND-24-DT
                  timeframe_seconds: 60
                  start: '2026-07-21T00:00:00Z'
                  end: '2026-07-22T00:00:00Z'
                  outcome: 0
                  rebuild: false
                - provider: polymarket
                  contract_id: 0xabc123...token
                  timeframe_seconds: 3600
                  start: '2026-07-15T00:00:00Z'
                  end: '2026-07-22T00:00:00Z'
      responses:
        '200':
          description: >
            Index-ordered results, one per request item. A failed item carries
            an `error` string and an empty `candles` array instead of a 5xx for
            the whole batch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CandlesBatchResponse'
        '400':
          description: >
            Malformed batch — `requests` missing/not an array/empty/over 200
            items, or a specific item is malformed (missing required field,
            unknown provider, invalid contract_id, disallowed timeframe_seconds,
            unparseable/out-of-order start/end, non-integer or negative
            outcome).
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    example: Max 200 requests
        '401':
          $ref: '#/components/responses/DataUnauthorized'
        '403':
          $ref: '#/components/responses/DataForbidden'
        '413':
          $ref: '#/components/responses/DataPayloadTooLarge'
        '422':
          $ref: '#/components/responses/DataValidationError'
        '429':
          $ref: '#/components/responses/DataRateLimited'
        '500':
          description: >-
            Every request in the batch failed; nothing could be served from
            cache.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataError'
components:
  schemas:
    CandlesBatchRequest:
      type: object
      required:
        - requests
      description: >
        `requests` is the primary field name; the router also accepts a legacy
        `items` key with the identical array shape as a fallback if `requests`
        is absent.
      properties:
        requests:
          type: array
          minItems: 1
          maxItems: 200
          items:
            $ref: '#/components/schemas/CandlesBatchRequestItem'
    CandlesBatchResponse:
      type: object
      required:
        - results
      properties:
        results:
          type: array
          description: Index-ordered, one entry per request item.
          items:
            $ref: '#/components/schemas/CandlesBatchResultItem'
    DataError:
      type: object
      description: Handler error envelope (FastAPI HTTPException).
      required:
        - detail
      properties:
        detail:
          type: string
          description: Human-readable error message.
          example: Invalid provider
    CandlesBatchRequestItem:
      type: object
      required:
        - provider
        - contract_id
        - timeframe_seconds
        - start
        - end
      properties:
        provider:
          type: string
          example: kalshi
        contract_id:
          type: string
          maxLength: 128
          example: KXPRESPOLAND-24-DT
        timeframe_seconds:
          type: integer
          enum:
            - 1
            - 60
            - 300
            - 900
            - 3600
            - 14400
            - 86400
          example: 60
        start:
          type: string
          format: date-time
          example: '2026-07-21T00:00:00Z'
        end:
          type: string
          format: date-time
          example: '2026-07-22T00:00:00Z'
        outcome:
          type: integer
          minimum: 0
          default: 0
          description: >-
            Zero-based outcome index. Omitted/null defaults to 0; negative or
            non-integer values return 400.
        rebuild:
          type: boolean
          default: false
          description: Bypass the cache for this item and force a fresh fetch.
    CandlesBatchResultItem:
      type: object
      required:
        - index
        - candles
      properties:
        index:
          type: integer
          description: >-
            Position of this result, matching the index of the corresponding
            item in the request's `requests` array.
          example: 0
        candles:
          type: array
          items:
            $ref: '#/components/schemas/CandlesCandle'
        error:
          type: string
          description: >-
            Present only when this specific item failed while other items in the
            batch succeeded from cache. `candles` is `[]` in that case.
          example: Candle batch fetch failed
    CandlesCandle:
      type: object
      description: >-
        One OHLCV bar. `token_id` is only present (never emitted as null) when
        the underlying series is keyed by an outcome token rather than a plain
        contract id.
      required:
        - contract_id
        - timeframe_seconds
        - bucket_start
        - open
        - high
        - low
        - close
        - volume
      properties:
        contract_id:
          type: string
          description: Contract/market identifier this bar belongs to.
          example: KXPRESPOLAND-24-DT
        timeframe_seconds:
          type: integer
          description: Bucket width in seconds.
          example: 60
        bucket_start:
          type: string
          format: date-time
          description: Bucket start time, ISO 8601 UTC.
          example: '2026-07-21T14:32:00+00:00'
        open:
          type: number
          description: Opening price, 0-100 cents scale.
          example: 63.5
        high:
          type: number
          description: High price in the bucket, 0-100 cents scale.
          example: 64
        low:
          type: number
          description: Low price in the bucket, 0-100 cents scale.
          example: 63
        close:
          type: number
          description: Closing price, 0-100 cents scale.
          example: 63.8
        volume:
          type: integer
          description: Traded size (contracts/shares) within the bucket.
          example: 1250
        token_id:
          type: string
          description: >-
            Outcome token identifier, present only when this series is
            token-scoped (e.g. multi-outcome Polymarket markets).
          example: >-
            71321045679252212594626385532706912750332728571942532289631379312455583992563
  responses:
    DataUnauthorized:
      description: >
        No valid credential presented — missing/invalid API-key headers, or an
        invalid/expired session token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataError'
          example:
            detail: Not authenticated
    DataForbidden:
      description: >
        Authenticated but not permitted. Three distinct causes: the session user
        is not invited

        (`Invite required`), the API key lacks the operation's scope, or API-key
        access to the

        requested venue is switched off (`API access is disabled for
        <provider>`). Admin and API-key

        callers bypass the invite check; session and admin callers bypass scope
        and venue checks.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataError'
          example:
            detail: Invite required
    DataPayloadTooLarge:
      description: >
        Request body exceeded the 8 MiB service-wide cap, refused before the
        handler ran.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataError'
          example:
            detail: Request body too large
    DataValidationError:
      description: Request failed FastAPI parameter validation.
      content:
        application/json:
          schema:
            type: object
            required:
              - detail
            properties:
              detail:
                type: array
                description: >-
                  One entry per failed field, with location, message, and error
                  type.
                items:
                  type: object
                  additionalProperties: true
    DataRateLimited:
      description: >
        Rate limit exceeded for this route's sliding window, keyed on the
        session `sub` when

        authenticated and on the trusted client IP otherwise. Honor
        `Retry-After`. A per-API-key

        data ceiling (set per credential) rejects with `{"detail": "API key data
        rate limit

        exceeded"}` instead of the `error` envelope below.
      headers:
        Retry-After:
          schema:
            type: integer
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: 'Rate limit exceeded: 100 per 1 minute'
  securitySchemes:
    apiKeyClientId:
      type: apiKey
      in: header
      name: X-Client-Id
      description: >-
        Credential client id (`kairos_ck_...`). Must be sent together with
        X-Api-Key and X-Api-Secret.
    apiKeyKey:
      type: apiKey
      in: header
      name: X-Api-Key
      description: 64-char hex API key.
    apiKeySecret:
      type: apiKey
      in: header
      name: X-Api-Secret
      description: 64-char hex API secret.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.