> ## 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.

# Batch fetch market metadata for multiple contracts

> Batch variant of `/markets/metadata`, capped at 100 contracts. Contracts whose provider is currently inactive are dropped before processing; if that empties the batch the response is `{}`. Looks up the rest via a cached-metadata fast path, with a fallback for cache misses and non-Polymarket providers. Unless `titles_only: true` is set, three further best-effort enrichment passes run (each independently swallows its own errors and never fails the request):
  1. **API fallback** — any contract still `found: false` is retried
     against the live provider API (same fallback `/markets/metadata`
     uses), for markets too new to be indexed.
  2. **Category inference** — markets with no `category` get one
     inferred from Kalshi crypto ticker patterns or crypto keywords
     in the title (bitcoin/ethereum/solana/xrp/"btc "/"eth "/"sol ").
  3. **Tag icon enrichment** — resolves market-to-platform-tag
     mappings and assigns the highest-precedence tag's icon as
     `tag_icon`; can also backfill `category` from the tag's slug
     (crypto/politics/sports/esports/finance/tech/world).
Note: Polymarket order-book liquidity-rewards enrichment (used by the single-market `/markets/metadata`) is intentionally **not** run here — this endpoint feeds list views where the rewards badge doesn't render.




## OpenAPI

````yaml /openapi/data-api.yaml post /markets/metadata/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:
  /markets/metadata/batch:
    post:
      tags:
        - Markets
      summary: Batch fetch market metadata for multiple contracts
      description: >
        Batch variant of `/markets/metadata`, capped at 100 contracts. Contracts
        whose provider is currently inactive are dropped before processing; if
        that empties the batch the response is `{}`. Looks up the rest via a
        cached-metadata fast path, with a fallback for cache misses and
        non-Polymarket providers. Unless `titles_only: true` is set, three
        further best-effort enrichment passes run (each independently swallows
        its own errors and never fails the request):
          1. **API fallback** — any contract still `found: false` is retried
             against the live provider API (same fallback `/markets/metadata`
             uses), for markets too new to be indexed.
          2. **Category inference** — markets with no `category` get one
             inferred from Kalshi crypto ticker patterns or crypto keywords
             in the title (bitcoin/ethereum/solana/xrp/"btc "/"eth "/"sol ").
          3. **Tag icon enrichment** — resolves market-to-platform-tag
             mappings and assigns the highest-precedence tag's icon as
             `tag_icon`; can also backfill `category` from the tag's slug
             (crypto/politics/sports/esports/finance/tech/world).
        Note: Polymarket order-book liquidity-rewards enrichment (used by the
        single-market `/markets/metadata`) is intentionally **not** run here —
        this endpoint feeds list views where the rewards badge doesn't render.
      operationId: getMarketMetadataBatch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MarketsBatchMetadataRequest'
            example:
              contracts:
                - ticker: KXBTC15M-26JUL221600-00
                  provider: kalshi
                - ticker: 0x1234abcd...ef
                  provider: polymarket
              titles_only: false
      responses:
        '200':
          description: >-
            Metadata items keyed by the requested ticker. Every requested ticker
            (whose provider wasn't hidden) appears, with `found: false` for
            unresolved ones.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketsBatchMetadataResponse'
        '400':
          description: >-
            Missing/empty `contracts` array, or more than 100 contracts
            requested.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
        '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: Metadata lookup failed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    example: Batch fetch failed
components:
  schemas:
    MarketsBatchMetadataRequest:
      type: object
      required:
        - contracts
      properties:
        contracts:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/MarketsBatchMetadataRequestItem'
        titles_only:
          type: boolean
          default: false
          description: >-
            When true, skips the API fallback, category inference, and tag-icon
            enrichment passes — returns whatever was already cached directly.
    MarketsBatchMetadataResponse:
      type: object
      description: Metadata items keyed by the requested ticker.
      additionalProperties:
        $ref: '#/components/schemas/MarketsBatchMetadataItem'
    MarketsBatchMetadataRequestItem:
      type: object
      required:
        - ticker
        - provider
      properties:
        ticker:
          type: string
        provider:
          type: string
    MarketsBatchMetadataItem:
      type: object
      required:
        - ticker
        - found
      properties:
        ticker:
          type: string
        found:
          type: boolean
        title:
          type:
            - string
            - 'null'
        event_title:
          type:
            - string
            - 'null'
        provider:
          type:
            - string
            - 'null'
        status:
          type:
            - string
            - 'null'
        image:
          type:
            - string
            - 'null'
        category:
          type:
            - string
            - 'null'
          description: >-
            May be backfilled by ticker-pattern/title-keyword inference or by a
            tag slug when the underlying record has none.
        end_date:
          type:
            - string
            - 'null'
        open_time:
          type:
            - string
            - 'null'
        tag_icon:
          type:
            - string
            - 'null'
          description: Highest-precedence active PlatformTag icon for this market.
        yes_sub_title:
          type:
            - string
            - 'null'
          description: >-
            Custom binary side label (e.g. Hyperliquid team names). Null falls
            back to "Yes" on the frontend.
        no_sub_title:
          type:
            - string
            - 'null'
        outcome_label:
          type:
            - string
            - 'null'
          description: >-
            Short sibling-differentiating label for multi-outcome events
            (Polymarket groupItemTitle / Kalshi yes_sub_title).
        resolved_outcome:
          type:
            - string
            - 'null'
          enum:
            - true
            - 'no'
            - void
            - null
          description: >-
            Winning side for already-resolved binary markets; null if unresolved
            or not surfaced by the provider.
        outcome_pair:
          type:
            - array
            - 'null'
          items:
            type: string
          description: >-
            The two real outcome labels for a non-Yes/No binary market
            (moneyline/spread/O-U), ordered to match outcome index 0/1.
    DataError:
      type: object
      description: Handler error envelope (FastAPI HTTPException).
      required:
        - detail
      properties:
        detail:
          type: string
          description: Human-readable error message.
          example: Invalid provider
  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.