{"id":146201,"date":"2025-03-20T18:44:07","date_gmt":"2025-03-20T18:44:07","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/google-tag-manager-soundcloud-integration\/"},"modified":"2025-03-20T18:44:07","modified_gmt":"2025-03-20T18:44:07","slug":"google-tag-manager-soundcloud-integration","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=146201","title":{"rendered":"Google Tag Manager: SoundCloud Integration"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>According to their website, <a href=\"https:\/\/soundcloud.com\/\">SoundCloud<\/a> is \u201cthe world\u2019s leading social sound platform where anyone can create sounds and share them everywhere\u201d. For artists, it\u2019s a channel for distributing previews of their tracks, and for people like me it\u2019s a nice way to do some API tinkering. To each their own, I guess!<\/p>\n<div style=\"aspect-ratio: 1276 \/ 898;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2015\/06\/gtm-soundcloud.jpg\" title=\"Google Tag Manager SoundCloud\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"898\" width=\"1276\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2015\/06\/gtm-soundcloud.jpg#ZgotmplZ\" alt=\"Google Tag Manager SoundCloud\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>I saw a number of requests in the <a href=\"https:\/\/plus.google.com\/u\/0\/communities\/104865292981489764063\">Google+ Google Tag Manager<\/a> community about a SoundCloud integration, so I decided to look into it to see if I could just build one.<\/p>\n<p>SoundCloud has something called the <a href=\"https:\/\/developers.soundcloud.com\/docs\/api\/html5-widget\">Widget API<\/a>, which listens for <code>window.postMessage<\/code> calls from within the embedded SoundCloud iframes. The benefit of this versus, for example, the <a href=\"http:\/\/www.cardinalpath.com\/youtube-video-tracking-with-gtm-and-ua-a-step-by-step-guide\/\">YouTube API<\/a> is that you don\u2019t need to do anything to the iframe itself to make this work. All you need to do is load the Widget API, and then indicate which iframe(s) you want to listen to for interactions.<\/p>\n<p>                <span class=\"simmer\"><br \/>\n  <span class=\"close\">X<\/span><\/p>\n<p>\n    <span class=\"fa fa-md fa-bell\"\/><br \/>\n    <strong>The Simmer Newsletter<\/strong>\n  <\/p>\n<p>\n    Subscribe to the <a href=\"https:\/\/www.simoahava.com\/newsletter\/\">Simmer newsletter<\/a> to get the latest news and content from Simo Ahava into your email inbox!\n  <\/p>\n<p>  <\/span><\/p>\n<h2 id=\"the-setup\">The setup<\/h2>\n<p>For this to work, you will need the following:<\/p>\n<ul>\n<li>\n<p><strong>Custom HTML Tag<\/strong> which loads the Widget API, adds the listeners, and does the <code>dataLayer.push()<\/code> calls<\/p>\n<\/li>\n<li>\n<p><strong>Custom Event Trigger<\/strong> which fires your Tag when a SoundCloud event is registered<\/p>\n<\/li>\n<li>\n<p><strong>Data Layer Variables<\/strong> for Event Category, Event Action, and Event Label<\/p>\n<\/li>\n<li>\n<p><strong>Event Tag<\/strong> which fires when the Trigger is activated, and sends the event hit to Google Analytics<\/p>\n<\/li>\n<\/ul>\n<p>The most complex component is the Custom HTML Tag, so let\u2019s start there.<\/p>\n<h2 id=\"the-custom-html-tag\">The Custom HTML Tag<\/h2>\n<p>Here\u2019s the full Tag code. Copy-paste it into a new Custom HTML Tag:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-html\" data-lang=\"html\"><span style=\"color:#aaa;font-style:italic\"><!-- Load the SoundCloud API synchronously --><\/span>\nscript <span style=\"color:#1e90ff\">src<\/span>=<span style=\"color:#a50\">\"https:\/\/w.soundcloud.com\/player\/api.js\"<\/span>&gt;<span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;\n<span style=\"color:#aaa;font-style:italic\"><!-- Initiate the API integration --><\/span>\nscript&gt;\n  (<span style=\"color:#00a\">function<\/span>() {\n    <span style=\"color:#00a\">try<\/span> {\n      <span style=\"color:#aaa;font-style:italic\">\/\/ initWidget is called when a SoundCloud iframe is found on the page\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>      <span style=\"color:#00a\">var<\/span> initWidget = <span style=\"color:#00a\">function<\/span>(w) {\n        <span style=\"color:#00a\">var<\/span> currentSound, act, pos, q1, q2, q3, go, lab;\n        <span style=\"color:#00a\">var<\/span> cat = <span style=\"color:#a50\">'SoundCloud'<\/span>;\n        <span style=\"color:#00a\">var<\/span> widget = SC.Widget(w);\n        \n        <span style=\"color:#aaa;font-style:italic\">\/\/ Events.READY is dispatched when the widget has been loaded\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>        widget.bind(SC.Widget.Events.READY, <span style=\"color:#00a\">function<\/span>() {\n          \n          <span style=\"color:#aaa;font-style:italic\">\/\/ Get the title of the currently playing sound\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>          widget.getCurrentSound(<span style=\"color:#00a\">function<\/span>(cs) { \n            lab = cs[<span style=\"color:#a50\">'title'<\/span>]; \n          });\n          \n          <span style=\"color:#aaa;font-style:italic\">\/\/ Fire a dataLayer event when Events.PLAY is dispatched\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>          widget.bind(SC.Widget.Events.PLAY, <span style=\"color:#00a\">function<\/span>() { \n            act = <span style=\"color:#a50\">'Play'<\/span>; \n            sendDl(cat, act, lab); \n          });\n          \n          <span style=\"color:#aaa;font-style:italic\">\/\/ Fire a dataLayer event when Events.PAUSE is dispatched\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>          <span style=\"color:#aaa;font-style:italic\">\/\/ The only exception is when the sound ends, and the auto-pause is not reported\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>          widget.bind(SC.Widget.Events.PAUSE, <span style=\"color:#00a\">function<\/span>(obj) { \n            pos = <span style=\"color:#0aa\">Math<\/span>.round(obj[<span style=\"color:#a50\">'relativePosition'<\/span>] * <span style=\"color:#099\">100<\/span>);\n            <span style=\"color:#00a\">if<\/span> (pos !== <span style=\"color:#099\">100<\/span>) {\n              act = <span style=\"color:#a50\">'Pause'<\/span>; \n              sendDl(cat, act, lab); \n            }\n          });\n          \n          <span style=\"color:#aaa;font-style:italic\">\/\/ As the play progresses, send events at 25%, 50% and 75%\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>          widget.bind(SC.Widget.Events.PLAY_PROGRESS, <span style=\"color:#00a\">function<\/span>(obj) { \n            go = <span style=\"color:#00a\">false<\/span>;\n            pos = <span style=\"color:#0aa\">Math<\/span>.round(obj[<span style=\"color:#a50\">'relativePosition'<\/span>] * <span style=\"color:#099\">100<\/span>);\n            <span style=\"color:#00a\">if<\/span> (pos === <span style=\"color:#099\">25<\/span> &amp;&amp; !q1) {\n              act = <span style=\"color:#a50\">'25%'<\/span>;\n              q1 = <span style=\"color:#00a\">true<\/span>;\n              go = <span style=\"color:#00a\">true<\/span>;\n            }\n            <span style=\"color:#00a\">if<\/span> (pos === <span style=\"color:#099\">50<\/span> &amp;&amp; !q2) {\n              act = <span style=\"color:#a50\">'50%'<\/span>;\n              q2 = <span style=\"color:#00a\">true<\/span>;\n              go = <span style=\"color:#00a\">true<\/span>;\n            }\n            <span style=\"color:#00a\">if<\/span> (pos === <span style=\"color:#099\">75<\/span> &amp;&amp; !q3) {\n              act = <span style=\"color:#a50\">'75%'<\/span>;\n              q3 = <span style=\"color:#00a\">true<\/span>;\n              go = <span style=\"color:#00a\">true<\/span>;\n            }\n            <span style=\"color:#00a\">if<\/span> (go) {\n              sendDl(cat, act, lab);\n            }\n          });\n          \n          <span style=\"color:#aaa;font-style:italic\">\/\/ When the sound finishes, send an event at 100%\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>          widget.bind(SC.Widget.Events.FINISH, <span style=\"color:#00a\">function<\/span>() { \n            act = <span style=\"color:#a50\">'100%'<\/span>; \n            q1 = q2 = q3 = <span style=\"color:#00a\">false<\/span>; \n            sendDl(cat, act, lab); \n          });\n        });\n      };\n      \n      <span style=\"color:#aaa;font-style:italic\">\/\/ Generic method for pushing the dataLayer event\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>      <span style=\"color:#aaa;font-style:italic\">\/\/ Use a Custom Event Trigger with \"scEvent\" as the event name\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>      <span style=\"color:#aaa;font-style:italic\">\/\/ Remember to create Data Layer Variables for eventCategory, eventAction, and eventLabel\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>      <span style=\"color:#00a\">var<\/span> sendDl = <span style=\"color:#00a\">function<\/span>(cat, act, lab) {\n        <span style=\"color:#0aa\">window<\/span>.dataLayer.push({\n          <span style=\"color:#a50\">'event'<\/span> : <span style=\"color:#a50\">'scEvent'<\/span>,\n          <span style=\"color:#a50\">'eventCategory'<\/span> : cat,\n          <span style=\"color:#a50\">'eventAction'<\/span> : act,\n          <span style=\"color:#a50\">'eventLabel'<\/span> : lab\n        });\n      };\n\n      <span style=\"color:#aaa;font-style:italic\">\/\/ For each SoundCloud iFrame, initiate the API integration\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>      <span style=\"color:#00a\">var<\/span> i,len;\n      <span style=\"color:#00a\">var<\/span> iframes = <span style=\"color:#0aa\">document<\/span>.querySelectorAll(<span style=\"color:#a50\">'iframe[src*=\"api.soundcloud.com\"]'<\/span>);\n      <span style=\"color:#00a\">for<\/span> (i = <span style=\"color:#099\">0<\/span>, len = iframes.length; i 1) {\n        initWidget(iframes[i]);\n      }\n    } <span style=\"color:#00a\">catch<\/span>(e) { console.log(<span style=\"color:#a50\">'Error with SoundCloud API: '<\/span> + e.message); }\n  })();\n<span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;<\/code><\/pre>\n<\/div>\n<p>Make sure this Custom HTML Tag fires upon a <strong>Page View<\/strong> Trigger, where the Trigger Type is <strong>DOM Ready<\/strong>. If it doesn\u2019t work, try changing the Trigger Type to <strong>Window Loaded<\/strong>. It\u2019s possible a race condition emerges, where the Custom HTML Tag is fired before your SoundCloud widgets are loaded, and shifting the Trigger to fire on Window Loaded should remedy that.<\/p>\n<p>Let\u2019s chop it up into pieces so we\u2019ll understand what\u2019s happening. This time, we\u2019ll start from the end!<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ For each SoundCloud iFrame, initiate the API integration\n<\/span><span style=\"color:#aaa;font-style:italic\"\/><span style=\"color:#00a\">var<\/span> i,len;\n<span style=\"color:#00a\">var<\/span> iframes = <span style=\"color:#0aa\">document<\/span>.querySelectorAll(<span style=\"color:#a50\">'iframe[src*=\"api.soundcloud.com\"]'<\/span>);\n<span style=\"color:#00a\">for<\/span> (i = <span style=\"color:#099\">0<\/span>, len = iframes.length; i 1) {\n  initWidget(iframes[i]);\n}\n<\/code><\/pre>\n<\/div>\n<p>The code above goes through all the iframes on the page. If it encounters an iframe that loads an embedded SoundCloud Widget, it calls the <code>initWidget<\/code> method, using the iframe object as a parameter. This is as simple as it gets. The cool thing is that each iframe gets its own bindings, so you can run the script with multiple SoundCloud widgets on the page!<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#00a\">var<\/span> initWidget = <span style=\"color:#00a\">function<\/span>(w) {\n  <span style=\"color:#00a\">var<\/span> currentSound, act, pos, q1, q2, q3, go, lab;\n  <span style=\"color:#00a\">var<\/span> cat = <span style=\"color:#a50\">'SoundCloud'<\/span>;\n  <span style=\"color:#00a\">var<\/span> widget = SC.Widget(w);\n        \n  <span style=\"color:#aaa;font-style:italic\">\/\/ Events.READY is dispatched when the widget has been loaded\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  widget.bind(SC.Widget.Events.READY, <span style=\"color:#00a\">function<\/span>() {\n          \n    <span style=\"color:#aaa;font-style:italic\">\/\/ Get the title of the currently playing sound\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    widget.getCurrentSound(<span style=\"color:#00a\">function<\/span>(cs) { \n      lab = cs[<span style=\"color:#a50\">'title'<\/span>]; \n    });          \n<\/code><\/pre>\n<\/div>\n<p>The <code>initWidget<\/code> function is called for all the SoundCloud iframes on the page. First, it declares some utility variables. Next, it uses the Widget API <code>SC.Widget<\/code> constructor to create a new widget object the API uses for the bindings.<\/p>\n<p>On the following lines, the <code>SC.Widget.Events.READY<\/code> event is bound to the widget object. This event is fired when the embedded SoundCloud object has loaded and is ready to be interacted with. All the listeners are put into this function callback, as we don\u2019t want to start listening for events before the embedded file has loaded, right?<\/p>\n<p>The first thing we do is get the title of the sound, and for that we need to use the asynchronous <code>getCurrentSound<\/code> function, whose callback returns the sound object. Then, we access this object\u2019s <code>title<\/code> key and store it in a variable. Now we have all the static variables defined, and we can create our four listeners.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ Fire a dataLayer event when Events.PLAY is dispatched\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>widget.bind(SC.Widget.Events.PLAY, <span style=\"color:#00a\">function<\/span>() { \n  act = <span style=\"color:#a50\">'Play'<\/span>; \n  sendDl(cat, act, lab); \n});\n<\/code><\/pre>\n<\/div>\n<p>The first listener is bound to the <code>SC.Widget.Events.PLAY<\/code> event, which, surprisingly, is dispatched when a \u201cPlay\u201d event is recorded in the widget. Once that happens, we set the <code>act<\/code> variable to \u201cPlay\u201d, and invoke the <code>sendDl<\/code> (see below) method, which does the <code>dataLayer.push()<\/code>. Parameters are the <code>cat<\/code> (\u201cSoundCloud\u201d), <code>act<\/code> (\u201cPlay\u201d), and <code>lab<\/code> (Sound title) variables.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ Fire a dataLayer event when Events.PAUSE is dispatched\n<\/span><span style=\"color:#aaa;font-style:italic\">\/\/ The only exception is when the sound ends, and the auto-pause is not reported\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>widget.bind(SC.Widget.Events.PAUSE, <span style=\"color:#00a\">function<\/span>(obj) { \n  pos = <span style=\"color:#0aa\">Math<\/span>.round(obj[<span style=\"color:#a50\">'relativePosition'<\/span>] * <span style=\"color:#099\">100<\/span>);\n  <span style=\"color:#00a\">if<\/span> (pos !== <span style=\"color:#099\">100<\/span>) {\n    act = <span style=\"color:#a50\">'Pause'<\/span>; \n    sendDl(cat, act, lab); \n  }\n});\n<\/code><\/pre>\n<\/div>\n<p>The next event we\u2019ll bind is <code>SC.Widget.Events.PAUSE<\/code>, which is dispatched when a \u201cPause\u201d event is recorded in the widget. It\u2019s practically the same as the \u201cPlay\u201d event, but we need to add one extra check there. SoundCloud auto-pauses the sound when it\u2019s completed, so GA would receive a number of \u201cPause\u201d events that were not initiated by the user. That\u2019s why we have the check on the first line of the callback, where we basically see if the \u201cPause\u201d event occurred when the position of the sound is at 100%. This would indicate that it\u2019s an auto-pause, and we won\u2019t invoke <code>sendDl<\/code> in that case.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ As the play progresses, send events at 25%, 50% and 75%\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>widget.bind(SC.Widget.Events.PLAY_PROGRESS, <span style=\"color:#00a\">function<\/span>(obj) { \n  go = <span style=\"color:#00a\">false<\/span>;\n  pos = <span style=\"color:#0aa\">Math<\/span>.round(obj[<span style=\"color:#a50\">'relativePosition'<\/span>] * <span style=\"color:#099\">100<\/span>);\n  <span style=\"color:#00a\">if<\/span> (pos === <span style=\"color:#099\">25<\/span> &amp;&amp; !q1) {\n    act = <span style=\"color:#a50\">'25%'<\/span>;\n    q1 = <span style=\"color:#00a\">true<\/span>;\n    go = <span style=\"color:#00a\">true<\/span>;\n  }\n  <span style=\"color:#00a\">if<\/span> (pos === <span style=\"color:#099\">50<\/span> &amp;&amp; !q2) {\n    act = <span style=\"color:#a50\">'50%'<\/span>;\n    q2 = <span style=\"color:#00a\">true<\/span>;\n    go = <span style=\"color:#00a\">true<\/span>;\n  }\n  <span style=\"color:#00a\">if<\/span> (pos === <span style=\"color:#099\">75<\/span> &amp;&amp; !q3) {\n    act = <span style=\"color:#a50\">'75%'<\/span>;\n    q3 = <span style=\"color:#00a\">true<\/span>;\n    go = <span style=\"color:#00a\">true<\/span>;\n  }\n  <span style=\"color:#00a\">if<\/span> (go) {\n    sendDl(cat, act, lab);\n  }\n});\n<\/code><\/pre>\n<\/div>\n<p>The next binding is for the <code>SC.Widget.Events.PLAY_PROGRESS<\/code>. This event is dispatched every few milliseconds, and the object it returns has the <em>relative position<\/em> of the sound at the time of the event. This relative position is actually a percentage of how far the user has listened to the track. So, because I\u2019ve chosen to send an event at 25%, 50%, 75% and 100%, I need to check if the relative position is at these milestones. I also use a couple of booleans, <code>q1 q2 q3 go<\/code>, which prevent the same milestone from being sent multiple times. The <code>go<\/code> variable ensures that the GA Event is only fired when the milestones are reached, and not for every single iteration of the PLAY_PROGRESS event.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ When the sound finishes, send an event at 100%\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>widget.bind(SC.Widget.Events.FINISH, <span style=\"color:#00a\">function<\/span>() { \n  act = <span style=\"color:#a50\">'100%'<\/span>; \n  q1 = q2 = q3 = <span style=\"color:#00a\">false<\/span>; \n  sendDl(cat, act, lab); \n});\n<\/code><\/pre>\n<\/div>\n<p>Finally, when the sound finishes, we send the 100% event, and we also reset the utility variables. If we don\u2019t reset them, repeated listenings would not be recorded.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#aaa;font-style:italic\">\/\/ Generic method for pushing the dataLayer event\n<\/span><span style=\"color:#aaa;font-style:italic\">\/\/ Use a Custom Event Trigger with \"scEvent\" as the event name\n<\/span><span style=\"color:#aaa;font-style:italic\">\/\/ Remember to create Data Layer Variables for eventCategory, eventAction, and eventLabel\n<\/span><span style=\"color:#aaa;font-style:italic\"\/><span style=\"color:#00a\">var<\/span> sendDl = <span style=\"color:#00a\">function<\/span>(cat, act, lab) {\n  <span style=\"color:#0aa\">window<\/span>.dataLayer.push({\n    <span style=\"color:#a50\">'event'<\/span> : <span style=\"color:#a50\">'scEvent'<\/span>,\n    <span style=\"color:#a50\">'eventCategory'<\/span> : cat,\n    <span style=\"color:#a50\">'eventAction'<\/span> : act,\n    <span style=\"color:#a50\">'eventLabel'<\/span> : lab\n  });\n};\n<\/code><\/pre>\n<\/div>\n<p>And here\u2019s the <code>sendDl<\/code> method. It just takes the parameters, and pushes them into a <code>dataLayer<\/code> object.<\/p>\n<h2 id=\"the-trigger\">The Trigger<\/h2>\n<p>To activate GTM Tags when a SoundCloud event is registered, create a new Custom Event Trigger that looks like this:<\/p>\n<div style=\"aspect-ratio: 950 \/ 682;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2015\/06\/custom-event.jpg\" title=\"Custom Event Trigger\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"682\" width=\"950\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2015\/06\/custom-event.jpg#ZgotmplZ\" alt=\"Custom Event Trigger\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>It\u2019s a simple one. This Trigger will fire your Tags when the <code>sendDl<\/code> method we built above is invoked.<\/p>\n<h2 id=\"the-data-layer-variables\">The Data Layer Variables<\/h2>\n<p>Next, make sure you have <strong>three<\/strong> Data Layer Variables. One for <code>eventCategory<\/code>, one for <code>eventAction<\/code>, and one for <code>eventLabel<\/code>. They\u2019d look something like this:<\/p>\n<div style=\"aspect-ratio: 2000 \/ 768;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2015\/06\/data-layer-variables.jpg\" title=\"Data Layer Variables for soundcloud\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"768\" width=\"2000\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2015\/06\/data-layer-variables.jpg#ZgotmplZ\" alt=\"Data Layer Variables for soundcloud\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>These are pretty generic, so you might find them useful <a href=\"https:\/\/www.simoahava.com\/analytics\/create-a-generic-event-tag\/\">elsewhere<\/a> as well.<\/p>\n<h2 id=\"the-event-tag\">The Event Tag<\/h2>\n<p>Finally, you need an Event Tag to carry this information to Google Analytics. Set it up as you would any other Event Tag, and make sure it fires with the Trigger you created before (<strong>Event &#8211; scEvent<\/strong>). Then, add the three Variables you created to their respective fields:<\/p>\n<div style=\"aspect-ratio: 642 \/ 704;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2015\/06\/soundcloud-event-tag.jpg\" title=\"SoundCloud Event Tag\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"704\" width=\"642\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2015\/06\/soundcloud-event-tag.jpg#ZgotmplZ\" alt=\"SoundCloud Event Tag\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>And that\u2019s it! Now your site should be ready to collect hits from SoundCloud widgets.<\/p>\n<h2 id=\"summary-and-caveats\">Summary and caveats<\/h2>\n<p>First, some caveats.<\/p>\n<p>Every now and then I noticed an annoying race condition, where the widget had loaded before the GTM Custom HTML Tag had time to complete. This means that the <code>SC.Widget.Events.READY<\/code> was dispatched too early for the listener to catch it. This race condition could be fixed by having a timeout of a second or something, which then does the bindings anyway. I didn\u2019t write it into this solution, but it should be pretty trivial to implement once you understand how the Widget API works.<\/p>\n<p>Some other things I noticed were quota errors from the API. There\u2019s nothing you can do about these, though I believe you can subscribe to some Premium account where these errors don\u2019t crop up. As far as I could tell, however, they had no impact on this tracking solution, and the events were sent nevertheless.<\/p>\n<div style=\"aspect-ratio: 2250 \/ 130;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2015\/06\/post-error.jpg\" title=\"SoundCloud quota error\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"130\" width=\"2250\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2015\/06\/post-error.jpg#ZgotmplZ\" alt=\"SoundCloud quota error\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Anyway, this is a pretty simple solution for tracking SoundCloud widgets on your site. I\u2019ve tried to mirror the excellent <a href=\"http:\/\/www.cardinalpath.com\/youtube-video-tracking-with-gtm-and-ua-a-step-by-step-guide\/\">YouTube tracking guide<\/a> by Cardinal Path.<\/p>\n<p>The <a href=\"https:\/\/developers.soundcloud.com\/docs\/api\/html5-widget\">Widget API<\/a> has a number of other interfaces you can tap into if you want to make the solution even better. Let me know if you encountered any problems, or if you have ideas how to improve this solution!<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>According to their website, SoundCloud is \u201cthe world\u2019s leading social sound platform where anyone can create sounds and share them everywhere\u201d. For artists, it\u2019s a channel for distributing previews of their tracks, and for people like me it\u2019s a nice way to do some API tinkering. To each their own, I guess! I saw a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":146202,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[1531,14481,14495,60148,1791],"dealstore":[],"offerexpiration":[],"class_list":["post-146201","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-google","tag-integration","tag-manager","tag-soundcloud","tag-tag"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Google Tag Manager: SoundCloud Integration - Som2ny Network<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fivemor.com\/?p=146201\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Google Tag Manager: SoundCloud Integration - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"According to their website, SoundCloud is \u201cthe world\u2019s leading social sound platform where anyone can create sounds and share them everywhere\u201d. For artists, it\u2019s a channel for distributing previews of their tracks, and for people like me it\u2019s a nice way to do some API tinkering. To each their own, I guess! I saw a [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=146201\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-03-20T18:44:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1276\" \/>\n\t<meta property=\"og:image:height\" content=\"898\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=146201#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=146201\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Google Tag Manager: SoundCloud Integration\",\"datePublished\":\"2025-03-20T18:44:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=146201\"},\"wordCount\":1257,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=146201#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg\",\"keywords\":[\"Google\",\"Integration\",\"Manager\",\"Soundcloud\",\"TAG\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=146201#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=146201\",\"url\":\"https:\/\/fivemor.com\/?p=146201\",\"name\":\"Google Tag Manager: SoundCloud Integration - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=146201#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=146201#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg\",\"datePublished\":\"2025-03-20T18:44:07+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=146201#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=146201\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=146201#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg\",\"width\":1276,\"height\":898},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=146201#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Google Tag Manager: SoundCloud Integration\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fivemor.com\/#website\",\"url\":\"https:\/\/fivemor.com\/\",\"name\":\"Som2ny Network\",\"description\":\"Daily Deals\",\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fivemor.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/fivemor.com\/#organization\",\"name\":\"Som2ny Network\",\"url\":\"https:\/\/fivemor.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"width\":300,\"height\":86,\"caption\":\"Som2ny Network\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"caption\":\"admin\"},\"sameAs\":[\"https:\/\/fivemor.com\"],\"url\":\"https:\/\/fivemor.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Google Tag Manager: SoundCloud Integration - Som2ny Network","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fivemor.com\/?p=146201","og_locale":"en_US","og_type":"article","og_title":"Google Tag Manager: SoundCloud Integration - Som2ny Network","og_description":"According to their website, SoundCloud is \u201cthe world\u2019s leading social sound platform where anyone can create sounds and share them everywhere\u201d. For artists, it\u2019s a channel for distributing previews of their tracks, and for people like me it\u2019s a nice way to do some API tinkering. To each their own, I guess! I saw a [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=146201","og_site_name":"Som2ny Network","article_published_time":"2025-03-20T18:44:07+00:00","og_image":[{"width":1276,"height":898,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=146201#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=146201"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Google Tag Manager: SoundCloud Integration","datePublished":"2025-03-20T18:44:07+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=146201"},"wordCount":1257,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=146201#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg","keywords":["Google","Integration","Manager","Soundcloud","TAG"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=146201#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=146201","url":"https:\/\/fivemor.com\/?p=146201","name":"Google Tag Manager: SoundCloud Integration - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=146201#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=146201#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg","datePublished":"2025-03-20T18:44:07+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=146201#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=146201"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=146201#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/gtm-soundcloud.jpg","width":1276,"height":898},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=146201#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Google Tag Manager: SoundCloud Integration"}]},{"@type":"WebSite","@id":"https:\/\/fivemor.com\/#website","url":"https:\/\/fivemor.com\/","name":"Som2ny Network","description":"Daily Deals","publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fivemor.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/fivemor.com\/#organization","name":"Som2ny Network","url":"https:\/\/fivemor.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","width":300,"height":86,"caption":"Som2ny Network"},"image":{"@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371","name":"admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","caption":"admin"},"sameAs":["https:\/\/fivemor.com"],"url":"https:\/\/fivemor.com\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/146201","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=146201"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/146201\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/146202"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=146201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=146201"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=146201"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=146201"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=146201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}