> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Video analytics


## Overview

[Cloudinary Video Analytics](https://console.cloudinary.com/app/video/analytics) helps you understand and optimize your video content served by Cloudinary. The metrics are automatically collected for all videos delivered through the [Cloudinary Video Player](cloudinary_video_player) and you can also [manually collect](#video_analytics_for_other_players) this data for your own video players using our JavaScript library.

> **NOTE**: You must be using a version of the Cloudinary video player that's later than `1.9.9`.

You can access the analytics by opening the **Video** sub-menu in your [Cloudinary Console](https://console.cloudinary.com/console) and selecting **Analytics**. Here, you can view a set of metrics and graphs that show how Cloudinary delivers your video content, including:
* **Plays**: The total number of video plays during the specified time period. This show the popularity of your video content. 
* **Unique Viewers**: The total number of individual users who have watched a video during the specified time period. This offers insights into the reach of your video content.
* **Watch Time**: The total duration viewers have spent watching all of your videos. This is another indicator of video content reach.
* **Watch Rate**: The percentage of total time videos have been watched in relation to the total duration. This can be used to identify which videos are the most engaging.
* **Play Rate**: The percentage of video plays in relation to the number of times the video player was loaded. This can be used to highlight any issues with discoverability of videos.

Other more specific metrics include:

* **Top Videos**: The most popular videos based on the number of views. This helps identify your best-performing content.
* **Top Devices / OS / Browsers / Countries**: A set of graphs showing how your video content is consumed. This offers insights into the demographics of your users, and can be used to optimize the formats and sizes you use to deliver your videos.

![Video analytics](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/docs/video_analytics_filters.png "thumb: w_500,dpr_2, width:500, popup: true")

The analytics dashboard includes search functionality within filter lists, making it easy to quickly locate and apply specific filters to your data.

You can also export all of the data as a CSV from the UI or [programmatically retrieve video analytics data](#access_video_views_data_programmatically) using our Video Analytics API.

## Video analytics for other players

To collect analytics for video players other than the Cloudinary Video Player, install our [JavaScript library](https://www.npmjs.com/package/cloudinary-video-analytics) and configure it on any pages that include a video player. The library targets the HTML5 `<video>` and `<audio>` tags and works with any other video or audio players that use these tags.

> **NOTE**: The analytics library works with both video and audio elements. All features and methods described in this section apply to both `<video>` and `<audio>` HTML5 elements. 

### Usage

**1. Install the package**:

```shell
npm i cloudinary-video-analytics
```

**2. Import the library**:

```js
import { connectCloudinaryAnalytics } from 'cloudinary-video-analytics';
```

**3. Connect the analytics**:

Connect the analytics by calling the `connectCloudinaryAnalytics` method and provide the video or audio element as a parameter. For example, if your video element has the id `video-player`:

```js
const videoElement = document.getElementById('video-player');
const cloudinaryAnalytics = connectCloudinaryAnalytics(videoElement);
```

Or for an audio player:

```js
const audioElement = document.getElementById('audio-player');
const cloudinaryAnalytics = connectCloudinaryAnalytics(audioElement);
```

**4. Start tracking**:

Start tracking analytics for any Cloudinary video or audio by calling the `startAutoTracking` method:

```js
cloudinaryAnalytics.startAutoTracking();
```

Alternatively, to track each video/audio manually, call the `startManualTracking` method, providing your Cloudinary cloud name and the public ID of the media you want to manually track:

```js
cloudinaryAnalytics.startManualTracking({
  cloudName: 'demo',
  publicId: 'cld-sample',
})
```

**5. Stop tracking (when needed)**:

When you need to stop tracking and end the current analytics session (for example, before swapping the media source dynamically), call the `stopManualTracking` method:

```js
cloudinaryAnalytics.stopManualTracking();
```

This method ends the current tracking session and sends any pending analytics data. Call this before changing the source to ensure:

* The analytics session for the previous media closes properly
* The library sends all analytics events before the new session begins
* You avoid race conditions when re-initializing tracking on the same element

> **NOTES**:
>
> * Auto and manual tracking can't work together for the same video/audio element. Use manual tracking only in cases where you need to track certain media files, you want to track a media element which is dynamic, or you have custom domains which require providing `cloudName` and `publicId`.

> * You may need manual tracking for videos where the library can't automatically identify the public ID or cloud name.

> * When using manual tracking with dynamic source swapping, follow this pattern: call `stopManualTracking()` first, change the source, then call `startManualTracking()` with the new media details. This properly ends the previous session and starts a new one for the new media.

> * The `stopManualTracking` method is only applicable when using manual tracking mode. It has no effect when using auto tracking.

### Dynamic source swapping example

For scenarios where you need to dynamically change the media source (common in video or audio playlist implementations), follow this pattern to ensure proper analytics tracking:

```js
const videoElement = document.getElementById('video-player');
const cloudinaryAnalytics = connectCloudinaryAnalytics(videoElement);

// Start tracking the first video
cloudinaryAnalytics.startManualTracking({
  cloudName: 'demo',
  publicId: 'video-1',
  customData: {
    customData1: 'Playlist-Name',
    customData2: 'Video-1',
  }
});

// Later, when you need to swap to a different video:
function changeVideo(newPublicId, videoName) {
  // Step 1: Stop tracking the current session
  cloudinaryAnalytics.stopManualTracking();
  
  // Step 2: Update the video source
  videoElement.src = `https://res.cloudinary.com/demo/video/upload/${newPublicId}.mp4`;
  
  // Step 3: Wait briefly to ensure the previous session is fully closed
  // This helps avoid race conditions between ending the old session and starting the new one
  setTimeout(() => {

    // Step 4: Start tracking the new video
    cloudinaryAnalytics.startManualTracking({
      cloudName: 'demo',
      publicId: newPublicId,
      customData: {
        customData1: 'Playlist-Name',
        customData2: videoName,
      }
    });
  }, 100);
}

// Example usage: switch to video 2
changeVideo('video-2', 'Video-2');
```

This pattern ensures:

* Each video gets its own complete analytics session
* Analytics data from the previous video sends properly before the new video starts
* You avoid race conditions between stopping and starting tracking
* Custom data fields are correctly associated with each video

## Custom fields

Alongside the fields captured automatically with video analytics, you can also capture up to 5 additional custom fields of your own choosing. This allows you to define, submit, and report on data points specific to your use case. This could be data such as page title, category, SKU, or subscription plan type.

Once you capture these metrics, they appear in your Video Analytics dashboard, allowing you to filter your data based on these metrics.

To capture custom fields, use the `cloudinaryAnalytics` constructor parameter of the [Cloudinary Video Player](video_player_api_reference#ads_and_analytics) or when initiating analytics tracking using the `cloudinary-video-analytics` JavaScript library.

Both methods take a `customData` object with up to five `customData` keys each named `customData` with a number suffix (`customData1`,`customData2`,`customData3`,`customData4`,`customData5`).

Below is an example of a custom data structure with 5 ecommerce related metrics:

```json
customData: {
  customData1: "My-Product-Name",
  customData2: "My-Product-Description",
  customData3: "My-Product-Category",
  customData4: "My-Product-Alternative",
  customData5: "My-Product-SKU-123",
}
```

### Video player example

Here is an example for sending custom fields with the video player:

```js
cloudinary.videoPlayer('player', {
  cloudinaryAnalytics: {
    customData: {
      customData1: "My-Product-Name",
      customData2: "My-Product-Description",
      customData3: "My-Product-Category",
      customData4: "My-Product-Alternative",
      customData5: "My-Product-SKU-123",
    },
  },
});
```

### Video analytics library example

Here is an example for sending custom fields using the [video analytics library](#video_analytics_for_other_players) with auto tracking:

```js
cloudinaryAnalytics.startAutoTracking({
  customData: {
    customData1: "My-Product-Name",
    customData2: "My-Product-Description",
    customData3: "My-Product-Category",
    customData4: "My-Product-Alternative",
    customData5: "My-Product-SKU-123",
  }
});
```

And here is an example with manual tracking:

```js
cloudinaryAnalytics.startManualTracking({
  cloudName: 'demo',
  publicId: 'my-video',
  customData: {
    customData1: "My-Product-Name",
    customData2: "My-Product-Description",
    customData3: "My-Product-Category",
    customData4: "My-Product-Alternative",
    customData5: "My-Product-SKU-123",
  }
});
```

> **NOTE**: When using custom data fields with manual tracking, pass the `customData` object as part of the same configuration object that includes `cloudName` and `publicId`.

## Access video views data programmatically

To retrieve analytics data for your video views programmatically, make use of our Video Analytics API. Get detailed information about all of the views your videos have received, and use expressions to limit the results based on certain conditions such as public ID and when the view ended. You can use the returned data as part of your own analytics systems or dashboards.

### Authentication

The API uses **Basic Authentication**, and your Cloudinary **API Key** and **API Secret** (which you can find on the [API Keys](https://console.cloudinary.com/app/settings/api-keys) page of your Console Settings) to authenticate your requests. Send your credentials as part of the request header, for example:

`Authorization: Basic <your basic auth token>`

Where your basic auth token is a base64-encoded `<api_key>:<api_secret>` string.

### Endpoint

The base URL to access video analytics data is:

`https://api.cloudinary.com/v1_1/<cloud_name>/video/analytics`

### Syntax

`GET views?<expression>&<max_results>&<sort_by>&<next_cursor>`

### Parameters

#### Query Parameters

| Parameter    | Type    | Description
|--------------|---------|---------
| `expression`  | String  | A set of conditions to limit the results to rows that match those conditions. See the [expressions](#expressions) section for more details.
| `max_results` | Integer | Specifies the number of items to include in the response. Default: `5`. Minimum: `1`. Maximum: `500`.
| `next_cursor` | String  | The value to use to obtain the next batch of results.
| `sort_by`     | String  | Specifies the expression field by which to sort the results. Possible fields: `view_ended_at`, `video_duration`,`view_watch_time`. By default, the system sorts results by most recent `view_ended_at` value. Prepend values with a `-` to reverse the order. 

### Examples

1. Retrieve video views data for the video with the public id `skate`:

```endpoint
curl \
  -H "Authorization: Basic <your basic auth token>" \
  https://api.cloudinary.com/v1_1/<cloud_name>/video/analytics/views?expression=video_public_id=skate
```

2. Retrieve video views data for videos with a duration greater than 10 seconds and view ended date less than (older than) the UNIX timestamp `1695039446`: 

```endpoint
curl \
  -H "Authorization: Basic <your basic auth token>" \
  https://api.cloudinary.com/v1_1/<cloud_name>/video/analytics/views?expression=video_duration>10 AND view_ended_at<1695039446
```

> **NOTE**: The Video Analytics API is currently not supported by our SDKs, but you can make a direct call to the API as shown in the examples above.

### Response

The response includes a `data array`, where each entry is an object containing information about a specific video view, along with a request ID and the next cursor value.

Here's an example response:

```json
{
    "request_id": "017e1aa21244400dac90ad48ef43f461",
    "next_cursor": "2a54714067686dc630b44e3b863163dc",
    "data": [
        {
            "video_public_id": "skate",
            "video_duration": 10,
            "viewer_application_name": "Chrome",
            "viewer_location_country_code": "GB",
            "viewer_os_identifier": "Mac OS X 10.15.7",
            "view_watch_time": 23,
            "view_ended_at": "2023-08-14T09:01:38.000Z"
        },
        {
            "video_public_id": "car",
            "video_duration": 15,
            "viewer_application_name": "Chrome",
            "viewer_location_country_code": "US",
            "viewer_os_identifier": "Mac OS X 10.15.7",
            "view_watch_time": 10,
            "view_ended_at": "2023-07-03T11:33:47.000Z"
        },
        {
            "video_public_id": "outdoors",
            "video_duration": 44,
            "viewer_application_name": "Chrome",
            "viewer_location_country_code": "ES",
            "viewer_os_identifier": "Mac OS X 10.15.7",
            "view_watch_time": 2,
            "view_ended_at": "2023-06-15T14:15:28.000Z"
        }
    ]
}
```

### Expressions

Use the expressions parameter to limit the results based on a set of provided conditions. 

#### Fields

The available fields for refining your queries. You can query all except `video_public_id` and `view_ended_at` for null values. 

Field Name | Type | Examples | Details
---|---|---|---
`view_ended_at` | Date | `view_ended_at20`| The duration in seconds of the video.
`viewer_application_name` | String | `viewer_application_name=Chrome`| The application used to view the video.
`viewer_location_country_code` | String | `viewer_location_country_code=GB`| The 2-digit ISO country code of the viewer location.
`viewer_os_identifier` | String | `viewer_os_identifier=Mac OS X 10.15.7`| The full identifier for the viewers operating system.
`view_watch_time` | Numeric | `view_watch_time` - finds results where the value is greater than the given condition

Use the boolean `AND` operator to combine expressions, for example:

`video_public_id=skate AND view_ended_at<1695037024`

## Custom Cloudinary Video Player analytics providers

If you'd like to capture custom analytics to pass to your Google Analytics account or other analytics trackers, you can manually push any events exposed by the Video Player.

You can either set the analytics parameter to `true`, which monitors all available events except `timeplayed` (the library monitors `percentplayed`), or you can specify the specific events you want to monitor, including additional event sub-settings where relevant (`'percentsplayed', percents` and `'timesplayed', times`. See example below).

```js
var vplayer = cloudinary.videoPlayer({ ... , analytics: true })
```
 
Or

```js
var vplayer = cloudinary.videoPlayer({ ... , analytics: { 
  events: ['play', 'pause', 'ended', { type: 'percentsplayed', percents: [10, 40, 70, 90] }, 'error']
} })

```

You then need to send the analytics to your chosen service, for example Google Analytics. See the relevant analytics provider documentation for more information on how to send the chosen metrics.

### Google Analytics 4 (GA4) example

To send video player events to GA4:

**1. Add the GA4 script to your page:**

```html
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXX', { debug_mode: true });
</script>
```

Replace `G-XXXXXXX` with your GA4 Measurement ID.

**2. Initialize the video player with analytics events:**

```js
cloudinary.player('player', {
  cloudName: 'demo',
  publicId: 'my-video', 
  info: { 
    title: 'My Video Title', 
    subtitle: 'Video subtitle', 
    description: 'Video description' 
  } ,
  analytics: {
    events: [
      'play',
      'pause',
      { type: 'percentsplayed', percents: [10, 50, 75, 100] },
      'start',
      'ended'
    ]
  }
});
```

The player automatically sends the configured events to GA4 when they occur during video playback.

See the [Analytics example](https://cloudinary.github.io/cloudinary-video-player/analytics.html) on the Video Player demo page. 
