GraphQL Analytics API
Stream provides analytics about both live video and video uploaded to Stream, via the GraphQL API described below, as well as on the Stream Analytics page of the Cloudflare dashboard.
The Stream Analytics API uses the Cloudflare GraphQL Analytics API, which can be used across many Cloudflare products. For more about GraphQL, rate limits, filters, and sorting, refer to the Cloudflare GraphQL Analytics API docs.
Getting started
-
In the Cloudflare dashboard, go to the Account API tokens page.
-
Generate an API token with the Account Analytics permission.
-
Use a GraphQL client of your choice to make your first query. Postman has a built-in GraphQL client which can help you run your first query and introspect the GraphQL schema to understand what is possible.
Refer to the sections below for available metrics, dimensions, fields, and example queries across multiple datasets.
Query Stream datasets under viewer.accounts. Use filter to select records and dimensions to group results.
Grouped datasets expose aggregates like avg, sum, and count. Raw datasets return fields directly, without an aggregate object.
Dotted field paths indicate nested selections: sum.minutesViewed means sum { minutesViewed }.
To find current fields and filter operators, explore the GraphQL schema using introspection. The following is a summary with explanations of useful fields.
Video delivered via HLS/DASH and WebRTC
Stream collects minutes of video delivered for live and on-demand video. These data include minutes delivered via HLS/DASH and WebRTC (WHEP), regardless of your player. This is a billable metric, but GraphQL data are sampled so it may not precisely match billable usage reported on invoices.
Use streamMinutesViewedAdaptiveGroups to query these delivery metrics.
Filters and dimensions
The dataset supports these filters and grouping dimensions:
| Field | Description |
|---|---|
date |
Date |
datetime |
Date and time |
uid |
Video ID (for HLS/DASH) or Input ID (for WebRTC/WHEP) |
clientCountryName |
ISO 3166-1 alpha-2 country code for the viewer |
creator |
Creator ID associated with the video, if set |
Some filters, like date, can be used with operators, such as gt (greater than) and lt (less than), as shown in the example query below. For more advanced filtering options, refer to filtering.
Metrics
The dataset exposes these metrics:
| Field | Description |
|---|---|
count |
Number of values per dimension group |
sum.minutesViewed |
Minutes of video delivered (must be queried as a sum) |
Example
Get minutes viewed by country
query StreamGetMinutesExample($accountTag: string!, $start: Date, $end: Date) {
viewer {
accounts(filter: { accountTag: $accountTag }) {
streamMinutesViewedAdaptiveGroups(
filter: { date_geq: $start, date_lt: $end }
orderBy: [sum_minutesViewed_DESC]
limit: 100
) {
sum {
minutesViewed
}
dimensions {
uid
clientCountryName
}
}
}
}
}
{
"data": {
"viewer": {
"accounts": [
{
"streamMinutesViewedAdaptiveGroups": [
{
"dimensions": {
"clientCountryName": "US",
"uid": "<VIDEO_ID>"
},
"sum": {
"minutesViewed": 2234
}
},
{
"dimensions": {
"clientCountryName": "CN",
"uid": "<VIDEO_ID>"
},
"sum": {
"minutesViewed": 700
}
},
{
"dimensions": {
"clientCountryName": "IN",
"uid": "<VIDEO_ID>"
},
"sum": {
"minutesViewed": 553
}
}
]
}
]
}
},
"errors": null
}
RTMP and SRT input metrics
Use these datasets to inspect RTMP or SRT inputs. Query liveInputEventsAdaptive for events and liveInputEventsAdaptiveGroups for stored segment metrics. These datasets are sampled, so consider using webhooks or Workers event subscriptions to receive events in realtime.
Event fields
liveInputEventsAdaptive returns event log fields directly, not segment metrics. It excludes events with an empty eventDescription.
You can select and filter these fields:
| Field | Description |
|---|---|
datetime |
Event date and time |
inputId |
Live input ID |
coloCode |
Ingest data center code |
eventCode |
Event code |
eventDescription |
Event description |
Filters and dimensions
liveInputEventsAdaptiveGroups contains only stored segment events. It supports these filters and grouping dimensions:
| Field | Description |
|---|---|
date |
Date |
datetime |
Date and time |
eventCode |
Stored segment event code |
inputId |
Live input ID |
Metrics
A GOP (group of pictures) is a sequence of video frames. A "GOP size" is sometimes called a "keyframe interval." These metrics all relate to video segments for each GOP:
| Field | Description |
|---|---|
count |
Number of segments stored |
avg.gopUploadTime, min.gopUploadTime, max.gopUploadTime |
GOP upload time, in milliseconds |
avg.gopDuration, min.gopDuration, max.gopDuration |
GOP duration, in milliseconds |
avg.gopByteSize, min.gopByteSize, max.gopByteSize |
GOP size, in bytes |
To estimate average broadcast bitrate, filter liveInputEventsAdaptiveGroups by inputId and the broadcast datetime range. Select avg.gopByteSize and avg.gopDuration in the same group.
Omit dimensions to get one result for the whole broadcast. For example, use avg.gopByteSize in bytes and avg.gopDuration in milliseconds to calculate bits per second:
bitrate_bps = 8000 * avg.gopByteSize / avg.gopDuration
Both averages use the same sampling weights, so their ratio estimates total bytes divided by total media duration. Only calculate the ratio when avg.gopDuration is greater than zero.
HLS/DASH quality of experience metrics
Common Media Client Data (CMCD) reports client-side playback measurements. Stream built-in players and supported custom players provide CMCD.
Use streamCMCDAdaptiveGroups to query quality of experience (QoE) metrics. These measurements describe player experience, not billable minutes delivered.
Filters and dimensions
The dataset supports these filters and grouping dimensions:
| Field | Description |
|---|---|
videoId |
Video ID |
creatorId |
Creator ID |
inputId |
Live input ID |
sessionId |
CMCD session ID |
contentId |
Content ID |
country |
ISO 3166-1 alpha-3 country code, unlike delivery's alpha-2 code |
resolution |
Video resolution |
bufferStarvation |
1 if the buffer starved between the previous and current requests, otherwise 0 |
bufferStarvationDuration |
Buffer starvation duration, in milliseconds |
initialBufferStarvationDuration |
Initial buffer starvation duration, in milliseconds |
bufferLength |
Buffer length for the requested object |
measuredThroughput |
Measured throughput between client and server |
streamType |
l for live or v for video on demand |
encodedBitrate |
Encoded bitrate of the requested object |
objectDuration |
Requested object duration, in milliseconds |
startup |
1 if the requested object is needed for startup, otherwise 0 |
streamingFormat |
h for HLS or low-latency HLS (LL-HLS), or d for DASH |
hlsType |
hls or ll-hls, otherwise blank |
topBitrate |
Highest bitrate rendition |
playbackRate |
Playback rate: 1 for real time, 2 for twice real time, or 0 when not playing |
date |
Date |
datetime |
Date and time |
Metrics
The dataset exposes these aggregates:
| Field | Description |
|---|---|
count |
Number of values per dimension group |
sum.millisecondsViewed |
Estimated viewing time in milliseconds, from successful requests only |
uniq.viewers |
Unique CMCD session IDs from successful requests only, not unique people |
avg.bufferStarvationDuration |
Average buffer starvation duration, in milliseconds |
avg.initialBufferStarvationDuration |
Average initial buffer starvation duration, in milliseconds |
avg.bufferLength |
Average buffer length |
avg.encodedBitrate |
Average encoded bitrate |
avg.measuredThroughput |
Average measured throughput |
max.latestDatetime |
Last time the combination of dimensions was seen |
Select percentile fields under quantiles. All durations are in milliseconds, and P999 means the 99.9th percentile:
| Field | Description |
|---|---|
bufferStarvationDurationP25 |
25th percentile of buffer starvation duration |
bufferStarvationDurationP50 |
50th percentile of buffer starvation duration |
bufferStarvationDurationP75 |
75th percentile of buffer starvation duration |
bufferStarvationDurationP90 |
90th percentile of buffer starvation duration |
bufferStarvationDurationP95 |
95th percentile of buffer starvation duration |
bufferStarvationDurationP99 |
99th percentile of buffer starvation duration |
bufferStarvationDurationP999 |
99.9th percentile of buffer starvation duration |
initialBufferStarvationDurationP25 |
25th percentile of initial buffer starvation duration |
initialBufferStarvationDurationP50 |
50th percentile of initial buffer starvation duration |
initialBufferStarvationDurationP75 |
75th percentile of initial buffer starvation duration |
initialBufferStarvationDurationP90 |
90th percentile of initial buffer starvation duration |
initialBufferStarvationDurationP95 |
95th percentile of initial buffer starvation duration |
initialBufferStarvationDurationP99 |
99th percentile of initial buffer starvation duration |
initialBufferStarvationDurationP999 |
99.9th percentile of initial buffer starvation duration |
WebRTC WHIP/WHEP metrics
Use streamWHIPBroadcastMetricsAdaptive and streamWHIPBroadcastMetricsAdaptiveGroups to inspect WebRTC-HTTP Ingestion Protocol (WHIP) ingest. For WebRTC-HTTP Egress Protocol (WHEP) playback, query minutes delivered.
Stream collects WHIP observations every 15 seconds. They can take two to three minutes to become queryable.
Raw fields
streamWHIPBroadcastMetricsAdaptive returns these fields directly. You can also use these fields in filters:
| Field | Description |
|---|---|
sampleInterval |
Adaptive sampling interval, not the observation duration |
datetime |
Observation date and time |
inputId |
Live input ID |
peerConnectionState |
Peer connection state |
iceConnectionState |
Interactive Connectivity Establishment (ICE) connection state |
trackIngestState |
Track ingest state |
codecName |
Codec name |
codecMimeType |
Codec media type |
peerConnectionId |
Peer connection ID |
trackId |
Track ID |
mediaKind |
audio or video |
receivedBytes |
Bytes received during the observation |
receivedPackets |
Real-time Transport Protocol (RTP) packets received during the observation |
droppedPackets |
RTP packets dropped from the ingest buffer during the observation, not all network loss |
observationDurationSeconds |
Observed time, in seconds |
Filters and dimensions
streamWHIPBroadcastMetricsAdaptiveGroups supports these filters and grouping dimensions:
| Field | Description |
|---|---|
date |
Date |
datetime |
Date and time |
sampleInterval |
Adaptive sampling interval |
inputId |
Live input ID |
peerConnectionState |
Peer connection state |
iceConnectionState |
ICE connection state |
trackIngestState |
Track ingest state |
codecName |
Codec name |
codecMimeType |
Codec media type |
peerConnectionId |
Peer connection ID |
trackId |
Track ID |
mediaKind |
audio or video |
Metrics
The grouped dataset exposes these metrics. Select count directly, and nested fields under sum or avg:
| Field | Description |
|---|---|
count |
Estimated number of observations |
sum.receivedBytes |
Estimated total bytes received |
sum.receivedPackets |
Estimated total RTP packets received |
sum.droppedPackets |
Estimated total RTP packets dropped from the ingest buffer |
sum.observationDurationSeconds |
Estimated total observed time, in seconds |
avg.sampleInterval |
Average adaptive sampling interval |
avg.ingestBitrate |
Received bits divided by observed seconds, in bits per second |
avg.ingestPacketRate |
Ingest packet rate, in packets per second |
Pagination
GraphQL API supports seek pagination: using filters, you can specify the last video UID so the response only includes data for videos after the last video UID.
The query returns data for two videos after $uId. Set $uId to the last video UID from the previous response:
query StreamPaginationExample(
$accountTag: string!
$start: Date
$end: Date
$uId: string
) {
viewer {
accounts(filter: { accountTag: $accountTag }) {
streamMinutesViewedAdaptiveGroups(
filter: { date_geq: $start, date_lt: $end, uid_gt: $uId }
orderBy: [uid_ASC]
limit: 2
) {
count
sum {
minutesViewed
}
dimensions {
uid
}
}
}
}
}
Here are the steps to implementing pagination:
- Call the first query without uid_gt filter to get the first set of videos
- Grab the last video UID from the response from the first query
- Call next query by specifying uid_gt property and set it to the last video UID. This will return the next set of videos
For more on pagination, refer to the Cloudflare GraphQL Analytics API docs.
Limitations
Query intervals and retention periods have dataset-specific defaults:
| Dataset | Maximum query interval | Data retention |
|---|---|---|
streamMinutesViewedAdaptiveGroups |
32 days (768 hours) | 92 days (2,208 hours) |
liveInputEventsAdaptive |
30 days | 31 days |
liveInputEventsAdaptiveGroups |
30 days | 31 days |
streamCMCDAdaptiveGroups |
30 days | 31 days |
streamWHIPBroadcastMetricsAdaptive |
30 days | 30 days |
streamWHIPBroadcastMetricsAdaptiveGroups |
30 days | 30 days |
Use the settings node under viewer.accounts to check limits for your account. The maxDuration and notOlderThan fields report the query interval and retention period in seconds.
谢谢你的反馈!