February 11th, 2015 update
November 15th, 2014 update
  • track onunload event
  • minor fix to error reporting
June 20th, 2014 update
  • now supports multiple players on the same page
  • includes optional code to add the necessary enablejsapi=1 to the video URL
  • now track the video title instead of the video id
September 16th, 2015 update

During our GTM+UA webinar I was asked about tracking YouTube videos with the help of Google Tag Manager and Universal Analytics. I quickly demonstrated how we’re doing it in our WASP playground.

Here’s a step by step guide to the macros, tags and rules required to track YouTube video embeds on your website.

A note for Google Analytics users: For sake of demonstration I’m showing Universal Analytics, but you could easily replace the Universal Analytics Event by a regular Google Analytics one and everything will work fine.


We will use the YouTube JavaScript API to expose user interactions with the video. In order for this to work, each of your YouTube embeds will need to include “enablejsapi=1” to the link of the video, for example:

<iframe width="420" height="315" src="//www.youtube.com/embed/Rvw0uxuYBCo?enablejsapi=1" frameborder="0" allowfullscreen></iframe>

Note: the “Is Youtube present” macro could be modified to automatically add the enablejsapi if it’s not there, but this will result in a quick flash of the Youtube frame.

Note: by default, related videos will be shown and tracked. If you want to disable this, add “&rel=0” to the Youtube embed link.

What you’ll get

Under Behavior/Events/Top Events, you will see an event category named “video”, with event actions named “pause”, “play”, 0%, 25%, 50%, 75% and 100%. The event label will be the unique identifier of the video.

Custom Report for Video Performance

If you want to get details about how individual videos are performing, follow those steps:

  1. Go in yout Behavior/Events/Top Events/video report;
  2. Click on “Customize” in the top left of the report;
  3. Under Dimension Drilldown, remove the “Event Category” dimension and switch “Event Action” with “Event Label”, so it now reads Event Label/Event Action;
  4. Modify the filter so it now reads “Event Category equals video”, so only videos are shown in this new custom report;
  5. Voilà! You now have detailed performance info about each individual video embedded on your website.


Is Youtube present?

You have two options here, either use this macro to automatically detect when a video is embedded on a page, or use a rule to include the Youtube Listener tag only on pages where you know there’s a video.

