---
title: "Get Metric Insights"
method: GET
path: "/v1/metrics/{metric}/insights"
---

# Get Metric Insights

`GET /v1/metrics/{metric}/insights`

## Request

- Base URL: `https://api.shortkit.dev` (the document declares no server; this is the origin it was published from)
- URL: `https://api.shortkit.dev/v1/metrics/{metric}/insights`
- Auth: none declared

Dimension values that move a metric (spec §5.1.6): for each low-cardinality
dimension, the values whose metric differs from the org-wide value and that carry a
meaningful share of views, ranked by how much they matter.

Query: timeframe[] / filters[] (as every metrics endpoint), measurement (default per
registry), limit 1–50 (default 10), min_share 0–1 (default 0.02), min_delta 0–10
(default 0.10, relative), dimensions[] (a subset of INSIGHT_DIMENSIONS; default all).

Insights always evaluate WHOLE UTC DAYS. The requested window is snapped outward once
(snap_days: start down to its UTC midnight, end up to the next one) and that single
window is used for the org-wide value and for every per-dimension read, whichever
store answers — so a value's views are always a part of total_views. The response's
`timeframe` is the evaluated (snapped) window; `inputs.requested_timeframe` is what
was asked for. A day-aligned request is evaluated as is.

Metrics. Ratios and averages only. 400 for: the session metrics and unique_viewers
(INSIGHT_EXCLUDED_METRICS), and any additive metric — a count, or a sum under the
effective measurement (insight_is_additive): a value's count or sum is a part of the
org total by construction, so it would always read as "below overall". watch_time
with measurement=avg is fine; watch_time (default sum) is not. A metric that ignores
`measurement` (a ratio such as rebuffer_percentage) accepts any value and is computed
at its registry default; inputs.measurement echoes that effective measurement.
A (metric, dimension) pair in INSIGHT_EXCLUDED_PAIRS is a tautology (completion_rate
by completed) or structurally dominated (watch_time by exit_before_start) and that
dimension is not read: it is absent from inputs.dimensions.

overall = the scalar over the window and filters (_scalar_value), total_views its
view count. Each dimension is one breakdown read (_breakdown_rows — the same store
decision as /breakdown: metrics_dim_daily when the request is unfiltered, the metric
has rollup columns and the org is backfilled, else live), top
INSIGHT_BREAKDOWN_LIMIT values by views. For a row:

    views_share    = row views / total_views
    delta          = row value − overall
    relative_delta = delta / |overall|
    score          = views_share × |relative_delta|

A row is an insight when views_share ≥ min_share and |relative_delta| ≥ min_delta.
The thresholds compare the raw (unrounded) floats: inclusive by intent, but a value
sitting exactly on a threshold may fall either side of it by float error. Skipped:
the NULL dimension value (not an actionable segment — its views still count in
total_views), and a row whose metric value is NULL (e.g. rebuffer_percentage where no
view has watch time). When overall is 0 or NULL a relative delta is undefined, so the
response has no insights (and no breakdown is read).

data is ordered by score DESC, then dimension ASC, then dimension_value ASC, and cut
to `limit`. The order uses the reported 4-dp score, so two values whose scores differ
only below that precision are a tie and sort by name. direction compares the delta's
sign with the registry's higher_is_better: "better" | "worse", null when the metric
has no direction (e.g. seek_count) or the reported (4-dp) relative_delta is 0 — two
stores can disagree in the last bit, and that must not read as a direction. metric_value,
overall_value and delta are rounded like every metric value (_round_value);
relative_delta, views_share and score to 4 dp.

dimension_value is usable verbatim as a filters[] value: a boolean dimension is
always lowercase "true" / "false", whichever store answered.

`inputs` echoes everything the ranking was computed from so a client can explain it,
including overall_path (the store that answered the org-wide value). meta.path
describes the per-dimension reads: "rollup" when every dimension came from
metrics_dim_daily, "live" when none did, "mixed" otherwise; with no breakdown read
it is overall_path.

Freshness: for a window that includes today, the stores are not refreshed at the
same instant — on the unfiltered rollup path today's cells in metrics_hourly (the
overall) and metrics_dim_daily (the rows) are rebuilt at slightly different moments,
and a "mixed" request compares a live dimension against a rollup overall — so shares
can drift by up to one scheduler interval.

## Path parameters

- `metric` string, required

## Query parameters

- `measurement` string, nullable
- `limit` integer
- `min_share` number
- `min_delta` number

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

## Changes

- **2026-10-05** `0ff8f106dc2e`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/shortkit/apis/shortkit-api/changes/v1/metrics/:metric/insights/get.md)

---

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