---
title: "Batch fetch market metadata for multiple contracts"
method: POST
path: "/markets/metadata/batch"
tags: ["Markets"]
---

# Batch fetch market metadata for multiple contracts

`POST /markets/metadata/batch`

## Request

- Base URL: `https://data.kairos.trade`
- URL: `https://data.kairos.trade/markets/metadata/batch`
- Auth: API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret`

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.

## Request body

- MarketsBatchMetadataRequest
  - `contracts` MarketsBatchMetadataRequestItem[], required
    - `ticker` string, required
    - `provider` string, required
  - `titles_only` boolean: When true, skips the API fallback, category inference, and tag-icon enrichment passes — returns whatever was already cached directly.

## Response `200`

Metadata items keyed by the requested ticker. Every requested ticker (whose provider wasn't hidden) appears, with `found: false` for unresolved ones.

- MarketsBatchMetadataResponse: Metadata items keyed by the requested ticker.

## Other responses

- `400`: Missing/empty `contracts` array, or more than 100 contracts requested.
- `401`: No valid credential presented — missing/invalid API-key headers, or an invalid/expired session token.
- `403`: 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.
- `413`: Request body exceeded the 8 MiB service-wide cap, refused before the handler ran.
- `422`: Request failed FastAPI parameter validation.
- `429`: 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.
- `500`: Metadata lookup failed.

---

[API](https://skmtc.dev/kairos/apis/kairos-data-api.md) - [All operations](https://skmtc.dev/kairos/apis/kairos-data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/kairos/apis/kairos-data-api/revisions/cf6b76079e38?raw)
