ImageKit video player ships with a built-in analytics module that reports engagement and quality-of-experience metrics for every playback session. Add an analytics object to your IKPlayer options to start collecting data and view results in the ImageKit dashboard under video analytics.
Enterprise plan only
Video analytics is available only on custom enterprise pricing plans. Contact us to enable it for your account.
The feature is currently in beta and actively evolving. Metric definitions and dashboard layouts may change before general availability.
What gets tracked
Analytics is opt-in. Set analytics.enabled to true to start. Once enabled, the player automatically collects data across four areas:
- Engagement: Who watches, how long, and how much of each video they complete
- Performance: Page, player, and video startup times plus seek responsiveness
- Reliability: Rebuffering, startup failures, and mid-stream errors
- Quality: Bitrate delivered vs. what the screen actually needed
All data is available in the dashboard with filters by device, browser, OS, country, video, and more. See Metrics for the full list.
Quick setup
Pass an analytics object on ikOptions with enabled: true to start the tracker. All other fields are optional.
import { videoPlayer } from '@imagekit/video-player';
import '@imagekit/video-player/styles.css';
const player = videoPlayer('my-video', {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true, // required to start the tracker
userId: 'anon-user-abc123', // optional anonymized ID, not email/username
customDimensions: {
cd_1: '2.1.0', // playerVersion
cd_2: 'experiment-b' // experimentBucket
}
}
});
player.src({ src: 'https://ik.imagekit.io/<your-imagekit-id>/video.mp4' });
import { IKVideoPlayer } from '@imagekit/video-player/react';
import '@imagekit/video-player/styles.css';
export default function App() {
const ikOptions = {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
userId: 'anon-user-abc123',
customDimensions: { cd_1: '2.1.0', cd_2: 'experiment-b' }
}
};
return (
<IKVideoPlayer
ikOptions={ikOptions}
source={{ src: 'https://ik.imagekit.io/<your-imagekit-id>/video.mp4' }}
/>
);
}
<template>
<IKVideoPlayer :ikOptions="ikOptions" :source="source" />
</template>
<script setup>
import { IKVideoPlayer } from '@imagekit/video-player/vue';
import '@imagekit/video-player/styles.css';
const ikOptions = {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
userId: 'anon-user-abc123',
customDimensions: { cd_1: '2.1.0', cd_2: 'experiment-b' }
}
};
const source = { src: 'https://ik.imagekit.io/<your-imagekit-id>/video.mp4' };
</script>
---
import { IKVideoPlayer } from '@imagekit/video-player/astro';
import '@imagekit/video-player/styles.css';
const ikOptions = {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
userId: 'anon-user-abc123',
customDimensions: { cd_1: '2.1.0', cd_2: 'experiment-b' }
}
};
const source = { src: 'https://ik.imagekit.io/<your-imagekit-id>/video.mp4' };
---
<IKVideoPlayer ikOptions={ikOptions} source={source} />
Analytics options
| Parameter | Description |
|---|---|
enabled (required)boolean | Set to true to start the analytics tracker. The tracker is otherwise inactive.Default: false |
userIdstring | Opaque, anonymized identifier for the end user. Used to compute Unique Users and to look up sessions per user. Do not pass values that are personally identifiable on their own (such as email or username); use an opaque ID from your system. If omitted, Unique Users counts will not be meaningful. |
customDimensions | Per-session custom dimensions keyed by dimension key ( |
mapError | Callback to remap or enrich an error before it is reported. Receives |
Metrics
All metrics below appear in the dashboard under Video analytics → Metrics, and can be sliced by any of the dimensions listed further down.
Engagement
| Metric | Definition |
|---|---|
| Total Views | Number of times a video successfully started playing in the selected period. Optionally expanded to include all playback attempts, including sessions where the first frame never appeared. |
| Unique Users | Number of distinct userIds with at least one successful view per time bucket. Requires userId to be set for meaningful counts. |
| Playing Time | Total time content was actively playing. Excludes seek time, rebuffering, and time while paused. |
| Playback Completed by Users | Distribution of users by the maximum completion percentage they reached. Replays by the same user do not count separately. |
| Playback Completed by Views | Distribution of playback sessions by completion percentage. Every replay counts as a separate session. |
| Audience Retention | Per-video timeline curve showing where viewers leave, skip, or continue. Requires a Video Source URL filter to scope to a single asset. |
Timing & latency
| Metric | Definition |
|---|---|
| Page Load Time | Time from the initial page request until the player is first initialized on the page. Typically recorded only for the first video on a page. |
| Player Startup Time | Time for the player itself to initialize and become ready for playback. Does not include the network fetch of the first frame. |
| Video Startup Time | Time from playback intent (autoplay or user action) to the first rendered video frame. |
| Seek Latency | Time between a seek action and playback resumption, summarized as percentiles across views. |
Playback health
| Metric | Definition |
|---|---|
| Re-buffer Percentage | Share of total watch time spent rebuffering after playback started. Aggregate ratio across all views in each time bucket. |
| Re-buffer Duration | Per-view seconds spent in rebuffer stalls, summarized as percentiles. |
| Re-buffer Frequency | Number of rebuffer events per minute of total watch time. Aggregate across all views. |
| Re-buffer Count | Per-view number of distinct rebuffer events, summarized as percentiles. |
| Abandoned Before Start Percentage | Share of attempts where the user left before the first frame and no error was recorded: voluntary startup abandonment. |
| Video Startup Failure Percentage | Share of attempts where the first frame never appeared because of a recorded error. Codes you mark as business exceptions are excluded by default (see error tracking). |
| Playback Failed Percentage | Share of views where a fatal error occurred after playback had started. Codes you mark as business exceptions are excluded by default. |
Quality
These metrics are most meaningful when serving Adaptive Bitrate Streaming (ABS) content (HLS or DASH), where the player switches renditions based on network conditions.
| Metric | Definition |
|---|---|
| Upscale Percentage | Maximum upscale percentage during a view. Non-zero values mean the player enlarged the decoded video to fit the display. |
| Downscale Percentage | Maximum downscale percentage during a view. Large values mean the delivered rendition was higher resolution than the display needed. |
| Weighted Average Bitrate | Per-view time-weighted average of the bitrates actually played, summarized as percentiles across views. |
Breakdowns
In addition to the time-series metrics above, the dashboard surfaces top-N breakdowns: Top Devices, Top Operating Systems, Top Browsers, Top Countries, and Top Videos.
Events
The metrics above are derived from a stream of playback events the player emits automatically once analytics is enabled. You don't subscribe to or handle these events yourself; they are sent to ImageKit in the background and listed here for transparency about what the player tracks. You can see the full event timeline for any individual playback on the playback detail page.
Events are grouped into sessions and views:
- A session is a viewer's continuous engagement within one browser, shared across all players, tabs, and page loads on the same site. It can span multiple videos and resets after 60 minutes of inactivity or 24 hours, whichever comes first. A different browser, profile, or incognito window gets its own session.
- A view is a single video playback within a session, identified by a
playbackId. Most metrics are computed per view.
| Event | When it fires |
|---|---|
sessioninit | A new session begins: the first playback on a page, or after a session reset due to inactivity or the 24-hour limit. |
playerready | The player has initialized and is ready for playback. |
viewinit | A view opens for a video source, either on initial load or when the source changes. |
viewstarted | The first video frame has rendered for the view. |
play | Playback intent was registered, from autoplay or a user action. |
playing | Active playback began or resumed (including after a pause, seek, or rebuffer). |
pause | Playback was paused. |
timeupdate | Periodic progress heartbeat carrying the current playhead position, cumulative playing time, and current bitrate. |
seeking / seeked | A seek started and then completed. Together they capture seek latency. |
rebufferstart / rebufferend | A mid-playback stall began and then ended. Together they capture rebuffer duration. |
videochange | The player switched to a new video source within the same session. |
error | A playback error was recorded, or an error was reported manually. Feeds the failure metrics described under error tracking. |
ended | Playback reached the end of the video. This does not close the view; a replay or seek continues on the same view. |
viewend | The view closed. The reason is one of videochange (source switched), error (fatal error), dispose (player was destroyed), navigation (user left the page), or sessionrotate (session idle timeout). |
Dimensions and breakdowns
Every metric can be filtered or grouped by the dimensions below. All dimensions are derived server-side from the request and User-Agent. The only ones you control from the player are User ID (userId) and Custom dimensions (customDimensions).
Viewer
| Dimension | Description & examples |
|---|---|
| User ID | The opaque identifier you pass on analytics.userId. Example: anon-user-abc123 |
Client
| Dimension | Description & examples |
|---|---|
| Browser | Browser name. Examples: Chrome, Safari, Edge, Firefox |
| Browser Version | Browser major version. Examples: 124, 17.4 |
| Operating System | OS name. Examples: iOS, Android, macOS, Windows |
| OS Version | OS version string. Examples: 17.4, 14, 10 |
| Page URL | Full URL of the page hosting the player. Example: https://example.com/videos/launch |
Device
| Dimension | Description & examples |
|---|---|
| Device Category | Device class. Examples: mobile, tablet, desktop, tv, console |
| Device Architecture | CPU architecture. Examples: arm64, x86_64 |
| Display Width | Screen width in CSS pixels. Example: 1920 |
| Display Height | Screen height in CSS pixels. Example: 1080 |
| Display DPR | Device pixel ratio. Examples: 1, 2, 3 |
Geo
| Dimension | Description & examples |
|---|---|
| Country | Country via GeoIP. Examples: India, United States, Germany |
| State | State or region. Examples: Karnataka, California |
| City | City name. Examples: Bengaluru, San Francisco |
Player
| Dimension | Description & examples |
|---|---|
| Player Software | Player name. Example: imagekit-video-player |
| Player Software Version | Player version. Example: 1.4.0 |
| ImageKit Plugin | ImageKit plugin identifier. Example: analytics-js |
| ImageKit Plugin Version | Plugin version. Example: 1.4.0 |
Video metadata
| Dimension | Description & examples |
|---|---|
| Video Source Type | MIME type of the source. Examples: application/x-mpegURL, video/mp4, application/dash+xml. See Adaptive Bitrate Streaming for HLS and DASH delivery. |
| Video Source URL | Full source URL. Example: https://ik.imagekit.io/<id>/launch.m3u8 |
| Video Source Hostname | Hostname of the source URL. Example: ik.imagekit.io |
| Audio Codec | Detected audio codec. Example: mp4a.40.2 |
| Video Codec | Detected video codec. Examples: avc1.640028, hvc1 |
Error
| Dimension | Description & examples |
|---|---|
| Error Code | Player error code (or remapped via mapError). Examples: 2, network-or-geo |
| Exited Before Video Start | Whether the view ended before the first frame. Values: true, false |
Playback
| Dimension | Description & examples |
|---|---|
| Autoplayed | Whether autoplay was active. Values: true, false |
| Preload | Preload attribute. Values: auto, metadata, none |
Custom dimensions (user-defined)
| Dimension | Description & examples |
|---|---|
| cd_1, cd_2, … | Values you pass in analytics.customDimensions. Keyed by the dimension key assigned in the dashboard. See custom dimensions. |
Custom dimensions
Custom dimensions let you slice analytics by metadata that's specific to your application. for example, app version, A/B test bucket, content category, or subscription tier.
ImageKit uses a two-step model:
- Register the dimension in the dashboard under Settings → Video analytics → Custom dimensions. You provide a human-readable label (and optional description); the dashboard assigns a stable dimension key such as
cd_1,cd_2, … - Send values from the player keyed by the dimension key (not the label).
Why dimension keys?
Dimension keys (cd_1, cd_2, …) are immutable identifiers. You can rename or disable a dimension's label at any time without breaking historical data, and the wire payload stays small.
Setting custom dimensions
Pass customDimensions in analytics at player initialization, or update them at any time during a session with setCustomDimensions.
const player = videoPlayer('my-video', {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
customDimensions: {
cd_1: '2.1.0', // playerVersion
cd_2: 'experiment-b', // experimentBucket
cd_3: 'premium' // subscriptionTier
}
}
});
// Replace the entire map at runtime (e.g. after the viewer signs in)
player.imagekitVideoPlayer().setCustomDimensions({
cd_1: '2.1.0',
cd_2: 'experiment-b',
cd_3: 'enterprise'
});
// Inspect the currently effective values
player.imagekitVideoPlayer().getCustomDimensions();
Limits and rules
- The number of active custom dimensions per account depends on your plan. See pricing.
- Values are strings only. Values longer than 256 characters are dropped client-side.
- Disabling a dimension from the dashboard reserves its key permanently; the same key (
cd_N) is never reassigned to a new label. This keeps historical data consistent. - Keys you send that are not registered for your account are ignored.
Use cases
- Release impact: Set a key to your player or app version and compare error rates across releases.
- A/B testing: Bucket viewers into experiments and compare quality of experience between groups.
- Content classification: Tag views with content category, genre, or producer to surface which categories perform best.
- Plan-based analysis: Track which tier (free, premium, enterprise) is experiencing issues.
Error tracking
The player automatically reports playback errors with the following fields:
- Error code: the player error code (e.g. Video.js MediaError code)
- Error message: human-readable description from the player
- Error context: JSON-serialized raw error object for debugging
Errors appear in the dashboard under Video analytics → Errors with their full context and contribute to Video Startup Failure Percentage and Playback Failed Percentage.
Remapping errors with mapError
Use mapError to rewrite the error code, message, or context before it reaches analytics. Common uses:
- Group errors more usefully in dashboards (e.g. extract the specific HLS failure type instead of the generic HTML5
MediaErrorcode). See example below. - Add your own context like a build version or request ID so you can correlate spikes with releases.
- Redact sensitive data from
contextif needed.
mapError receives { code, message, context } and may return any subset of those keys. Returning nothing keeps the original values.
const player = videoPlayer('my-video', {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
mapError: (err) => {
try {
const ctx = JSON.parse(err.context || '{}');
// For HLS/DASH errors, ctx.metadata.errorType is something like
// 'networkrequestfailed' or 'networkrequesttimeout', much more
// actionable than the generic HTML5 MediaError code.
if (ctx.metadata?.errorType) {
return {
code: ctx.metadata.errorType,
// You can also enrich `context` to correlate spikes with releases:
// context: JSON.stringify({ ...ctx, build: window.__BUILD_SHA__ })
};
}
} catch { /* ignore */ }
}
}
});
import { IKVideoPlayer } from '@imagekit/video-player/react';
import '@imagekit/video-player/styles.css';
export default function App() {
const ikOptions = {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
mapError: (err) => {
try {
const ctx = JSON.parse(err.context || '{}');
if (ctx.metadata?.errorType) {
return { code: ctx.metadata.errorType };
}
} catch { /* ignore */ }
}
}
};
return (
<IKVideoPlayer
ikOptions={ikOptions}
source={{ src: 'https://ik.imagekit.io/<your-imagekit-id>/video.m3u8' }}
/>
);
}
<template>
<IKVideoPlayer :ikOptions="ikOptions" :source="source" />
</template>
<script setup>
import { IKVideoPlayer } from '@imagekit/video-player/vue';
import '@imagekit/video-player/styles.css';
const ikOptions = {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: {
enabled: true,
mapError: (err) => {
try {
const ctx = JSON.parse(err.context || '{}');
if (ctx.metadata?.errorType) {
return { code: ctx.metadata.errorType };
}
} catch { /* ignore */ }
}
}
};
const source = { src: 'https://ik.imagekit.io/<your-imagekit-id>/video.m3u8' };
</script>
Astro renders .astro components to HTML on the server, so a JavaScript function passed as a prop can't be carried over to the browser. Assign mapError imperatively from a sibling <script> instead.
---
import { IKVideoPlayer } from '@imagekit/video-player/astro';
import '@imagekit/video-player/styles.css';
const ikOptions = {
imagekitId: 'YOUR_IMAGEKIT_ID',
analytics: { enabled: true, userId: 'anon-user-abc123' }
};
const source = { src: 'https://ik.imagekit.io/<your-imagekit-id>/video.mp4' };
---
<IKVideoPlayer id="player" ikOptions={ikOptions} source={source} />
<script>
const el = document.querySelector('#player');
el.mapError = (err) => {
try {
const ctx = JSON.parse(err.context || '{}');
if (ctx.metadata?.errorType) {
return { code: ctx.metadata.errorType };
}
} catch { /* ignore */ }
};
</script>
Business exceptions
Some errors aren't real playback failures. Certain conditions are expected outcomes you don't want polluting your failure metrics.
Classify these in the dashboard under "Error Configuration" in video analytics settings: add the error code (the one reported by the player, or the one you produce via mapError), give it a label, and mark it as a business exception.
- Business-exception codes are excluded by default from Video Startup Failure Percentage and Playback Failed Percentage.
- Toggle "Show excluded errors" on either metric to display business exceptions as a separate line for visibility.
- The standard HTML5 MediaError codes (
1–4) are seeded automatically and treated as real errors unless you mark them as exceptions.
The player itself does not classify error severity. Every error reaches the dashboard with the same shape, and classification is applied at query time based on your configuration.
Best practices
- Keep
userIdanonymous. Always pass an opaque, anonymized value (e.g. a hashed internal ID). Never use an email, username, or anything personally identifiable on its own. - Use custom dimensions sparingly. High-cardinality values (e.g. unique session tokens) reduce query performance. Prefer low-to-medium cardinality fields like app version, experiment bucket, or content category.
- Set
mapErrorearly so error codes are normalized before they reach the dashboard, keeping your error configuration consistent across releases. - Disable in development. Omit the
analyticsobject or setanalytics.enabledtofalseto avoid polluting production metrics with test traffic. - No cookies. The analytics module sets no cookies and collects no PII by default.
Related features
- Video player overview: Player configuration and source options
- Adaptive Bitrate Streaming: HLS and DASH delivery (enables the Quality metrics above)
- Playlist & recommendations: Sequence videos and show recommendations
- Shoppable videos: Turn videos into shopping experiences
- Subtitles & chapters: Add captions and chapter markers