Monday, April 15, 2024
No menu items!
HomeData Analytics and VisualizationYoutube trigger isn’t working in Google Tag Manager? 9 ways to fix...

Youtube trigger isn’t working in Google Tag Manager? 9 ways to fix it

Updated: October 12th, 2021

This is an updated version of the blog post posted back in 2016 (when the built-in Youtube trigger was not a thing in GTM yet).

In the autumn of 2017, the Google Tag Manager team released multiple awesome features and the native Youtube trigger was one of them. Before that happened, the industry was utilizing custom video tracking solutions created by Bounteous or Cardinal Path.

Even though the new Youtube trigger was a great addition to the feature set within GTM, it wasn’t (and still isn’t) bulletproof. Due to particular factors (I’ll explain them later), it will not be able to track some YT players. That’s why you can still occasionally see new posts popping up in GTM communities or forums asking for help tracking YT videos.

In this blog post, I’ll share several tips/workarounds you can implement in order to track videos when the Youtube trigger isn’t working in Google Tag Manager.

Table of contents

The result you should expect from the built-in Youtube trigger in GTM
Why is the built-in Youtube trigger not working in Google Tag Manager?
How to fix Youtube trigger in Google Tag Manager? (+ some alternatives)
#1. Enable JavaScript API support to all embedded Youtube videos
#2. Play around with the new “Enable this trigger on” setting
#3. Load the Youtube Iframe API library
#4. Privacy-enhanced mode
#5. Check if the video play contains the “src” attribute
#6. When src attribute is updated later
#7. Embedded Youtube video’s URL must contain valid query parameters

Other solutions to replace the built-in Youtube trigger in GTM
#8. Custom Youtube listener by Bounteous
#9. Modified Cardinal Path’s solution

Conclusion: what to do when Youtube trigger in Google Tag Manager is not working


The result you should expect from the built-in Youtube trigger in GTM

Here’s how Youtube video tracking should work in the perfect scenario:

You must have at least one Youtube video trigger enabled on a page
When you interact with the embedded Youtube video player, a event appears in the Preview and Debug console (that’s because the Youtube auto-event listener catches the interaction and pushes it to the Data Layer).
If this happens, you’re good to go. Enabled built-in video variables in GTM, create a tag (e.g. GA Event), and fire it on those Data Layer events.

However, this does not always happen. The main problem that occurs with Youtube trigger in GTM is that does not appear with all videos. Why?


Why is the built-in Youtube trigger not working in Google Tag Manager?

The main cause of this problem is a thing called lazy-loaded videos. For example, if a video player is not loaded together with the entire page document but later (e.g. in a popup after a user clicks a big blue button on your homepage), that is considered as a lazy-loaded video and the built-in GTM trigger will not track its interactions.

Another quite popular reason for the Youtube trigger not working is the absence of the enablejsapi query parameter in the embedded YT video player code. When this parameter is set to 1, the website (and GTM, of course) can start listening to the player via JavaScript API.

Other possible reasons are: the embedded Youtube video is using privacy-enhanced mode or has a query parameter origin set to null. Let’s take a closer look at them and what are the possible solutions.

How to fix Youtube trigger in Google Tag Manager? (+ some alternatives)


Option #1. Enable JavaScript API support to all embedded Youtube videos

The first thing you could do with the trigger if you’re not seeing events in the Preview and Debug console is to enable JavaScript API support. How can you do that? You have two options:

#1.1 Ask a developer (or change it yourself if you have access to the website’s code) to add an additional query parameter enablejsapi=1 to the URL of the embedded Youtube video. Before you do that, you need to make sure that this is a problem at all. Inspect the video player and keep looking for the iframe element that loads the YT video. Check its src attribute. Does the URL contain enablejsapi=1? If no, then you could indeed ask a developer to add this parameter to the embedded video’s URL.

#1.2. Use the checkbox in trigger’s settings: Add JavaScript API support to all Youtube videos. After you enable this checkbox, Google Tag Manager will check the existing embedded YT videos on a page and if some of them are missing this query parameter, it will be added automatically.

