---
title: "Aggregate trade volume/pressure metrics for a contract window"
method: GET
path: "/v1/trades/metrics"
tags: ["Trades"]
---

# Aggregate trade volume/pressure metrics for a contract window

`GET /v1/trades/metrics`

## Request

- Base URL: `https://md.kairos.trade`
- URL: `https://md.kairos.trade/v1/trades/metrics`
- Auth: one of:
  - none
  - API key in header `X-Client-Id` and API key in header `X-Api-Key` and API key in header `X-Api-Secret`

Returns aggregated volume and buy-pressure metrics for a (provider, contract_id) over
a trailing window, computed from the same deduplicated trade data as `/v1/trades`.

**Two query shapes depending on provider**, because the platform cannot generically
infer which side of a market is "Yes":
- **Kalshi**: `outcome_0` is the side where `outcome` or `token_id`
  (case-insensitive) equals `"yes"`, `outcome_1` is `"no"`.
- **All other providers**: volumes are grouped by `token_id` and the **top 2 tokens by
  volume** are reported as `outcome_0`/`outcome_1` — a volume ranking, not a
  guaranteed Yes/No mapping.

USD volume is always `size * price / 100` (price is cents 0–100 for every provider).
`outcome_0_volume_share_pct` defaults to 50.0 when both outcome volumes are zero.
`coverage_pct` measures how much of the requested window is actually backed by data
and is 0 when no trades are found in the window.

**Rate-limit cost (LIGHT bucket)**: flat 1 unit per request.

**Caching**: responses are cached for 10s per replica (with singleflight coalescing) to
absorb duplicate concurrent requests. `Cache-Control: private, max-age=5`, no ETag —
this route never returns 304.

## Query parameters

- `provider` 'kalshi' | 'kalshi_offchain' | 'polymarket' | 'dome' | 'opinion' | 'predictfun' | 'hyperliquid', required
- `contract_id` string, required
- `window_seconds` integer

## Response `200`

Aggregated metrics for the window. Present with all-zero volumes (and `coverage_pct: 0`) rather than a 404 when no trades exist.

- TradeMetricsResponse
  - `metrics` TradeMetrics, required
    - `volume_usd` number, required: Total USD notional traded in the window (size * price / 100, summed), rounded to 2 decimals.
    - `outcome_0_volume_usd` number, required: USD volume attributed to "outcome 0". For Kalshi this is the "yes" side; for all other providers it is whichever token had the highest volume in the window (not guaranteed to be "Yes"). Rounded to 2 decimals.
    - `outcome_1_volume_usd` number, required: USD volume attributed to "outcome 1". For Kalshi this is the "no" side; for all other providers it is the second-highest-volume token in the window. Rounded to 2 decimals.
    - `outcome_0_volume_share_pct` number, required: outcome_0_volume_usd as a percentage of (outcome_0 + outcome_1) volume, rounded to 1 decimal. Defaults to 50.0 when both outcome volumes are zero.
    - `trade_count` integer, required: Number of deduplicated trades in the window.
    - `window_seconds` integer, required: Echoes the effective `window_seconds` request parameter.
    - `coverage_pct` number, required: Percentage of the requested window actually covered by data, clamped to [0, 100]. 0 when no trades are found. Rounded to 1 decimal.

## Other responses

- `400`: Validation failure: missing `provider`, unrecognized `provider`, missing `contract_id`, or `window_seconds` outside [3600, 86400].
- `401`: No valid credential presented. Every path returns `code: unauthorized`; the triggers differ: - An `Authorization` header on a deployment where bearer auth is not enabled (`bearer authentication is not enabled`). - A bearer token that fails verification (`invalid or expired session`), or whose token/session has been revoked (`session has been revoked`). - An incomplete or wrong API-key header set. Sending *any* of `X-Client-Id`, `X-Api-Key`, `X-Api-Secret` commits the request to the API-key path, so a partial set is rejected rather than silently degrading to anonymous limits (`valid X-Client-Id, X-Api-Key, and X-Api-Secret headers are required`). - No credential material at all while the anonymous free tier is unavailable — switched off for the deployment, forced off through the runtime kill switch, or not yet resolvable on a replica that has never read the flag (it fails closed).
- `403`: API key presented from a source IP not in that credential's whitelist.
- `429`: Two different gates return this status, both with `code: rate_limited`: - **Per-credential (or per-IP, when anonymous) weighted budget exhausted.** The message is `rate limit exceeded`, or, for anonymous callers, `anonymous rate limit exceeded — request an API key for higher limits (…)`. `Retry-After` is the whole number of seconds until the current one-minute window rolls over, floored at 1. All four `X-RateLimit-*` headers are present. - **Per-IP admission gate.** An in-process, pre-authentication guard that keeps a flood off the shared credential cache and database. The message is `too many requests from this address` and `Retry-After` is a flat `60`. Because this fires *before* authentication, the response carries **no** `X-RateLimit-*` headers at all.
- `500`: The trade-metrics query failed.
- `503`: The rate limiter is unreachable. Fails CLOSED — the request is rejected rather than let through unmetered.

---

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