How to Track Vimeo Videos Using a Google Tag Manager Event Listener (Step-by-Step)

If you’re using Google Tag Manager, Vimeo tracking doesn’t come out of the box like YouTube. So you need a custom listener to capture video engagement properly.

In this guide, I’ll show you exactly how to implement it—including the full working code—so you can track starts, progress, and completions.

What You’ll Track

With this setup, you’ll capture:

  • Video start
  • Progress (10%, 25%, 50%, 75%)
  • Video complete
  • Title, URL, duration, and playback time

All pushed into the dataLayer for GTM.

Step 1: Add the Full Vimeo Tracking Code

 Add this inside a Custom HTML tag in GTM (trigger: All Pages or DOM Ready)

Here is the complete working code:

<script src=”https://player.vimeo.com/api/player.js”></script>

<script>

(function() {

    var videoProgress = [10, 25, 50, 75];

    function handlePlayer(player) {

        var triggeredSteps = {};

        var video_title = ”;

        var video_url = ”;

        var isStarted = false;

        var upperRange = 2;

        player.getVideoTitle().then(function(title) {

            video_title = title;

        });

        player.getVideoUrl().then(function(url) {

            video_url = url;

        });

        player.on(‘play’, function(data) {

            if (!isStarted) {

                if (data.duration < 30) {

                    upperRange = 6;

                }

                window.dataLayer = window.dataLayer || [];

                dataLayer.push({

                    event: ‘Vimeo Video’,

                    video_status: ‘start’,

                    video_current_time: 0,

                    video_duration: data.duration,

                    video_percent: 0,

                    video_provider: ‘Vimeo’,

                    video_title: video_title,

                    video_url: video_url

                });

                isStarted = true;

            }

        });

        player.on(‘ended’, function(data) {

            window.dataLayer = window.dataLayer || [];

            dataLayer.push({

                event: ‘Vimeo Video’,

                video_status: ‘complete’,

                video_current_time: data.seconds,

                video_duration: data.duration,

                video_percent: 100,

                video_provider: ‘Vimeo’,

                video_title: video_title,

                video_url: video_url

            });

        });

        player.on(‘timeupdate’, function(data) {

            var actualPercent = data.percent * 100;

            videoProgress.forEach(function(targetPercent) {

                if (

                    !triggeredSteps[targetPercent] &&

                    actualPercent >= targetPercent &&

                    actualPercent <= targetPercent + upperRange

                ) {

                    window.dataLayer = window.dataLayer || [];

                    dataLayer.push({

                        event: ‘Vimeo Video’,

                        video_status: ‘progress’,

                        video_current_time: data.seconds,

                        video_duration: data.duration,

                        video_percent: targetPercent,

                        video_provider: ‘Vimeo’,

                        video_title: video_title,

                        video_url: video_url

                    });

                    triggeredSteps[targetPercent] = true;

                }

            });

        });

    }

    function findVimeoPlayer(node) {

        if (node.tagName === ‘IFRAME’ && node.src.includes(‘player.vimeo.com/video’)) {

            var player = new Vimeo.Player(node);

            handlePlayer(player);

        } else if (node.querySelectorAll) {

            var iframes = node.querySelectorAll(‘iframe’);

            iframes.forEach(function(iframe) {

                if (iframe.src.includes(‘player.vimeo.com/video’)) {

                    var player = new Vimeo.Player(iframe);

                    handlePlayer(player);

                }

            });

        }

    }

    var observer = new MutationObserver(function(mutationsList) {

        mutationsList.forEach(function(mutation) {

            if (mutation.type === ‘childList’) {

                mutation.addedNodes.forEach(function(node) {

                    findVimeoPlayer(node);

                });

            }

        });

    });

    observer.observe(document.body, { childList: true, subtree: true });

    var existingPlayers = document.querySelectorAll(‘iframe[src*=”player.vimeo.com/video”]’);

    existingPlayers.forEach(function(playerNode) {

        handlePlayer(new Vimeo.Player(playerNode));

    });

})();

</script>

Step 2: Create GTM Trigger

Inside Google Tag Manager:

  • Trigger Type → Custom Event
  • Event Name → Vimeo Video

This will listen to all dataLayer pushes from the script.

Step 3: Create Data Layer Variables

Create these variables in GTM:

  • video_status
  • video_percent
  • video_title
  • video_url
  • video_duration
  • video_current_time

Step 4: Send to GA4

Create a GA4 Event Tag:

Event Name:

vimeo_video

Parameters:

ParameterValue
status{{video_status}}
percent{{video_percent}}
title{{video_title}}
url{{video_url}}

Step 5: Test Everything

Use Preview mode in GTM:

  • Play video
  • Watch events fire
  • Confirm values

You should see:

  • start
  • progress (10, 25, 50, 75)
  • complete

Final Thoughts

This setup gives you full Vimeo tracking control inside Google Tag Manager—something most setups miss.

Once implemented, you can:

  • Build video engagement funnels
  • Optimize content performance
  • Use video as conversion signals