Base URL
Authentication
The five/trader-stats/* routes (pnl-history, positions, summary, analysis, trades) are fully public and take no credentials at all — sending auth headers to them does nothing, and they never return 401 or 403.
GET /search-traders is the only authenticated endpoint on this page (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. No API-key scope is required. Session-JWT callers additionally pass the invite gate; API-key and admin-secret callers bypass it. It shares the heavy rate-limit group (10 requests/minute) with /trader-stats/* and /top-holders.
The /search-traders example below reads credentials from KAIROS_CLIENT_ID, KAIROS_API_KEY, and KAIROS_API_SECRET in your shell.
Address handling
wallet_address is validated as either an Ethereum address (0x..., 42 chars) or a Solana
base58 address, normalized (EVM addresses lower-cased) before lookup — a value matching
neither format returns 400. When omitted, positions and trades choose the provider with
the most ClickHouse fills for the wallet, then fall back to address format
(0x → Polymarket, Solana → Kalshi). PnL history defaults to Polymarket. Pass
provider for deterministic results, especially for multi-venue EVM wallets.
PnL history
heavy rate-limit group (10 requests/minute per trusted client IP).
Request
Gotcha:
ALL is not all-time. The window map is 1D → last 24h in 1-minute buckets, 1W → last 7 days in 5-minute buckets, 1M and ALL → last 30 days in 1-hour buckets. ALL and 1M return identical data.Response
Gotcha:data_points[].timestampcarries no timezone suffix — it is a ClickHouse-local UTC timestamp such as2026-07-15T00:00:00. Parsers that assume local time will shift your chart. Treat it as UTC explicitly.
Gotcha: all PnL values are decimal strings, not floats — parse with a decimal library to avoid precision loss.Responses are cached publicly for 30 seconds (
stale-while-revalidate=60); the service also
caches the computed history in Redis for 120 seconds. Providers not yet covered by the
ClickHouse FIFO pipeline (Kalshi and other non-EVM venues) return an all-zero payload with
data_points: [].
Unlike positions and trades, this route does not auto-detect the provider from the
wallet’s fills — omitting provider always queries Polymarket.
Positions
heavy rate-limit group
(10 requests/minute per trusted client IP).
Request
Response
Market names/icons are resolved only for the returned page, so wallets holding tens of
thousands of positions can’t blow past the backend’s max query size.
Responses are cached publicly for 15 seconds (
stale-while-revalidate=30); the service also
caches per (provider, include_redeemable, status, limit, offset) in Redis for 30
seconds.
The Kalshi adapter reads the configured authenticated Kalshi account and does
not use wallet_address to select an arbitrary public account. Pass
provider=kalshi only when that account-level view is intended.
Summary
performance figures as
/positions — without the position list, so the profile header and stats panel
can render before the positions page.
Public — no authentication required. Uses the heavy rate-limit group
(10 requests/minute per trusted client IP).
Request
Response
performance carries the same shape as on /positions, and the
two always agree. It is null for a venue whose stats come only from the full
positions build — read them from /positions there; passing provider for such
a venue returns performance: null rather than an error.
Responses are cached publicly for 15 seconds (
stale-while-revalidate=30); the
service also caches per (provider, include_redeemable) in Redis for 30
seconds.
Analysis
heavy rate-limit group (10 requests/minute per trusted client IP).
Request
Venue coverage: only venues whose trades flow through the lot allocator are supported — currently Predict.fun. For any other venue,
supported is false and no figures are returned: windows and daily come back empty, and win_loss and roi_distribution are null. This is not an error — check supported before rendering the panel.Response
Gotcha:
roi_distribution can be null while supported is true — that means the ROI read failed and the rest of the panel was served without it. Treat a null distribution as “unavailable this call”, not “no closed positions”, and retry rather than rendering an empty chart.1D covers today only and a partially elapsed day is still one full bucket. The panel is cached per wallet for up to a minute.
Trade history
heavy rate-limit group
(10 requests/minute per trusted client IP).
Request
Response
This endpoint skips the positions/inventory scan the full profile build does, so it stays
fast even for high-frequency wallets.
Responses are cached publicly for 15 seconds (
stale-while-revalidate=30); the service also
caches per (provider, trade_limit, trade_offset) in Redis for 30 seconds.
Profile search
X-Admin-Secret (no scope). Rate limit: heavy group,
10 requests/minute (keyed by authenticated user id when a JWT session is present, otherwise
by trusted client IP). Live upstream profile fetch — not served from a local cache.
Request
0x address auto-detects as Polymarket — pass provider explicitly for other EVM
venues (Opinion, predict.fun). Solana addresses auto-detect as Kalshi. Non-Polymarket venues
currently return a synthetic, service-generated profile rather than a live upstream fetch.
Response
An address with an unrecognized format returns
200 with profile: null and
an explanatory error. A blank address still returns 400.
Migration from removed profile routes
The former/trader-stats/profile/{wallet_address},
/trader-stats/category-breakdown/{wallet_address}, and
/trader-stats/refresh/{wallet_address} routes are no longer registered.
Replace a profile request with parallel calls to the current positions, trades,
and PnL-history endpoints. There is no public force-refresh operation; use the
cache headers above.
Errors
Errors are returned as{ "detail": "<message>" }, except the rate limiter’s 429, which
uses { "error": "Rate limit exceeded: <limit>" } plus X-RateLimit-* and Retry-After
headers.
The five /trader-stats/* routes take no credentials at all, so they never return 401 or
403. Only GET /search-traders is authenticated.
The Code column holds the exact detail string where this page documents one, otherwise —.