The main advantage of the second solution is that it does not require a developer’s input. But on the other hand, it will add a flicker effect to the player. This means that GTM will reload the player once in order to load it with the enablejsapi query parameter present. This can become an issue if a video on your page starts playing automatically (because it might start playing, then GTM will reload it and it will start playing again). This is a VERY rare edge case (but still worth knowing).

Important: If the embedded YT video is already using the enablejsapi parameter that is set to 0 (enablejsapi=0), then GTM will not change its value to 1. Unfortunately, that’s how GTM works right now (thanks to Simo Ahava for his comment).

Important #2: enablejsapi must be added to the URL as a valid query parameter. this means that it must go after the question mark and if there are multiple query parameters, then they should be connected with an ampersand &.

Good example:

Bad example:

Also, if there is no ampersand in front of the enablejsapi (when it should be), that’s also not good.

If you are dealing with the trouble of incorrect ampersands (e.g. encoded &), you must contact your developer and ask for a fix.

Also, another important note from Simo was that if the embedded player’s URL contains the query parameter origin that is set to null (origin=null), Youtube trigger will also not work. Ask a developer to remove that parameter from the URL (or it should contain the URL of the domain that the player is embedded on).


Option #2. Play around with the new “Enable this trigger on” setting

One of the main reasons why I published this blog post today is the new feature in GTM. On March 6th, Google Tag Manager team added a new setting to YT and Scroll triggers. You can now control when the trigger is enabled on a page:

On the earliest moment when GTM container loads (gtm.js event)
When the Document Object Module is ready, a.k.a. DOM Ready (gtm.dom event)
When all scripts are loaded on a page, a.k.a. Window Loaded (gtm.load event)

By default, Youtube Trigger is enabled on the DOM Ready event, however, you may try to delay it and enable on Window Loaded. Maybe that will help if the video is injected dynamically into the page and that happens after the DOM ready event has already occurred.


Option #3. Load the Youtube Iframe API library

I’ve learned this today as Sergii Lysenko (a member of the GTM community on Facebook) shared his tip (and Simo added his two cents as well). Thanks!

In addition to the enablejsapi=1 requirement, there are two more things you need to keep in mind in order to make the Youtube listener start working. If at least one of those two things (read requirements) is met then the built-in YT trigger in GTM will work:

An embedded video must be already present on a page when GTM container loads
Or the Youtube Iframe API script must be present on a page when GTM container loads

So if one of these two things are present on a page, then the YT listener will be activated and will start listening to video interactions.

However, as I have mentioned at the beginning of this blog post, the main problem comes with the lazy-loaded videos. When GTM loads, the lazily loaded video (and the Youtube Iframe API script) are not present yet, therefore, the listener never activates.

A solution? Load the Youtube API script before the Youtube Trigger is enabled. Create a Custom HTML tag with the following code:

<script src=””>

And fire it on All Pages (or only those pages where it is possible for the lazy-loaded Youtube video to appear). Just make sure you choose Page view trigger.

Then enable the Youtube trigger on DOM Ready event. Or if you have set the Custom HTML tag to fire on DOM Ready, then set the YT trigger to be enabled on Window Loaded event.

I haven’t tried to use both of them on the same event but I presume that there might be some race-conditions (I might be totally wrong there, so feel free to correct me).

And that’s it! When the page loads, the Youtube API script will be already available thus the Youtube trigger will be activated and listening to video interactions. As a result, you should start seeing events in the Preview and Debug console.

However, other factors may still be interfering with your YT tracking (e.g. enablejsapi parameter is set to “0” or is missing at all) so make sure you’ve read other tips here as well.

This is not a silver bullet (but still very useful tip). I just love the GTM community and constant learning!


Option #4. Privacy-enhanced mode

Youtube video player can also be embedded with the privacy-enhanced mode (that does not store cookies on visitor’s browser). However, if this setting is enabled, Youtube trigger will not work (because in this case, the video will be loaded not from the but from, instead).

How can you know whether the embedded video player is in the privacy-enhanced mode?