Macro Name: Youtube is present
Macro Type: Custom JavaScript
Custom JavaScript:
// Return "true" if there is at least one Youtube video on the page
function () {
    for (var e = document.getElementsByTagName('iframe'), x = e.length; x--;)
        if (/youtube.com\/embed/.test(e[x].src)) return true;
    return false;


Macro Name: event
Macro Type: Custom Event

dataLayer action

Macro Name: dataLayer action
Macro Type: Data Layer Variable
Data Layer Variable Name: action

dataLayer label

Macro Name: dataLayer label
Macro Type: Data Layer Variable
Data Layer Variable Name: label


Listen for Youtube activity

Tag Name: Youtube Listener
Tag Type: Custom HTML Tag
Firing Rule: Youtube present
<script type="text/javascript">
// OPTIONAL: Enable JSAPI if it's not already on the URL
// note: this will cause the Youtube player to "flash" on the page when reloading to enable the JS API
for (var e = document.getElementsByTagName("iframe"), x = e.length; x--;)
  if (/youtube.com\/embed/.test(e[x].src))
     if(e[x].src.indexOf('enablejsapi=') === -1)
        e[x].src += (e[x].src.indexOf('?') ===-1 ? '?':'&') + 'enablejsapi=1';

var gtmYTListeners = []; // support multiple players on the same page
// attach our YT listener once the API is loaded
function onYouTubeIframeAPIReady() {
    for (var e = document.getElementsByTagName("iframe"), x = e.length; x--;) {
        if (/youtube.com\/embed/.test(e[x].src)) {
            gtmYTListeners.push(new YT.Player(e[x], {
                events: {
                    onStateChange: onPlayerStateChange,
                    onError: onPlayerError
            YT.gtmLastAction = "p";

// listen for play/pause, other states such as rewind and end could also be added
// also report % played every second
function onPlayerStateChange(e) {
    e["data"] == YT.PlayerState.PLAYING && setTimeout(onPlayerPercent, 1000, e["target"]);
    var video_data = e.target["getVideoData"](),
        label = video_data.video_id+':'+video_data.title;
    if (e["data"] == YT.PlayerState.PLAYING && YT.gtmLastAction == "p") {
            event: "youtube",
            action: "play",
            label: label
        YT.gtmLastAction = "";
    if (e["data"] == YT.PlayerState.PAUSED) {
            event: "youtube",
            action: "pause",
            label: label
        YT.gtmLastAction = "p";

// catch all to report errors through the GTM data layer
// once the error is exposed to GTM, it can be tracked in UA as an event!
// refer to https://developers.google.com/youtube/js_api_reference#Events onError
function onPlayerError(e) {
        event: "error",
        action: "GTM",
        label: "youtube:" + e

// report the % played if it matches 0%, 25%, 50%, 75% or completed
function onPlayerPercent(e) {
    if (e["getPlayerState"]() == YT.PlayerState.PLAYING) {
        var t = e["getDuration"]() - e["getCurrentTime"]() <= 1.5 ? 1 : (Math.floor(e["getCurrentTime"]() / e["getDuration"]() * 4) / 4).toFixed(2);         if (!e["lastP"] || t > e["lastP"]) {
            var video_data = e["getVideoData"](),
                label = video_data.video_id+':'+video_data.title;
            e["lastP"] = t;
                event: "youtube",
                action: t * 100 + "%",
                label: label
        e["lastP"] != 1 && setTimeout(onPlayerPercent, 1000, e);

// Crossbrowser onbeforeunload hack/proxy
// https://developer.mozilla.org/en-US/docs/WindowEventHandlers.onbeforeunload
window.onbeforeunload = function (e) {
 var e = e || window.event;
 // For IE and Firefox prior to version 4
 e.returnValue = 'na';
 // For Safari
 return 'na';
window.onbeforeunload = trackYTUnload;
function trackYTUnload() {
 for (var i = 0; i < gtmYTplayers.length; i++)
 if (gtmYTlisteners[i].getPlayerState() === 1) { // playing
 var video_data = gtmYTlisteners[i]['getVideoData'](),
 label = video_data.video_id+':'+video_data.title;
 event: 'youtube',
 action: 'exit',
 label: label
// load the Youtube JS api and get going
var j = document.createElement("script"),
    f = document.getElementsByTagName("script")[0];
j.src = "//www.youtube.com/iframe_api";
j.async = true;
f.parentNode.insertBefore(j, f);

UA Youtube event

If you are using Google Analytics instead of Universal Analytics, simply change the Tag Type to Google Analytics and everything will work fine.

Tag Name: Youtube Event
Tag Type: Universal Analytics
Tracking ID: UA-XXXXXX-Y
Track Type: Event
Event Tracking Parameters:
Category: {{event}}
Action: {{dataLayer action}}
Label: {{dataLayer label}}
Firing rule: Youtube event


Youtube embed found on a page

Rule Name: Youtube present
Conditions: {{event}} equals gtm.dom AND {{youtube is present}} equals true

Youtube is telling us something

Rule Name: Youtube event
Conditions: {{event}} equals youtube

Putting it all together

That’s it! Now you can track Youtube video embeds on your own website thanks to Google Tag Manager and Universal Analytics.

Don’t forget to use Google Tag Manager “debug & preview” with WASP to make sure everything works fine before you publish.

This topic, and many more, are covered in our GTM+UA  Master Class. Please check the Cardinal Path training calendar for more locations.

Check out the new, and up to date version by Nicky Yuen.