Base URL
Authentication
x-kairos-auth: api-key. Send the API-key triple (X-Client-Id, X-Api-Key,
X-Api-Secret), a first-party session JWT, or an X-Admin-Secret. Sending no
credentials returns 401 Authentication required (JWT token / API key / admin secret).
No API-key scope is required — any active credential can read holders.
Session-JWT callers additionally pass the invite gate (403 Invite required if they
haven’t); API-key and admin-secret callers bypass it.
Rate-limited by the heavy group at 10 requests/minute (keyed by authenticated user id
when a JWT session is present, otherwise by trusted client IP). Live upstream reads share
this budget with /search-traders and /trader-stats/*. An API key with a per-credential
data override is also checked against that absolute ceiling.
Examples below read credentials from KAIROS_CLIENT_ID, KAIROS_API_KEY, and
KAIROS_API_SECRET in your shell.
Top holders
X-Admin-Secret (no scope). Rate limit: heavy group,
10 requests/minute.
This is a live upstream read (e.g. Polymarket’s data-api holders endpoint) — no
local cache sits in front of it.
Request
market accepts up to 50 comma-separated IDs, 128 characters each — an empty value, more
than 50 IDs, or an oversized ID all return 400. Omitting market entirely is a schema
failure, so that returns 422, not 400.
Gotcha: an empty
market and a missing market fail differently. market= is 400; leaving the parameter off entirely is 422.polymarket, non-0x IDs are first resolved to condition IDs via the market metadata
cache and ClickHouse. IDs that don’t resolve are skipped silently; if none resolve the
response is an empty array with 200, not a 404.
Gotcha: unresolvable IDs are dropped without any signal. Compare the
token values you get back against the ids you sent — a short array does not mean a short holder list, it may mean ids were skipped, and an all-unresolvable request is an empty 200.provider is validated against the registered venue list, but only venues with a
top-holders client (polymarket, opinion, predictfun) can actually be served — any
other registered venue returns 400 Invalid top holders request parameters.
Response
The body is a bare array, one entry per requested market/token combination the provider returned — not wrapped in an object.Gotcha:
amount is shares, not dollars. Multiply by the outcome token’s price to get a USD figure.Errors
Errors are returned as{ "detail": "<message>" }, except the local rate limiter’s 429,
which uses { "error": "Rate limit exceeded: <limit>" }.
The Code column holds the exact detail string where this endpoint documents one, otherwise —.