Inspect the player and check with is the source of the iframe (see the src attribute). If the domain is, that’s the privacy-enhanced mode.

What options do you have?

Disable the privacy-enhanced mode by changing the to in the code of the embedded player (this needs to be done in the web site’s source code or in content management system’s editor. However, in the light of GDPR this might not be the best option).
Accept the defeat and skip Youtube tracking.
Use a custom listener and modify its code to start listening to instead of If, for example, you want to start using solution #9 (from this blog post), replace these two domains with the in that listener’s code (it is available in the Custom HTML tag).


Option #5. Check if the embedded Youtube video contains the “src” attribute

Sometimes Youtube videos are embedded in a non-standard way. By saying “non-standard”, I mean that the embedded video’s src attribute (that contains the URL of the video) does not exist.

Instead, developers use the data-src attribute.

To make sure that you’re dealing with this issue, you should inspect the video player, locate the iframe element and check whether the src attribute (see the screenshot below) contains the URL.

If not, then the Youtube video tracking will not work.

In the screenshot below, the iFrame element has the src attribute but it contains the image, not the video URL. Instead, the video URL is stored in the data-src attribute.

What is the solution? Ask a developer to fix this issue and store the video URL in the src attribute. That’s the only solution.


Option #6. When src attribute is updated later

The subtitle of this option might look a bit misleading or confusing but bear with me. In fact, it is quite related to option #5. When developers want to optimize the page loading speed, one of the places that definitely can improve things is lazy-loaded Youtube video players.

For example, when a player is on a page, only the thumbnail is displayed. When the visitor then clicks the play button, the actual player is loaded and then you watch the video.

This, unfortunately, quite often is not supported by the built-in Youtube trigger in Google Tag Manager.

Here is a situation: when the page is loaded, Click Inspect Element on the player. If that inspected element contains the data-src attribute (that contains the URL of the video) BUT:

the src does not exist in that very same element
OR the src exists but its value is not….., then continue reading this tip.

Now, check another thing. While inspecting that element, click that video thumbnail/play button. Did the src attribute of the player get new value and that value is the URL of the video + additional parameters (like enablejsapi=1)?

If this is exactly what you are seeing in your project, here’s the explanation of why the Youtube trigger does not work. One of the things that this trigger is looking for is iframe elements that contain the src attribute with the URL of a Youtube video. This check happens when the GTM container is loaded (but depends on the settings).

During the initial page load, your Youtube players do not contain the “src” attribute with the Youtube URL (because the URL is stored in the data-src attribute). When GTM loads, the Youtube listener checks all the Youtube players on a page and inspects them once. When the element is inspected, it gets a new attribute, called data-gtm-yt-inspected…. 

