June 4, 2020
https://www.analyticsmania.com/post/google-tag-manager-video-tracking/
Google Tag Manager Video Tracking Guide: Beyond Youtube
Updated: June 4th, 2020.
To understand what website visitors are doing on your site, tracking pageviews is far from enough. You should also consider other interactions, such as clicks, views of particular elements, video interactions, etc.
When it comes to videos, the most popular embedded video players are Youtube (I have no stats to support this; just saying this based on what I’ve seen on other websites). But what if your website is using a different player (like Vimeo or maybe a generic HTML5 player)?
That’s not a problem, because you can track them with GTM.
In today’s blog post, I’ll show you how to track videos with Google Tag Manager. I’ll cover Youtube video tracking, Vimeo video tracking, and will also mention several other players.
Table of contents
+ Show table of contents +
So, what’s the plan?
First, we’ll take a look at the most common video tracking example in Google Tag Manager: Youtube video trigger. Then, I’ll show you an example with a video player that is not supported by GTM by default. That’s Vimeo.
And finally, I’ll also give you some ready-made solutions (recipes) designed for various embedded video players.
Ready? Let’s go.
Chapter 1. How to track Youtube videos with Google Tag Manager
If you are in a hurry, I have created a ready-made Youtube video tracking recipe. Download it, import, configure (according to instructions) and it will automatically start tracking Youtube videos on your website in no time.
But if you want to understand the ins and outs of the Youtube video tracking, then continue reading.
Here are the steps you need to complete in order to track Youtube videos with Google Tag Manager.
Step 1. Create a Youtube Video Trigger
It all starts with a trigger. In Google Tag Manager, go to Triggers > New > Trigger Configuration and choose Youtube Video. Then enter the following settings:
You can change the Percentage threshold to anything you like, just separate them with a comma. Also, you can enable Pause, Seeking, and Buffering tracking. Personally, I prefer tracking Start, Complete, and Progress (e.g. 25% of the video was watched).
Also, even though the Add JavaScript API support to all Youtube videos is an Advanced setting, I enable it by default because it increases the change that Youtube video tracking will work.
Step 2. Enable built-in Youtube video variables
In Google Tag Manager, go to Variables > Configure and enable all video-related variables.
Step 3. Create a Lookup Table variable
Here’s what we want to achieve:
- If a visitor starts or completes a video, we want to send the word “start” or “complete” to Google Analytics
- But if the visitor has watched a certain amount of a video (e.g. 25%) we want to send not only the word “progress”. We also will send the percentage of a watched video, e.g. progress 25%
To do that, we will create a lookup variable table in GTM that will return a particular output based on the Youtube video interaction.
In GTM, go to Variables > New > Lookup table and enter the following settings:
- In the Input Variable field enter the {{Video Status}} variable
- Then in the Input field enter “progress” (without quotation marks, all lowercase) and then in the Output field enter {{Video Status}} {{Video Percent}}%
- Finally, enable “Set Default Value” checkbox and insert {{Video Status}} variable once again.
What did we do here?
If the video player status is “progress”, this means that a certain percentage of a video was seen by a visitor and this event became available in the preview and debug mode.
In this case, we want this lookup variable to return the video player status (progress) and also the video percentage that was watched together with a % sign.
But if the Video status is not “progress”, then this lookup variable will just return the name of the status (for example, start, complete, etc.). That’s why I have configured the “Set Default Value” checkbox.
Step 4. Create a Google Analytics tag
Now, it’s time to send the Youtube video data to Google Analytics. In GTM, go to Tags > New > Universal Analytics and enter the following settings.
If you are new to Google Tag Manager and have no idea what a Google Analytics Settings Variable is, click here.
You can choose some other names/values in the Event Category/Action/Label fields if you want (but I highly recommend following this naming convention).
Also, keep an eye on the Event Action field. We have entered the Lookup Table variable here.
Also, assign the Custom Event trigger (for video event) to that tag.
Step 5. Test the setup
Once you have completed all previous steps, it’s time to test. Enable/Refresh the Preview and Debug mode, refresh the page on your website with the embedded Youtube video player and try to interact with it.
First of all, you should start seeing Youtube Video events in the preview mode. If you don’t see them, read this troubleshooting guide.
Click one of them and check whether your Google Analytics tag has fired.
If yes, then go to Google Analytics Real-time reports > Events and check whether you see them too. If you cannot see the events, read this troubleshooting guide.
After a while, your events data will appear in Standard Google Analytics reports as well. But that might take up to 24 hours.
Chapter 2. How to track Vimeo video player with Google Tag Manager (custom solution)
What if your website does not contain embedded Youtube videos? What if you are using something else? In that case, the built-in Youtube video tracking in Google Tag Manager will not work. You need to find some custom solution.
It all starts with identifying the video player
Before you start with the actual tracking of video player interactions, first you need to identify what kind of video player is that. Video players clearly show their logo in the player box (e.g. Vimeo), others will probably be not that obvious.
If your case is the latter, do this. In your browser, open developer tools (in Chrome, that’s F12 (on Windows)) and then go to Elements tab. Then click this button…
… and then click on the player itself. Now try to browse a bit and locate some clues mentioning the player provider name. For example, in this case, the player that I’m inspecting is JW player.
If you can’t find the name, most likely it is an HTML5 video player.
Once you identify the name of the video player, just google [ video player name ] tracking with Google Tag Manager and you should find some guide or tutorial.
Let’s imagine, that in our case, we see the “Vimeo” logo at the corner of the embedded video player.
Step 1. Create a variable “Is Vimeo player present on a page”
In order to track Vimeo players with GTM on a page, we will need to add a lengthy code to the container. Loading that code on every page is not optimal and will affect the page loading speed, that’s why we need to activate that code ONLY when the Vimeo player is actually embedded on that site.
To do that, first, we need to create a Custom JavaScript variable and paste the following code:
function () { for (var e = document.getElementsByTagName("iframe"), x=0; x < e.length; x++) { if (/^https?:\/\/player.vimeo.com/.test(e[x].src)) { return true; } } return false; }
If the Vimeo player is embedded in the page, this variable will return true.
Then create a pageview trigger and use that Custom JavaScript variable in it. If the Vimeo player is present, this trigger will be activated. If there is no Vimeo player, that trigger will remain silent.
Step 2. Vimeo Auto-Event Listener
Now, it’s Vimeo Listener’s turn. A listener is a function (or a bunch of functions) that are built to keep looking for certain interactions on a page. In this case, the listener will be looking for Vimeo player interactions. If it spots one, it will make that data visible in the Preview and Debug mode.
Create a Custom HTML tag and paste the following code. The original authors of this code are Bill Tripple and Bogdan Bistriceanu from Cardinal Path).
This listener was recently slightly updated to support some changes in Vimeo.
<!-- Google Analytics Tag Manager (V2) custom HTML tag for Vimeo video tracking Copyright 2016, Cardinal Path, Inc. Original author: Bill Tripple <btripple@cardinalpath.com> Revised by: Bogdan Bistriceanu <bbistriceanu@cardinalpath.com> Updated by: Julius Fedorovicius <julius@analyticsmania.com> and Richard Outram <Richard.Outram@simmbiotic.com> Version 2.1 --> <script> var dataLayer = (typeof(dataLayer) !== "undefined" && dataLayer instanceof Array) ? dataLayer : []; var videoLabels=[]; var lastP=[]; //we declare variables that will hold information about the video being played var _playerTitle = {}, _playerAuthor = {}, _playerAuthorURL = {}, _playerUploadDate = {}; try{ init(); } catch(err){ dataLayer.push({ 'event': 'gtm.error', 'errorMessage': e.message, 'tag': 'CP - UA - Vimeo Video Listener' }) } function init(){ try{ var player=document.getElementsByTagName("iframe"); for (i = 0; i < player.length; ++i) { var url=player[i].getAttribute("src"); if(/player\.vimeo\.com\/video/.test(url)){ // vimeo iframe found if(!player[i].hasAttribute("id")){ // id attribute missing player[i].setAttribute("id","vimeo_id_"+i); // add id attribute } var urlUpdated=false; if(!/api=/.test(url)){ // check to see if api parameter is in src attribute url=updateUrl(url,"api",1); urlUpdated=true; } if(!/player_id=/.test(url)){ // check if player_id is in src attribute url=updateUrl(url,"player_id",player[i].getAttribute("id")); urlUpdated=true; } if(urlUpdated){ // repopulate src attribute with added parameters player[i].setAttribute("src",url) } videoLabels[player[i].getAttribute("id")]=player[i].getAttribute("src"); // id to label dictionary } } // Listen for messages from the player if (window.addEventListener){ window.addEventListener('message', onMessageReceived, false); } else { window.attachEvent('onmessage', onMessageReceived, false); } } catch(err){ } } function updateUrl(url,param,value){ try{ return url+((/\?/.test(url)) ? "&" : "?")+param+"="+value; } catch(err){ } } // Handle messages received from the player function onMessageReceived(e) { try{ var data = e.data; if(typeof data === "string"){ data = JSON.parse(data); } switch (data.event) { case 'ready': onReady(data); break; case 'play': onPlay(data); break; case 'pause': onPause(data); break; case 'timeupdate': onPlayProgress(data); break; } } catch(err){ } } // Helper function for sending a message to the player function post(action, value) { try{ var data = { method: action }; if (value) { data.value = value; } var message = JSON.stringify(data); var player = document.getElementsByTagName("iframe"); var url; var prot; for (i = 0; i < player.length; ++i) { url=player[i].getAttribute("src"); if(/player\.vimeo\.com\/video/.test(url)){ // Check if protocol exists prot = player[i].getAttribute('src').split('?')[0].split('//')[0]; // If protocol doesn't exist, then need to append to "url" if (!prot){ url="https:" + player[i].getAttribute("src").split('?')[0]; } player[i].contentWindow.postMessage(data, url); } } } catch(err){ } } function getLabel(id){ try{ return videoLabels[id].split('?')[0].split('/').pop(); } catch(err){ } } //our function that will use the Vimeo oEmbed API to retrieve additional information about the video function getVimeoInfo(url, callback) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.getElementsByTagName('body')[0].appendChild(script); } //the callback function which takes the data received from the Vimeo oEmbed API and places it into the corresponding objectes function vimeoCallback(e){ //console.log(e); _playerTitle[e['video_id']] = e['title']; _playerAuthor[e['video_id']] = e['author_name'] _playerAuthorURL[e['video_id']] = e['author_url'] _playerUploadDate[e['video_id']] = e['upload_date'] } function onReady(data) { try{ //execute our function which queries the Vimeo oEmbed API once the embedded videos are "ready" getVimeoInfo("https://www.vimeo.com/api/oembed.json?url=https://vimeo.com/"+getLabel(data.player_id)+"&callback=vimeoCallback", vimeoCallback); post('addEventListener', 'play'); post('addEventListener', 'pause'); post('addEventListener', 'finish'); post('addEventListener', 'playProgress'); } catch(err){ } } function onPlay(data){ try{ dataLayer.push({ event: "video", eventCategory: "vimeo", eventAction: "vimeo play", eventLabel: _playerTitle[getLabel(data.player_id)].toLowerCase() + " - " + getLabel(data.player_id), vimeo_playerID: getLabel(data.player_id), vimeo_playerTitle: _playerTitle[getLabel(data.player_id)].toLowerCase(), vimeo_playerAuthor: _playerAuthor[getLabel(data.player_id)].toLowerCase(), vimeo_playerAuthorURL: _playerAuthorURL[getLabel(data.player_id)].toLowerCase(), vimeo_playerUploadDate: _playerUploadDate[getLabel(data.player_id)], nonInteractive: true }); } catch(err){ } } function onPause(data){ try{ dataLayer.push({ event: "video", eventCategory: "vimeo", eventAction: "vimeo video pause", eventLabel: _playerTitle[getLabel(data.player_id)].toLowerCase() + " - " + getLabel(data.player_id), vimeo_playerID: getLabel(data.player_id), vimeo_playerTitle: _playerTitle[getLabel(data.player_id)].toLowerCase(), vimeo_playerAuthor: _playerAuthor[getLabel(data.player_id)].toLowerCase(), vimeo_playerAuthorURL: _playerAuthorURL[getLabel(data.player_id)].toLowerCase(), vimeo_playerUploadDate: _playerUploadDate[getLabel(data.player_id)], nonInteractive: true }); } catch(err){ } } // Track progress: 25%, 50%, 75%, 100% function onPlayProgress(data) { try{ var t = data.data.duration - data.data.seconds <= 1.5 ? 1 : (Math.floor(data.data.seconds / data.data.duration * 4) / 4).toFixed(2); if (!lastP[data.player_id] || t > lastP[data.player_id]) { lastP[data.player_id]=t; if (parseFloat(t) != 0){ dataLayer.push({ event: "video", eventCategory: "vimeo", eventAction: "vimeo video " +t*100+ "% Complete", eventLabel: _playerTitle[getLabel(data.player_id)].toLowerCase() + " - " + getLabel(data.player_id), vimeo_playerID: getLabel(data.player_id), vimeo_playerTitle: _playerTitle[getLabel(data.player_id)].toLowerCase(), vimeo_playerAuthor: _playerAuthor[getLabel(data.player_id)].toLowerCase(), vimeo_playerAuthorURL: _playerAuthorURL[getLabel(data.player_id)].toLowerCase(), vimeo_playerUploadDate: _playerUploadDate[getLabel(data.player_id)], nonInteractive: true }) } } } catch(err){ } } </script>
Don’t forget to assign the previously created Pageview Trigger:
Checkpoint! Let’s see what we’ve created so far:
- A Pageview Trigger which checks whether Vimeo video player is embedded in the web page (thanks to a Custom JavaScript variable).
- A Vimeo Auto-Event Listener (as a Custom HTML tag) fires only when the aforementioned Pageview Trigger activates. Every time a Vimeo player interaction occurs, the listener will dispatch a Data Layer event with the following data:
- Event Name: video (this value never changes)
- eventCategory: Vimeo (this value never changes)
- eventAction. Possible values: Played video, Paused video, 10%, 25%, 50%, 75%, 90%, or 100%.
- eventLabel: [Video title] (this value is dynamically changed and depends on a video).
If you want to test this now, enable the Preview and Debug mode, refresh the page with the Vimeo player and try interacting with it. You should start seeing video events in the Preview mode’s left side.
Step 3. Create Data Layer Variables and a Custom Event Trigger
Google Tag Manager does not recognize Data Layer events or other data. So if you wish to transfer some information to other tools (e.g. Google Analytics), you need to “teach” GTM to fetch certain information (with the help of Data Layer Variables).
After variables are configured, it’s time to create a Custom Event Trigger. Vimeo Auto-Event Listener sends all interactions as Data Layer events under the name of “video”.
dataLayer.push({ event: "video", eventCategory: "vimeo", eventAction: "vimeo play", eventLabel: video_title });
So the next thing you should do is to create a Custom Event Trigger which listens to ALL video events. Later, it will be assigned to Google Analytics Tag.
In GTM, go to Triggers > New > Custom Event and enter the following settings:
Step 4. Create a Google Analytics Tag
Last but not least, Google Analytics. Now you need to send an event and pass the corresponding data with it. Create a new tag, select Universal Analytics as Tag Type, choose Event as Track Type, and enter all three Data Layer variables.
This means that every time a video event is created, a Google Analytics Event will be pushed to Google’s servers and all three variables will be dynamically replaced with the actual data.
If eventLabel equals to Video Title Lorem Ipsum, this exact value will be passed to Google Analytics.
Just make sure that you enter the GA settings Variable in its special dropdown (see the screenshot below).
Step 5. Test test test
Don’t forget to test this entire configuration. Enable GTM Preview and Debug mode, go to a page with an embedded Vimeo player, and click Play. The next thing you should see is a video event in Preview and Debug mode’s event stream. Click it and see whether the Universal Analytics tag has fired.
Also, don’t forget to check Google Analytics Real-time reports. Video events will appear in Realtime > Events reports. Within 24 hours (but usually much sooner), that event data will also appear in standard reports: Behavior > Events > Top Events.
The entire process in a nutshell
- We created a Custom JS variable that returns true if the Vimeo player is embedded on a page.
- Then we created a Pageview Trigger that checks whether Vimeo video player is embedded in the web page (thanks to a Custom JavaScript variable)
- Then we created a Custom HTML tag (a Vimeo Auto-Event Listener). And it will fire on all pages where the Vimeo player is embedded. Important: even if you haven’t interacted with the player yet, that tag will still be fired.
- Every time a Vimeo player interaction occurs, the listener will dispatch a Data Layer event with the following data:
- Event Name: video (this value never changes)
- eventCategory: Vimeo (this value never changes)
- eventAction. Possible values: Played video, Paused video, 10%, 25%, 50%, 75%, 90%, or 100%.
- eventLabel: [Video title] (this value is dynamically changed and depends on a video).
- You have created 3 Data Layer Variables and one Custom Event Trigger (for the video event).
- Finally, you created a Google Analytics tag that fires on the video event and will send the values of those 3 Data Layer Variables to Google Analytics.
Chapter 3. Six Google Tag Manager Video Tracking Recipes. All Unified.
Believe it or not, but the flow that I have described in the Vimeo player tracking section applies to a bunch of other non-Youtube video players:
- HTML5 Video player (supports various players). A solution created by David Vallejo.
- JW Player. A solution created by Pat Grady and David Vallejo.
- Vidyard
- Wistia. A solution created by the team of Bounteous.
Since most of the GTM Recipes are created by distinct authors, their naming convention is also diverse. That’s why I’ve spent some time and unified them (and included the 6th recipe – Youtube tracking with the built-in GTM functionality).
What does it mean? Well, if you use several types of video players on your website, video auto-event listeners will fire events following the same naming pattern. In other words, data in your Google Analytics event reports will be consistent. For example, Vimeo auto-event listener would fire this Data Layer event:
dataLayer.push({ event: "video", eventCategory: "vimeo", eventAction: "vimeo video play", eventLabel: "video title - video id" });
While HTML5 video player would send different this.
dataLayer.push({ event: "video", eventCategory: "video", eventAction: "play", eventLabel: video_url });
As a result, your event reports would be chaotic.
Anyway, here’s what data you’ll receive from each video auto-event listener:
P.S. Some video player listeners also send Resumed video and Video Error events.
P.P.S. JS Player listener will send the video file URL instead of the title if that video does not have a title.
Feel free to download any of these Google Tag Manager Recipes and start tracking video players in no time. Didn’t I tell you that Google Tag Manager Video Tracking was pretty easy?
![HTML5 video Tracking Recipe for Google Tag Manager](https://mlxzzgdzt2jx.i.optimole.com/cb:yX5x.1db17/w:auto/h:auto/q:mauto/f:best/ig:avif/https://clickety-clack.click/img/html5-video-tracking-recipe-image-2.png)
HTML5 Video Tracking Recipe
Tracks interactions of the HTML5 video player (play, pause, etc.)
![JW Player Tracking Recipe for Google Tag Manager](https://mlxzzgdzt2jx.i.optimole.com/cb:yX5x.1db17/w:auto/h:auto/q:mauto/f:best/ig:avif/https://clickety-clack.click/img/jw-player-tracking-recipe-image-2.jpg)
JW Player Tracking Recipe
Tracks interactions of embedded JW player (play, pause, complete, etc.).
![Vidyard Listener Recipe for Google Tag Manager](https://mlxzzgdzt2jx.i.optimole.com/cb:yX5x.1db17/w:auto/h:auto/q:mauto/f:best/ig:avif/https://clickety-clack.click/img/vidyard-listener-recipe-image-2.jpg)
Vidyard Tracking Recipe
Automatically tracks interactions of embedded Vidyard player (play, pause, etc.).
![Vimeo Recipe For Google Tag Manager](https://mlxzzgdzt2jx.i.optimole.com/cb:yX5x.1db17/w:auto/h:auto/q:mauto/f:best/ig:avif/https://clickety-clack.click/img/vimeo-recipe-image-2.jpg)
Vimeo Tracking Recipe
Tracks interactions of embedded Vimeo player (play, pause, etc.).
![Wistia Tracking Recipe for Google Tag Manager](https://mlxzzgdzt2jx.i.optimole.com/cb:yX5x.1db17/w:auto/h:auto/q:mauto/f:best/ig:avif/https://clickety-clack.click/img/wistia-tracking-recipe-image-2.jpg)
Wistia Tracking Recipe
Automatically tracks interactions with embedded Wistia videos.
![Youtube Tracking Recipe Image](https://mlxzzgdzt2jx.i.optimole.com/cb:yX5x.1db17/w:auto/h:auto/q:mauto/f:best/ig:avif/https://clickety-clack.click/img/youtube-tracking-recipe-image-2.jpg)
Youtube Tracking Recipe
Automatically tracks interactions with embedded YouTube video players on your site. This recipe uses the built-in Youtube video tracking functionality with GTM.
One thing that I’ve noticed is that the HTML5 recipe doesn’t play well with others (well, at least some of them). Sometimes, it overtakes all interactions and other video listeners stop working. So make sure that HTML5 auto-event listener does not fire on the same page as JW player, etc. Everything else should be fine. If you notice any bugs, please let me know.
Video tracking with Google Tag Manager: Final words
When it comes to video tracking with Google Tag Manager, the first thing that probably comes to your mind is the tracking of embedded Youtube videos. Well, I don’t blame you, since this is the most popular video hosting platform in the world.
But if you are dealing with a non-youtube video player, that is still possible with some custom solutions.
In this blog post, I’ve explained how you can track both Youtube and non-youtube videos embedded on your site.
And to save you some time, I’ve also included a bunch of recipes that you can download, import, configure, and start tracking videos in minutes.
Just download any of them, import to your GTM account, and start tracking videos in a blink of an eye.
All these Google Tag Manager video tracking solutions follow the same principle, so once you master one, you’ll conquer all of them.