Watchtime & engagement

Per-content watchtime, completion, and engagement metrics for VOD and live (separate from legacy analytics datasets).

Watchtime & engagement

Per-content watchtime, unique viewers, completion, engagement buckets, and CDN bytes for VOD or live channels.

This is a separate API from the older Content Group / Individual analytics datasets (bytesperperiod, visitorsperperiod, and so on). For those, see Analytics.

When to use this

GoalUse
Plays / impressions / bandwidth time series (legacy datasets)Analytics
Per-video (or per-channel) watchtime, completion rate, engagement % bucketsThis guide → Engagement data

Full parameter and response reference: Engagement data.

Endpoints

GET /v2/analytics/vod/watchtime-engagement
GET /v2/analytics/live/watchtime-engagement

Authenticate with X-Api-Key and send X-Format: default. Host: https://developer.dacast.com.

A successful call returns 200 and { "results": [ ... ] }. No data for the range → 200 with "results": [] (not 404).

Quick example (VOD)

curl -X GET "https://developer.dacast.com/v2/analytics/vod/watchtime-engagement?startdate=2026-10-09&enddate=2026-10-09&content-ids=aaaaaaaa-dc47-4838-b1cb-cd4ea208a670" \
  -H "X-Api-Key: insertYourApiKeyHere" \
  -H "X-Format: default"
{
  "results": [
    {
      "period": "2026-10",
      "period-start": "2026-10-09",
      "period-end": "2026-10-09",
      "content-id": "vod-aaaaaaaa-dc47-4838-b1cb-cd4ea208a670",
      "title": "Episode 1",
      "total-plays": 2,
      "unique-viewers": 2,
      "total-watch-time": 1801.156,
      "total-playtime": 1801.156,
      "avg-watch-time-per-viewer": 900.578,
      "completion-rate": 50,
      "bytes-consumed": 1772696099,
      "engagement-10": 0,
      "engagement-25": 0,
      "engagement-50": 1,
      "engagement-75": 0,
      "engagement-90": 0,
      "engagement-100": 1
    }
  ]
}

Useful query parameters

ParamNotes
startdate / enddateYYYY-MM-DD only. enddate is inclusive. Same-day ranges are valid.
granularitymonth (default) or day. Month grain: max range 366 days.
content-idsOptional comma-separated UUIDs, with or without vod- / live- prefix.
page / perPagePagination (default page 1, perPage 100).
timezoneOptional IANA zone (default UTC) used to interpret the date range.

How to read a row

  • Each row is one content × period (month or day).
  • period is YYYY-MM or YYYY-MM-DD depending on granularity.
  • period-start / period-end are inclusive and clipped to your request (a partial month does not look like a full calendar month).
  • content-id is prefixed (vod-… or live-…).
  • total-watch-time - total seconds the content was actively watched (browser tab in focus).
  • total-playtime - total seconds the content was played (player playing).
  • unique-viewers - distinct viewers for that content × period. A viewer is identified by user_id when present, otherwise by session_id. Events with neither identity are not counted.
  • avg-watch-time-per-viewer = total-watch-time / unique-viewers.
  • completion-rate = engagement-100 / total-plays * 100 (see Engagement buckets below).

Engagement buckets

Fields engagement-10, engagement-25, engagement-50, engagement-75, engagement-90, and engagement-100 count plays by the highest threshold their furthest watched point reached on the VOD (as a percentage of asset duration). A small duration tolerance is applied near the end of the asset.

They are not a cumulative funnel: a play is counted in exactly one bucket. A play that reached 100% is not also added into engagement-10 … engagement-90.

FieldMeaning
engagement-10Plays that peaked at 10% of duration (reached 10%, not yet 25%)
engagement-25Plays that peaked at 25% (reached 25%, not yet 50%)
engagement-50Plays that peaked at 50% (reached 50%, not yet 75%)
engagement-75Plays that peaked at 75% (reached 75%, not yet 90%)
engagement-90Plays that peaked at 90% (reached 90%, not yet 100%)
engagement-100Plays that peaked at 100% (treated as completed)

Between thresholds: a play that stops between two buckets lands in the lower one it already reached - for example 35% → engagement-25, 60% → engagement-50. A play that never reaches 10% is not counted in any engagement bucket.

Completion rate uses only the 100% bucket:

completion-rate = engagement-100 / total-plays * 100

Walkthrough of the example above

The sample row has total-plays: 2 and:

BucketCountReading
engagement-10 … engagement-250No play stopped in the 10–25% buckets
engagement-501One play furthest point reached 50% (not 75%)
engagement-75 … engagement-900No play stopped in the 75–90% buckets
engagement-1001One play furthest point reached 100%

So: 1 of 2 plays completed → completion-rate is 50.
engagement-50 + engagement-100 = 2, which matches total-plays here. Plays with unknown duration (or no usable progress) may not appear in any bucket, so the sum of buckets can be less than total-plays.

Live channels

The live endpoint (GET /v2/analytics/live/watchtime-engagement) returns the same response shape for plays, unique viewers, watch/play time, and bytes. Engagement buckets are VOD-only (live has no fixed asset duration), so for live they are typically all 0 and completion-rate is not meaningful the same way. Prefer total-plays, unique-viewers, watch/play time, and bytes-consumed for live.

Live example

curl -X GET "https://developer.dacast.com/v2/analytics/live/watchtime-engagement?startdate=2026-10-01&enddate=2026-10-09&page=1&perPage=2" \
  -H "X-Api-Key: insertYourApiKeyHere" \
  -H "X-Format: default"

Path is …/live/…; response shape matches VOD. Filter with bare channel UUIDs or live- prefixes via content-ids.

Related