Even if the Youtube player’s URL (in the src attribute) changes, that change will not be noticed by GTM anymore (because the player was already inspected and it got that data-gtm-yt-inspected… attribute.

How can you solve this? You’ll need the developer’s input.

Your developer must do certain changes in code that is responsible for the lazy-loading of the video. Your developer will have to figure this out.

Currently, when a visitor clicks on the video thumbnail, the src attribute is updated with the actual Youtube URL. To make sure that GTM Youtube listener notices this, ask a developer to also add some functionality that removes all the data-gtm-yt-inspected… attributes that were added. This must be done every time a visitor clicks the thumbnail and the actual video player is loaded.

This will force GTM to check the player once again and start tracking it.

Keep in mind that in your case, the number of data-gtm-yt-inspected… attributes and their names will, most likely, be different.


Option #7. Embedded Youtube video’s URL must contain valid query parameters

When I say “valid” query parameters, I mean that they must come after a question mark (in the URL) and multiple parameters must be connected with an ampersand (&).

This is a correct example of the URL (that you can see if you do the “Inspect element” on the embedded Youtube player):

But in some rare cases, you might see something like this:

Every URL parameter is connected with &amp; instead of just &. This breaks the Youtube video player tracking in GTM. The solution here is to ask a developer to identify the code on a site that is causing this and fix it.


Other solutions (replacements of the built-in Youtube trigger in GTM)

Even though the aforementioned tips should be enough to fix the Youtube Trigger in Google Tag Manager, you might also find useful two tips mentioned below. They are both custom (in fact, that’s how the web tracking industry was rolling back in the older days of Youtube tracking).


Option #8. Custom Youtube listener by Bounteous

Before GTM offered the built-in Youtube trigger, people were using either Bounteous‘ or Cardinal Path’s custom solutions. Bounteous’ listener supports lazy-loaded videos (at least in the majority of cases that I remember). So if for some reason, none of the above tips helped (even though that’s very unlikely), you can try implementing this one.

Just remember that this custom solution uses custom triggers and variables (meaning that you’ll need to create Data Layer Variables and Custom Event trigger on your own. The built-in video triggers will not work here.

P.S. Here’s a GTM recipe (that automatically created triggers, variables, etc.).


Option #9. Modified Cardinal Path’s solution

Another custom solution that was popular in the industry was created by Cardinal Path. However, I remember working on one project where neither Bounteous’ nor this Cardinal Path’s solution worked.

As a result, I asked a colleague of mine who is a JavaScript developer to modify the listener a bit. Now that listener is listening to changes of the Youtube player every second. So even if the video is lazily loaded, after 1 second it will be tracked. Here’s the listener’s code:

//enable the JavaScript API for an embedded player
for (var e = document.getElementsByTagName(“iframe”), x = e.length; x–;)
if (/[x].src))
if (e[x].src.indexOf(‘enablejsapi=’) === -1)
e[x].src += (e[x].src.indexOf(‘?’) === -1 ? ‘?’ : ‘&’) + ‘enablejsapi=1’;

var gtmYTListeners = [],
gtmYTListenersStates = []; // 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 (/[x].src))
gtmYTListeners.push(new YT.Player(e[x],
onError: onPlayerError,
onReady: onPlayerReady
//onStateChange: onPlayerStateChange
YT.gtmLastAction = “p”;

function onPlayerReady(e)
var url =;
gtmYTListenersStates[url] =;

setInterval(function ()
var state =;

if (gtmYTListenersStates[url] !== state)
{ = state;
gtmYTListenersStates[url] = state;
}, 100);

// listen for play, pause and end states
// also report % played every second
function onPlayerStateChange(e)
e[“data”] == YT.PlayerState.PLAYING && setTimeout(onPlayerPercent, 1000, e[“target”]);
var video_data =[“getVideoData”](),
label = video_data.title;
// Get title of the current page
var pageTitle = document.title;
if (e[“data”] == YT.PlayerState.PLAYING && YT.gtmLastAction == “p”)
label = “Video Played – ” + video_data.title;
‘event’: ‘youtube’,
‘eventCategory’: ‘Youtube Videos’,
‘eventAction’: pageTitle,
‘eventLabel’: label
YT.gtmLastAction = “”;
if (e[“data”] == YT.PlayerState.PAUSED)
label = “Video Paused – ” + video_data.title;
‘event’: ‘youtube’,
‘eventCategory’: ‘Youtube Videos’,
‘eventAction’: pageTitle,
‘eventLabel’: 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!
function onPlayerError(e)
‘event’: ‘error’,
‘eventCategory’: ‘Youtube Videos’,
‘eventAction’: ‘GTM’,
‘eventLabel’: “youtube:” + e[“target”][“src”] + “-” + e[“data”]

// 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.title;
// Get title of the current page
var pageTitle = document.title;
e[“lastP”] = t;
label = t * 100 + “% Video played – ” + video_data.title;
‘event’: ‘youtube’,
‘eventCategory’: ‘Youtube Videos’,
‘eventAction’: pageTitle,
‘eventLabel’: label
e[“lastP”] != 1 && setTimeout(onPlayerPercent, 1000, e);

// load the Youtube JS api and get going
var j = document.createElement(“script”),
f = document.getElementsByTagName(“script”)[0];
j.src = “//”;
j.async = true;
f.parentNode.insertBefore(j, f);

It will push the “youtube” events to the Data Layer. If you’re not sure what variables and trigger should you create, everything is explained in the Cardinal Path’s blog post. All you need to do is to update Cardinal Path’s Custom HTML tag’s code with the one that I’ve posted above.

The solution isn’t perfect (compared to those that are at the beginning of the post) and it can cause some video flickering but if you are looking for a plan B, this might be it.

In this case, you should also remember that this solution uses custom triggers and variables (meaning that you’ll need to create Data Layer Variables and Custom Event trigger on your own. The built-in video triggers will not work here.

P.S. Here’s a GTM recipe (that automatically created triggers, variables, etc.).


Bonus Option #9. Changed video URL

This is kind of an edge case but still worth checking.  So, recently I stumbled upon a case where none of the above-mentioned solutions worked (this happened in the #MeasureSlack). A video player appeared in a popup only after a visitor clicked a certain button on a page.

But there was something different that broke the player tracking.

Apparently, the Youtube video player was always present on a page but it was hidden. When GTM loaded, Youtube listener successfully spotted the video player and was ready to start listening. However, when the button was clicked (that opens a video popup), some custom JavaScript on a page changed the Youtube video URL (added an additional parameter ?autoplay=1). This caused the iFrame to reload and create a new instance of the iFrame, which is no longer tracked by GTM.

GTM’s built-in Youtube tracking checks video players on a page only once, hence that new instance is ignored.

Here are a bit more details. Basically, I opened the Elements inspector in my Chrome browser and did a quick search for /embed/ (because your embedded Youtube videos have that in URL).

Here’s a screenshot of the embedded player’s URL while the popup is still invisible.

Memorize this. The URL contains the hostname, /embed/some_id, and two query parameters (enablejsapi and origin).

However, when I click the button that opens a popup with Youtube video, its URL changes to this one:

A new query parameter is added to the URL, autoplay=1. This URL change caused the issue that GTM Youtube tracking was not working. And this is not related ONLY to autoplay. There could have been any parameter or any other change. Youtube tracking would not have worked.

What’s the solution? You need to tell the developers to not change the video URL. Or have those parameters added to YT video URL right from the beginning.

This cause of this issue was spotted (to no one’s surprise) by Simo Ahava.


Conclusion: what to do when Youtube trigger isn’t working in Google Tag Manager

So there you have it. 9 ways how you can solve the puzzle when the built-in Youtube trigger isn’t working in Google Tag Manager.  Here are the key takeaways:

The default Youtube trigger will properly track YT videos if:
enablejsapi is added to the URL of the embedded Youtube video player and is set to 1 (you can check that by inspecting the iframe of the Youtube video and checking its src attribute).
and one of these conditions is met:
Youtube video player is already present on a page when GTM container loads (and that’s why lazy loaded videos are not tracked by default)
Youtube Iframe API script is already present when the GTM container loads

The enablejsapi issue can be solved either by a developer (ask him/her to add this parameter to the src of the iframe) or by enabling Add JavaScript API support to all Youtube videos checkbox in your GTM trigger. However, if the video URL already contains the enablejsapi=0 (means disabled), ask a developer to set it to 1 in the website’s source code.

As for those other two conditions (where at least one must be met), create a Custom HTML tag that contains the Youtube Iframe API script and fire it before the Youtube trigger is enabled.

<script src=””>

All pages trigger assigned to that Custom HTML tag should do the trick.

If these settings do not work (even though they should), consider checking other tips:

Take a look at whether your embedded videos are using the privacy-enhanced Youtube mode
Try implementing custom Youtube listeners. These solutions are not very elegant (compared to the built-in functionality of GTM) but still might serve as a plan B.
Check whether the URL of the embedded YT player does not contain origin=null query parameter. If it does, it should be removed (or set to the domain of the parent page).

As always, the comments section is at your service. Did I miss anything? Let me know.

The post Youtube trigger isn’t working in Google Tag Manager? 9 ways to fix it appeared first on Analytics Mania.

Read MoreAnalytics Mania



Please enter your comment!
Please enter your name here

Most Popular

Recent Comments