# Find your most-watched moments with hotspots and heatmaps

Get per-moment engagement for your content as a heatmap or a ranked list of hotspots. Use the Engagement API to find the most-watched and least-watched moments of a video.

The Engagement API<BetaTag /> reports how much of each moment of a piece of content viewers watched, aggregated across every completed view. It returns this data in two forms:

* A **heatmap**: an engagement value for every slice of the timeline, ordered from the start of the content to the end.
* **Hotspots**: a ranked list of the most or least engaging moments, each as a start time, an end time, and a score.

Engagement metrics are a subset of Mux Data's [historical metrics](/docs/api-reference/data). They're calculated from completed views, so they reflect how a whole population of viewers behaved rather than any single session.

<Callout type="info" title="Beta">
  The Engagement API is in beta. Its request and response shapes may change before general availability.
</Callout>

<Callout type="info">
  The Engagement API is part of the Mux Data REST API and uses the same [access token authentication](/docs/core/make-api-requests) and [rate limits](/docs/core/make-api-requests#api-rate-limits) as the rest of the Data API. Before you can pull engagement data, you'll need:

  * A Mux Data integration on a [supported version](#sdk-version-requirements) already collecting views for the content, either through a [Mux Data SDK](/docs/guides/track-your-video-performance) or a [custom integration](/docs/guides/build-a-custom-data-integration).
  * Enough completed views during the [specified timeframe](#scope-with-timeframe) for us to aggregate. Engagement is calculated across a population of views during the chosen time period, so freshly published content or content without views during that period won't have data.
  * An access token with **Mux Data** read permissions.

  To query by video ID, you'll also need to be reporting the [`video_id` metadata field](/docs/guides/make-your-data-actionable-with-metadata) from your player.
</Callout>

## Identify your content three ways

Every endpoint comes in three variants, one per identifier. All three return the same response shape, so pick the one that matches how you key your content.

| Identifier | Path segment | What it covers |
| :-- | :-- | :-- |
| **Asset ID** | `/assets/{ASSET_ID}` | Engagement for a single [Mux Video asset](/docs/api-reference/video/assets). |
| **Video ID** | `/videos/{VIDEO_ID}` | Engagement aggregated across every asset and playback ID that shares the same [`video_id`](/docs/guides/make-your-data-actionable-with-metadata) — your own internal identifier for a piece of content. |
| **Playback ID** | `/playback-ids/{PLAYBACK_ID}` | Engagement for a single [playback ID](/docs/guides/play-your-videos), covering only the views that used that playback ID. |

The base path for all six endpoints is `https://api.mux.com/data/v1/engagement`.

<Callout type="info">
  The playback ID endpoints only cover views for that specific playback ID. If an asset has more than one playback ID, such as a public one and a signed one, a playback ID query won't include the others. To analyze the whole asset across all of its playback IDs, use the asset ID or video ID endpoint instead.
</Callout>

## Get a heatmap

A heatmap is an array of engagement values spread evenly across the content timeline, ordered from the start of the content to the end.

```bash
curl 'https://api.mux.com/data/v1/engagement/assets/{ASSET_ID}/heatmap?timeframe[]=7:days' \
  -u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET}
```

```json
{
  "total_row_count": 1,
  "timeframe": [1610025789, 1610112189],
  "data": {
    "total_views": 1024,
    "value": [0.42, 0.55, 0.61, 0.58, 0.47]
  }
}
```

**API reference:** <ApiRefLink href="/docs/api-reference/data/engagement/get-engagement-heatmap-asset">Asset</ApiRefLink>, <ApiRefLink href="/docs/api-reference/data/engagement/get-engagement-heatmap-video">Video</ApiRefLink>, <ApiRefLink href="/docs/api-reference/data/engagement/get-engagement-heatmap-playback-id">Playback ID</ApiRefLink>.

The engagement values live in the `data.value` array:

* Each number is the engagement value for that slice of the timeline. The higher the value, the more viewers were watching that moment.
* The array runs from the start of the content to the end.
* The number of buckets is dynamic — somewhere between 10 and 1000 — and scales with the content's duration. Read the bucket count from the array length rather than hard-coding it.
* `data.total_views` is the number of views the heatmap was aggregated from.

### Map buckets to timestamps

The buckets are evenly spaced, so once you know the content's duration you can turn any bucket index into a position in the content. Each bucket covers `duration / value.length` seconds:

```js
// durationSeconds comes from your own metadata or the Mux asset
function bucketsToTimeline(value, durationSeconds) {
  const bucketSize = durationSeconds / value.length;
  return value.map((score, i) => ({
    startSeconds: i * bucketSize,
    endSeconds: (i + 1) * bucketSize,
    score,
  }));
}
```

Each engagement value is now paired with the time range it represents.

## Get hotspots

Hotspots are the highest-scoring moments of the content, returned as discrete, ranked time ranges.

```bash
curl 'https://api.mux.com/data/v1/engagement/assets/{ASSET_ID}/hotspots?timeframe[]=7:days' \
  -u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET}
```

```json
{
  "total_row_count": 2,
  "timeframe": [1610025789, 1610112189],
  "data": {
    "total_views": 1024,
    "hotspots": [
      { "start_ms": 30000, "end_ms": 45000, "score": 0.842 },
      { "start_ms": 120000, "end_ms": 138000, "score": 0.713 }
    ]
  }
}
```

**API reference:** <ApiRefLink href="/docs/api-reference/data/engagement/get-engagement-hotspots-asset">Asset</ApiRefLink>, <ApiRefLink href="/docs/api-reference/data/engagement/get-engagement-hotspots-video">Video</ApiRefLink>, <ApiRefLink href="/docs/api-reference/data/engagement/get-engagement-hotspots-playback-id">Playback ID</ApiRefLink>.

Each entry in `data.hotspots` describes one moment:

* `start_ms` — where the moment begins, in milliseconds from the start of the content.
* `end_ms` — where it ends, in milliseconds (exclusive).
* `score` — an engagement score between 0 and 1. Higher is more engaging.

By default, hotspots come back ordered from most to least engaging. `data.total_views` is the number of views they were aggregated from.

<Callout type="info">
  Hotspots are statistical peaks or valleys that must clear certain thresholds to be considered valid. With only a handful of views, the curve is too noisy for anything to clear those score thresholds, so you may get an empty hotspots array as a result. Only content that produces a discernible engagement pattern will return with a result.
</Callout>

### Query parameters

Hotspots take two optional query parameters:

| Parameter | Description |
| :-- | :-- |
| `limit` | The maximum number of hotspots to return. Leave it off to get all of them. |
| `order_direction` | `desc` (the default) returns the most engaging moments first. `asc` returns the least engaging moments — the cold spots — first. |

To return the three least-engaging moments, sort ascending and set a limit:

```bash
curl 'https://api.mux.com/data/v1/engagement/videos/{VIDEO_ID}/hotspots?timeframe[]=30:days&order_direction=asc&limit=3' \
  -u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET}
```

## Scope with `timeframe`

Every endpoint accepts the standard Data [`timeframe[]`](/docs/api-reference/data) query parameter to limit results to a window. Pass it as a relative duration or as a pair of epoch timestamps:

* Relative: `timeframe[]=7:days` or `timeframe[]=24:hours`
* Absolute range: `timeframe[]=1610025789&timeframe[]=1610112189`

If you leave it off, the timeframe defaults to the last 7 days. Engagement data follows the same retention as the rest of your Mux Data, so how far back you can query depends on your plan.

## SDK version requirements

Engagement data is only collected by recent versions of the Mux Data SDKs. For a piece of content to have heatmaps and hotspots, the views must be reported by an integration on at least the version below. See the [changelog announcement](/docs/changelog/data-engagement-heatmap-sdk) for details.

| SDK | Minimum version |
| :-- | :-- |
| Mux Player (web) | v3.11.0 |
| Mux Player Android | v1.5.3 |
| Mux Player Swift | Built on mux-stats-sdk-objc v5.11.0+ |
| mux-embed | v5.15.0 |
| mux-stats-sdk-media3 | v1.11.1 |
| mux-stats-sdk-objc | v5.11.0 |
| mux-stats-sdk-java | v8.9.0 |
| roku-mux | v2.6.0 |
