{"id":126119,"date":"2025-03-11T10:50:14","date_gmt":"2025-03-11T10:50:14","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/introducing-ga-spy-for-google-analytics\/"},"modified":"2025-03-11T10:50:14","modified_gmt":"2025-03-11T10:50:14","slug":"introducing-ga-spy-for-google-analytics","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=126119","title":{"rendered":"Introducing GA Spy For Google Analytics"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<div style=\"aspect-ratio: 400 \/ 400;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2016\/09\/stephen-harris.jpg\" title=\"Stephen Harris (c) Seer Interactive\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"400\" width=\"400\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2016\/09\/stephen-harris.jpg#ZgotmplZ\" alt=\"Stephen Harris (c) Seer Interactive\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p><em>This is a guest post by <a href=\"http:\/\/www.seerinteractive.com\/about\/team\/stephen-harris\/?utm_source=simoahava.com&amp;utm_medium=referral&amp;utm_campaign=ana-tech\">Stephen Harris<\/a> from <a href=\"http:\/\/www.seerinteractive.com\/?utm_source=simoahava.com&amp;utm_medium=referral&amp;utm_campaign=ana-tech\">Seer Interactive<\/a><br \/>\n. He was kind enough to share his awesome solution in this blog, so I\u2019m very grateful indeed for his contribution.<\/em><\/p>\n<p>If <a href=\"https:\/\/tagmanager.google.com\/\">Google Tag Manager<\/a> is loaded as the primary instrument for tracking on a webpage (as it should be), then all webpage tracking could and should be configurable via GTM. But we don\u2019t always control the circumstances, and it\u2019s not uncommon to face hardcoded <a href=\"https:\/\/analytics.google.com\">Google Analytics<\/a> tracking outside of GTM.<\/p>\n<p>Perhaps GA tracking cannot be removed from the website source code quickly enough. Maybe the website is on a publishing schedule that doesn\u2019t suit measurement needs, or maybe the folks responsible for the website don\u2019t have sufficient resources. Whatever the reason, you got a \u201cno\u201d when you <del>requested<\/del> insisted on removal. (You <em>did<\/em> insist on removal, didn\u2019t you?)<\/p>\n<p>Or what about when there are hardcoded GA commands in a platform or plugin? It could be screwing with your data. But chances are it\u2019s not doing anything at all (because it\u2019s tracking to the nonexistent default GA tracker) and there seems to be no way to get it to work through GTM.<\/p>\n<p>And even when all tracking runs through Google Tag Manager, some things can be difficult (or impossible) to implement using the tag templates (such as adding <a href=\"https:\/\/www.simoahava.com\/analytics\/universal-analytics-plugins-explained\/\">GA plugins<\/a>, or defining Universal Analytics tracker field defaults).<\/p>\n<p>Here\u2019s a solution.<\/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=\"1-spying-on-ga\">1. Spying On GA<\/h2>\n<p>You can grab the code <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/blob\/master\/ga-spy.js\">on GitHub<\/a>. Copy-paste it either into a Custom HTML Tag in Google Tag Manager, or load it in the page template proper.<\/p>\n<p>It\u2019s called <strong>GA Spy<\/strong> because it can silently hijack and control all tracking that relies on Google\u2019s Universal Analytics library (<strong>analytics.js<\/strong>). Effectively, this is a Google Analytics listener that does karate.<\/p>\n<div style=\"aspect-ratio: 520 \/ 434;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy_diagram.jpg\" title=\"GA Spy Operation\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"434\" width=\"520\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy_diagram.jpg#ZgotmplZ\" alt=\"GA Spy Operation\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>To put this in layman\u2019s terms, the script listens for interactions with the <code>ga()<\/code> interface, returning the arguments passed to the function for you to process for whatever purpose you want. For example, you can automatically copy all calls to <code>ga('send'...)<\/code> to <code>dataLayer.push()<\/code> syntax, allowing you to fluently replicate hardcoded Universal Analytics tracking in Google Tag Manager.<\/p>\n<p>This blog post is written assuming the hardcoded tracking is for Universal Analytics, but there is a version of the <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/blob\/master\/ga-spy-async.js\">script for the async GA library<\/a> (ga.js), to which the same core concepts apply.<\/p>\n<h3 id=\"11-spy-code-101\">1.1. Spy Code 101<\/h3>\n<p>Let\u2019s walk through the script logic. Before implementing, it\u2019s important to get a basic idea of what this script actually does. Once you understand the basics, you\u2019ll also understand why <strong>this should be considered a temporary solution<\/strong>. Indeed, you should document the use of this solution, so that when the hardcoded Universal Analytics code is eventually removed from the site, you won\u2019t be left hanging dry with methods that don\u2019t really do anything.<\/p>\n<p>This section also explains this script\u2019s biggest caveat: <strong>GA Spy<\/strong> is not guaranteed to intercept (or even detect) tracking that fires while the page is loading (e.g. the standard GA snippet) if the hack is deployed via GTM. Capturing these requests requires that you deploy the script on the page template itself, which unfortunately might detract from the usefulness for those without developer resources handy.<\/p>\n<p>For a more technical breakdown, check out the <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/blob\/master\/README.md\">README<\/a>.<\/p>\n<h3 id=\"12-a-good-spy-starts-with-research\">1.2. A Good Spy Starts With Research<\/h3>\n<p>Let\u2019s start with an overview of how the Universal Analytics tracker works on the page.<\/p>\n<p>Many GA users are familiar with the JavaScript namespace <code>ga<\/code>. It\u2019s the global object for tracking with the Universal Analytics library (analytics.js). When the browser loads the page, this object is actually processed twice. First, it\u2019s created by the <a href=\"https:\/\/www.thyngster.com\/how-does-the-universal-analytics-snippet-work\/\">Universal Analytics snippet<\/a>, and it <em>queues<\/em> all commands to <code>ga()<\/code> while waiting for the library to load. Then, once the library has loaded, the namespace is converted to a full interface which processes each command as it is executed.<\/p>\n<div style=\"aspect-ratio: 358 \/ 436;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy_diagram-ga-lib.jpg\" title=\"How the GA Library works\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"436\" width=\"358\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy_diagram-ga-lib.jpg#ZgotmplZ\" alt=\"How the GA Library works\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>So the standard Universal Analytics snippet instantiates <code>ga<\/code> as a tiny function that saves all the arguments you pass to it as an Array in <code>ga.q<\/code>. This is called the <em>command queue<\/em>. It\u2019s created by the second line of the GA install snippet function, shown here (beautified with more readable variable names):<\/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:#0aa\">window<\/span>[gaObjName] = <span style=\"color:#0aa\">window<\/span>[gaObjName] || <span style=\"color:#00a\">function<\/span>() {\n  ( <span style=\"color:#0aa\">window<\/span>[gaObjName].q = <span style=\"color:#0aa\">window<\/span>[gaObjName].q || [] ).push( arguments )\n},\nga.l = <span style=\"color:#099\">1<\/span> * <span style=\"color:#00a\">new<\/span> <span style=\"color:#0aa\">Date<\/span>();\n<\/code><\/pre>\n<\/div>\n<p>Aside: Notice any similarities to the <a href=\"https:\/\/www.simoahava.com\/gtm-tips\/datalayer-declaration-vs-push\/\">recommended format<\/a> for data layer pushes?<\/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:#0aa\">window<\/span>.dataLayer = <span style=\"color:#0aa\">window<\/span>.dataLayer || [] ).push( { } )\n<\/code><\/pre>\n<\/div>\n<p>Once this queue is established, the snippet creates a <em>script loader<\/em>, which starts an asynchronous request to download the <strong>analytics.js<\/strong> library from Google\u2019s servers.<\/p>\n<p>Once analytics.js loads, it will run the commands in the command queue and replace the queue function <code>ga<\/code> with much more robust object, which we call the <code>ga<\/code> object or function. Once this happens, <code>ga<\/code> stops keeping track of previously called commands, and will run commands as soon as they are called.<\/p>\n<h3 id=\"13-spy-moves\">1.3. Spy Moves<\/h3>\n<p>The core of this script is the <code>hijack<\/code> function. What it does is simple: it saves a private reference to the global <code>ga<\/code> object, then updates the global reference (<code>window.ga<\/code> or simply <code>ga<\/code>) to point to a custom function, which we\u2019ll call our \u201cproxy\u201d function. Note that if you\u2019ve chosen to rename the global function as something other than <code>ga<\/code>, it will still work nicely with <strong>GA Spy<\/strong>.<\/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\">function<\/span> hijack(){\n  <span style=\"color:#aaa;font-style:italic\">\/\/ The current global GA object (could be GA command queue or loaded GA object).\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">var<\/span> gaOrig = <span style=\"color:#0aa\">window<\/span>.ga;\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Replace global GA object with a proxy.\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#0aa\">window<\/span>.ga = proxy;\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Maintain references to GA's public interface.\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">for<\/span>( k <span style=\"color:#00a\">in<\/span> gaOrig )\n     <span style=\"color:#00a\">if<\/span>( gaOrig.hasOwnProperty( k ) )\n        proxy[k] = gaOrig[k];\n}\n<\/code><\/pre>\n<\/div>\n<p>The <code>proxy<\/code> function mimics the data members and methods of the original <code>ga<\/code> function. Thus it behaves like the original, which we accomplish by passing its arguments back to the <code>ga<\/code> function (and returning the result). But, only if we so choose. We can reject the message, simply by not sending the message to the original <code>ga<\/code> function, and thus intercept and block the hardcoded GA from running its requests altogether.<\/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\">function<\/span> proxy(){\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Grab all arguments as an array.\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">var<\/span> args = [].slice.call( arguments );\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Unless processArgs() returns false ...\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">if<\/span>( processArgs( args ) )\n     <span style=\"color:#aaa;font-style:italic\">\/\/ ... pass through to original GA object.\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>     <span style=\"color:#00a\">return<\/span> gaOrig.apply( gaOrig, args );\n};\n<\/code><\/pre>\n<\/div>\n<p>The <code>processArgs<\/code> function simply delegates the processing and filtering of all commands to you (via the custom listener function).<\/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\">function<\/span> processArgs( a ){\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Call listener, return false only if listener returns false.\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  <span style=\"color:#00a\">return<\/span> listener( a ) !== <span style=\"color:#00a\">false<\/span>;\n}\n<\/code><\/pre>\n<\/div>\n<h3 id=\"14-time-to-spy\">1.4. Time To Spy<\/h3>\n<p>As soon as <strong>GA Spy<\/strong> is run, it will instantiate the command queue if it doesn\u2019t already exist, just as the Universal Analytics snippet would. Both in this script and in the Universal Analytics snippet, we\u2019re careful not to blindly set a new variable and lose any previous data.<\/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\">if<\/span>( ! <span style=\"color:#0aa\">window<\/span>[gaObjName] ){ <span style=\"color:#aaa;font-style:italic\">\/\/ If global object doesn't exist ...\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>  ga = <span style=\"color:#00a\">function<\/span>(){\n    <span style=\"color:#00a\">if<\/span>( ! <span style=\"color:#0aa\">window<\/span>[gaObjName].q )\n      <span style=\"color:#0aa\">window<\/span>[gaObjName].q = [];\n      <span style=\"color:#0aa\">window<\/span>[gaObjName].q.push( arguments );\n  };\n  ga.l = <span style=\"color:#099\">1<\/span> * <span style=\"color:#00a\">new<\/span> <span style=\"color:#0aa\">Date<\/span>();\n  <span style=\"color:#0aa\">window<\/span>[gaObjName] = ga;\n}\n<\/code><\/pre>\n<\/div>\n<p>Note: the above code does the exact same thing as the second line of the GA install snippet function, shown beautified under A Good Spy Starts With Research above.<\/p>\n<p>Then, regardless of whether it already existed or was just initiated, <strong>GA Spy<\/strong> hijacks <code>ga<\/code>.<\/p>\n<p>If <code>ga<\/code> is found to be the fully-formed Universal Analytics tracking method, then analytics.js has already loaded and we will be tapped into all subsequent calls to <code>ga<\/code> until the page is unloaded. Job done. Since the global <code>ga<\/code> object does not keep track of previously run <code>ga()<\/code> commands, we cannot access the information passed in those commands. It\u2019d be useful if we could access the command history, but we still would not be able to block those hits, since JavaScript does not support time reversal (not gonna work: <code>setTimeout( hijack, -5 )<\/code>).<\/p>\n<p>However, if <strong>GA Spy<\/strong> finds the <em>command queue<\/em> instantiated in <code>ga<\/code>, then we know that if\/when analytics.js loads, it will replace the hijacked command queue (our <code>proxy<\/code>) with the actual Universal Analytics tracking method. We can easily hijack it again, but timing is important. We want the global <code>ga<\/code> object to be in existence for as short a period as possible to avoid missing any commands before we manage to hijack it.<\/p>\n<p>Any function in the command queue will be executed as soon as the <a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/analyticsjs\/command-queue-reference#ready-callback\"><code>ga<\/code> object is ready<\/a>. So we add the <code>hijack<\/code> function, thereby setting a trap the <code>ga<\/code> object will trigger immediately upon loading, and put our <code>proxy<\/code> in its place before the <code>ga<\/code> object is even available to other scripts.<\/p>\n<p>The only other thing we need to do is run the stack of existing commands through the listener, and when it returns false just filter that command out of the queue.<\/p>\n<h2 id=\"2-reliability-and-caveats\">2. Reliability And Caveats<\/h2>\n<p>This should be an extremely reliable solution. In fact, the method of listening by \u201chijacking\u201d a function is used by a number of Google libraries, including GTM\u2019s own <a href=\"https:\/\/www.simoahava.com\/analytics\/google-tag-manager-data-model\/\">Data Layer<\/a> and <a href=\"https:\/\/github.com\/googleanalytics\/autotrack\/blob\/911e9f7b7a0d1fe73e00c464c05d57825efbba1b\/lib\/plugins\/page-visibility-tracker.js#L148-L164\">Autotrack<\/a>. Note that it\u2019s possible <strong>GA Spy<\/strong> can be broken by an update to analytics.js, but the <code>ga<\/code> interface <strong>GA Spy<\/strong> relies on is wholly defined in the standard Universal Analytics install snippet, so breakage due to unannounced library updates is highly unlikely.<\/p>\n<p>Nevertheless, there are some important caveats to note before using this.<\/p>\n<h3 id=\"21-uncommon-behavior-for-a-common-library\">2.1. Uncommon Behavior For A Common Library<\/h3>\n<p>Whenever this is used to modify or block a <code>ga<\/code> command, I would consider this a \u201chacky\u201d solution. Although the code is sound, such usage modifies the typical behavior of the global <code>ga()<\/code> function, making it work differently than it works on 99% of other websites. This can impede troubleshooting and confuse those who are new to Google Analytics or JavaScript.<\/p>\n<p><em>Tip: You can tell that you\u2019re dealing with hijacked GA by checking for the presence of the property <code>_gaOrig<\/code> in the global Google Analytics object. By default it would be: <code>window.ga._gaOrig<\/code>.<\/em><\/p>\n<p>In some cases, hijacking the GA function is necessitated by third-party vendors that require their JavaScript to be implemented with a hardcoded GA tracker.<\/p>\n<p>In other cases, such as when completing a migration from hardcoded Google Analytics to Google Tag Manager, this treatment should be acceptable only as a short-term solution.<\/p>\n<h3 id=\"22-cannot-intercept-hardcoded-pageviews-from-ga\">2.2. Cannot Intercept Hardcoded Pageviews From GA<\/h3>\n<p>If <strong>GA Spy<\/strong> runs before analytics.js loads, you will be able to access and block 100% of the commands queued to analytics.js. But if <strong>GA Spy<\/strong> runs after, then it has zero insight into what hits (if any) were previously fired. So if you want to intercept all hardcoded commands, <strong>GA Spy<\/strong> needs to be deployed directly on the page. However, for many purposes, this is not necessary at all.<\/p>\n<p>Unfortunately, in some cases where deploying on-page is necessary, the inability to make on-page changes is the very problem that prompted the need for <strong>GA Spy<\/strong> in the first place, making this solution a catch-22. For GTM migrations, one way to mitigate this is to request to have <strong>GA Spy<\/strong> placed on the page at the same time that GTM is added.<\/p>\n<p>Note, if synchronous loading of Google Tag Manager is ever supported, hardcoding <strong>GA Spy<\/strong> will no longer be required, but still recommended, because loading anything synchronously in the <code\/> will degrade page loading speed.<\/p>\n<h3 id=\"23-incomplete\">2.3. Incomplete<\/h3>\n<p>Currently, <strong>GA Spy<\/strong> only intercepts GA commands that are called upon the global <code>ga<\/code> object, but there are other ways to send commands to analytics.js. Many GA plugins and some custom implementations call commands directly upon the tracker object (i.e. <code>ga.getAll()[0].send('event')<\/code>). Support for intercepting these commands <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/issues\/2\">may be added in the future<\/a>.<\/p>\n<p>Another limitation is that <strong>GA Spy<\/strong> can process only the values passed to GA commands. It doesn\u2019t provide access to the default values for all the fields that are populated by analytics.js.<\/p>\n<p>One effect of these two facts is that <strong>GA Spy<\/strong> will not pick up hit modifications (or extra hits) done by GA plugins (and other implementations using the Tasks API). This is by design and no support for this planned. (If you see a need for this, describe the use case and upvote this enhancement <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/issues\/7\">here<\/a>.)<\/p>\n<h3 id=\"24-more-code\">2.4. More Code!<\/h3>\n<p>As a developer I have many protests against the following statement, but I can\u2019t deny it\u2019s essential truth: <code>code is bad<\/code>. Meaning, no matter how good the code, <a href=\"https:\/\/blog.codinghorror.com\/the-best-code-is-no-code-at-all\/\">no code is better<\/a>. Code leads to bugs which lead to more code, which leads to more bugs. Even bullet-proof code requires some degree of maintenance. Perhaps the biggest issue is that a code-based solution imposes a higher skill barrier on managing and customizing the solution. As a general rule, we should avoid custom code whenever there is a sound alternative.<\/p>\n<h2 id=\"3-usage\">3. Usage<\/h2>\n<h3 id=\"31-placement\">3.1. Placement<\/h3>\n<p>If you need to intercept GA commands that run upon page load or shortly thereafter (such as the tracker creation or <code>'pageview'<\/code> in the base GA install snippet), then you\u2019ll need deploy <strong>GA Spy<\/strong> in an inline script tag or external script (with no <code>async<\/code> or <code>defer<\/code> attributes) above the first GA command (usually in the base GA snippet).<\/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\">html&gt;\n  head&gt;\n  ...\n    \/\/ GA Spy:\n    script&gt;\n      ;<span style=\"color:#0aa\">window<\/span>.gaSpy = <span style=\"color:#0aa\">window<\/span>.gaSpy...\n    <span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;\n\n    \/\/ Universal Analytics snippet:\n    script&gt;\n      (<span style=\"color:#00a\">function<\/span>(i,s,o,g,r,a,m)...\n    <span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;\n  ...\n  <span style=\"color:#1e90ff;font-weight:bold\">head<\/span>&gt;<\/code><\/pre>\n<\/div>\n<p>If the commands you need to intercept are triggered by user action, then this is not an issue and you can deploy <strong>GA Spy<\/strong> using GTM.<\/p>\n<div style=\"aspect-ratio: 1744 \/ 858;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2016\/10\/gaspy-gtm.jpg\" title=\"GA Spy in Custom HTML\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"858\" width=\"1744\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2016\/10\/gaspy-gtm.jpg#ZgotmplZ\" alt=\"GA Spy in Custom HTML\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<h3 id=\"32-your-listener\">3.2. Your Listener<\/h3>\n<p>This script will not <em>do<\/em> anything on its own. You need to implement all actions in your custom <code>listener<\/code> callback function. You can do things like:<\/p>\n<ol>\n<li>\n<p>Access and modify the arguments sent to <code>ga()<\/code>.<\/p>\n<\/li>\n<li>\n<p>Access information about the command scope (tracker name, hit type, etc.)<\/p>\n<\/li>\n<li>\n<p>Block the hit by returning <code>false<\/code> (returning merely \u201cfalsy\u201d values like <code>0<\/code> or <code>undefined<\/code> will not block the hit).<\/p>\n<\/li>\n<li>\n<p>And everything else you can do in JavaScript \ud83d\ude42<\/p>\n<\/li>\n<\/ol>\n<p>In many cases, we\u2019ll want to push the hit values onto the <code>dataLayer<\/code> so they can be accessed in GTM.<\/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\">script&gt;\n  gaSpy(<span style=\"color:#00a\">function<\/span>(obj) {\n    \n    <span style=\"color:#aaa;font-style:italic\">\/\/ Do something with the arguments to ga():\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#00a\">var<\/span> args = obj.args;\n\n    <span style=\"color:#aaa;font-style:italic\">\/\/ Do something with details about the tracker itself:\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>    <span style=\"color:#00a\">var<\/span> details = obj.the;\n\n  });\n<span style=\"color:#1e90ff;font-weight:bold\">script<\/span>&gt;<\/code><\/pre>\n<\/div>\n<p>Naturally, any calls to <code>gaSpy()<\/code> need to be timed so that they take place <em>after<\/em> you\u2019ve loaded the <strong>GA Spy<\/strong> code itself.<\/p>\n<p>See the <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/blob\/master\/README.md#configuration\">README file<\/a> for more details on what data is passed to the callback function, as well as additional <strong>GA Spy<\/strong> configuration options.<\/p>\n<h3 id=\"33-listening-from-gtm\">3.3. Listening From GTM<\/h3>\n<p>If using <strong>GA Spy<\/strong> in GTM, the listener should typically ignore all commands from GTM. Failure to do this could result in blocking GTM hits or in an infinite loop. We can do this by checking the tracker name. GTM uses tracker names starting with \u201cgtm\u201d followed by the timestamp of when the tracker was created.<\/p>\n<p>However, this method will not work when callbacks are passed in place of a GA command. GTM does not appear to use these currently, but we cannot do much with a them anyway, so as a precaution any listener should ignore callbacks too.<\/p>\n<p>Use the this code at the top of your callback function to avoid issues:<\/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\">gaSpy( <span style=\"color:#00a\">function<\/span>( ev ){\n  <span style=\"color:#00a\">if<\/span>( ev.the.callback )\n    <span style=\"color:#00a\">return<\/span>;\n  <span style=\"color:#00a\">if<\/span>( ev.the.trackerName &amp;&amp; ev.the.trackerName.substr( <span style=\"color:#099\">0<\/span>, <span style=\"color:#099\">3<\/span> ) == <span style=\"color:#a50\">'gtm'<\/span> ) \n    <span style=\"color:#00a\">return<\/span>;\n  <span style=\"color:#aaa;font-style:italic\">\/\/ Your code here\n<\/span><span style=\"color:#aaa;font-style:italic\"\/>});\n<\/code><\/pre>\n<\/div>\n<p>This is not foolproof, but it\u2019ll work unless hardcoded tracking is using a tracker name starting with \u201cgtm\u201d (or if the tracker names in GTM are customized, which is probably done as an alternative to this solution). For example, this method will not work for Wistia\u2019s built-in tracking, because Wistia\u2019s code (erroneously) sends hits through every named tracker on the page.<\/p>\n<h2 id=\"4-examples\">4. Examples<\/h2>\n<h3 id=\"41-log-ga-commands\">4.1. Log GA Commands<\/h3>\n<p>Logging GA commands is a useful way to easily see what commands are being picked up by <strong>GA Spy<\/strong>, letting you see which things <strong>GA Spy<\/strong> can block and\/or latch on to in order to execute custom behavior. Compared with browser extensions and the verbose analytics_debug.js, <strong>GA Spy<\/strong> logging can be very minimalistic. You could even log using only emojis if you wanted! And it has the advantage of working even when the hit does not fire (such as when using GA opt-out or tracking blockers).<\/p>\n<p>This listener callback will print <code>ga()<\/code> arguments exactly as they are given:<\/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\">gaSpy( <span style=\"color:#00a\">function<\/span> gaSpy_cb_( ev ){\n    console.debug.apply( console, ev.args )\n});\n<\/code><\/pre>\n<\/div>\n<p>Or check out <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/blob\/master\/examples\/ga-spy-log.js\">one of the examples on GitHub<\/a> for something a bit more robust:<\/p>\n<div style=\"aspect-ratio: 972 \/ 260;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy-log.jpg\" title=\"Console logging GA requests\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"260\" width=\"972\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy-log.jpg#ZgotmplZ\" alt=\"Console logging GA requests\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Since this is all one script, you could turn this into a bookmarklet. This could also block all hits, so it serves as a \u2018test mode\u2019 bookmarklet, which both logs and prevents any hits from being fired.<\/p>\n<h3 id=\"42-access-hardcoded-ga-command-data-via-gtm\">4.2. Access Hardcoded GA Command Data Via GTM<\/h3>\n<p>If you know exactly what you\u2019re looking for, you can simply look for that format and send it to <code>dataLayer<\/code>. Remember to define an <a href=\"https:\/\/www.simoahava.com\/gtm-tips\/add-the-event-key-to-datalayer-pushes\/\">event name<\/a> in the <code>dataLayer.push()<\/code>, so you can create a GTM trigger based on the event. Here\u2019s an example that clocks hardcoded events from the social sharing plugin ShareThis and forwards them to GTM:<\/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\">gaSpy( <span style=\"color:#00a\">function<\/span> gaSpy_cb_( ev ){\n  <span style=\"color:#00a\">var<\/span> a = ev.args;\n  <span style=\"color:#00a\">if<\/span>( a[<span style=\"color:#099\">0<\/span>] == <span style=\"color:#a50\">\"send\"<\/span> \n   &amp;&amp; a[<span style=\"color:#099\">1<\/span>] == <span style=\"color:#a50\">\"event\"<\/span> \n   &amp;&amp; a[<span style=\"color:#099\">2<\/span>] == <span style=\"color:#a50\">\"ShareThis\"<\/span> ){\n    dataLayer.push({\n      <span style=\"color:#a50\">'event'<\/span> : <span style=\"color:#a50\">'spy.ga.socialPlugin'<\/span>,\n      <span style=\"color:#a50\">'spy.ga.socialPlugin'<\/span> : {\n        <span style=\"color:#a50\">'network'<\/span> : a[<span style=\"color:#099\">3<\/span>],\n        <span style=\"color:#a50\">'url'<\/span> : a[<span style=\"color:#099\">4<\/span>]\n      }\n    });\n  }\n  <span style=\"color:#00a\">return<\/span> <span style=\"color:#00a\">false<\/span>;\n});\n<\/code><\/pre>\n<\/div>\n<p><a href=\"https:\/\/github.com\/smhmic\/ga-spy\/blob\/master\/examples\/ga-spy-datalayer.gtm.tag.html\">Here\u2019s a more robust example<\/a> that sends all non-GTM commands to <code>dataLayer<\/code>.<\/p>\n<div style=\"aspect-ratio: 871 \/ 374;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy-datalayer.jpg\" title=\"GA Spy dataLayer\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"374\" width=\"871\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2016\/10\/ga-spy-datalayer.jpg#ZgotmplZ\" alt=\"GA Spy dataLayer\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Most of the code necessary for this simply normalizes the command arguments (since GA commands have a flexible format for defining field values). It also handles data scope (different trackers, plugins) and also wipes hit-only fields after the relevant GTM event.<\/p>\n<h2 id=\"5-next-steps\">5. Next Steps<\/h2>\n<p>Even though this is a reliable and relatively simple method for working with hardcoded GA tracking, hopefully this was not your first choice. Even though this fixes real data issues and helps consolidate your implementation in GTM, it\u2019s an awkward way of doing so. You can get the same result with a much simpler configuration: no hardcoded tracking, and no spying! Don\u2019t be satisfied with getting tracking working; <strong>pursue a clean implementation<\/strong>. Even if it will take a while, plan to have the hardcoded tracking removed.<\/p>\n<p>Your script is making your Universal Analytics code work differently than it would work on virtually every other website, confusing troubleshooters and learners. So, even if you believe hardcoded tracking removal is imminent, be sure to <strong>note your use of GA Spy (prominently) in your tracking documentation<\/strong>.<\/p>\n<p>If you are using <strong>GA Spy<\/strong> to deal with code from a third party platform or plugin, contact the developers. Let them know their script is not compatible with GTM. (Actually, you should do that before implementing <strong>GA Spy<\/strong>; they might be responsive and fix their code quickly!)<\/p>\n<h2 id=\"6-feedback\">6. Feedback<\/h2>\n<p>This method was designed to be flexible and was tested with various setups, but that\u2019s not to say it\u2019s bulletproof. On the contrary, I\u2019m eager to discover bugs, incompatibilities with browser plugins, and similar issues. Naturally, I admit it\u2019d be nicer to find out there are none \ud83d\ude42<\/p>\n<p>Please post any problems or suggestions as a <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/issues\/new\">new issue on GitHub<\/a> if it has not <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/issues\">already been added<\/a>. Be sure to upvote fixes and enhancements you want to see implemented (by clicking the little thumbs-up icon)!<\/p>\n<h2 id=\"7-summary-by-simo\">7. Summary (by Simo)<\/h2>\n<p>This is some top-notch JavaScript right here! What Stephen has built is a swiss-army knife that lets you take full control over how <strong>analytics.js<\/strong> functions as a tracking interface on your website. Even though translating the hardcoded <code>ga()<\/code> calls to <code>dataLayer.push()<\/code> commands is the obvious choice, there are lots of use cases for this library, and you can check some of the examples out <a href=\"https:\/\/gist.github.com\/smhmic\/492213ea74bb47c5878b#file-examples-spygoogleanalytics-js\">here<\/a>.<\/p>\n<p>One of the things this enables is what we\u2019ve been waiting for so long with Google Tag Manager: the ability to intercept and modify the payload sent by GTM\u2019s Tags to Google Analytics. There\u2019s no way to add your own <a href=\"https:\/\/www.simoahava.com\/analytics\/universal-analytics-plugins-explained\/\">custom plugins<\/a>, for example, but this library with its <code>hijack<\/code> function lets you modify the tracker object between its creation and when the data is dispatched. It\u2019s not entirely trivial, but I might just have an article in the pipeline showing some cool uses cases for this particular library, so stay tuned!<\/p>\n<p>In any case, thank you so much to Stephen for sharing this incredible resource with the community. If you have implementation trouble with the library or any other type of feedback to share, please sound off in the comments. And please respect Stephen\u2019s wishes when he requested any actual bugs and issues to be contributed to the <a href=\"https:\/\/github.com\/smhmic\/ga-spy\/issues\">issue tracker<\/a> in GitHub.<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>This is a guest post by Stephen Harris from Seer Interactive . He was kind enough to share his awesome solution in this blog, so I\u2019m very grateful indeed for his contribution. If Google Tag Manager is loaded as the primary instrument for tracking on a webpage (as it should be), then all webpage tracking [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":126120,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[12765,1531,13032,22392],"dealstore":[],"offerexpiration":[],"class_list":["post-126119","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-analytics","tag-google","tag-introducing","tag-spy"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Introducing GA Spy For Google Analytics - 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=126119\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Introducing GA Spy For Google Analytics - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"This is a guest post by Stephen Harris from Seer Interactive . He was kind enough to share his awesome solution in this blog, so I\u2019m very grateful indeed for his contribution. If Google Tag Manager is loaded as the primary instrument for tracking on a webpage (as it should be), then all webpage tracking [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=126119\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-03-11T10:50:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"400\" \/>\n\t<meta property=\"og:image:height\" content=\"400\" \/>\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=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=126119#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=126119\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Introducing GA Spy For Google Analytics\",\"datePublished\":\"2025-03-11T10:50:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=126119\"},\"wordCount\":3098,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=126119#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg\",\"keywords\":[\"Analytics\",\"Google\",\"Introducing\",\"Spy\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=126119#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=126119\",\"url\":\"https:\/\/fivemor.com\/?p=126119\",\"name\":\"Introducing GA Spy For Google Analytics - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=126119#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=126119#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg\",\"datePublished\":\"2025-03-11T10:50:14+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=126119#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=126119\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=126119#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg\",\"width\":400,\"height\":400},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=126119#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Introducing GA Spy For Google Analytics\"}]},{\"@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":"Introducing GA Spy For Google Analytics - 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=126119","og_locale":"en_US","og_type":"article","og_title":"Introducing GA Spy For Google Analytics - Som2ny Network","og_description":"This is a guest post by Stephen Harris from Seer Interactive . He was kind enough to share his awesome solution in this blog, so I\u2019m very grateful indeed for his contribution. If Google Tag Manager is loaded as the primary instrument for tracking on a webpage (as it should be), then all webpage tracking [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=126119","og_site_name":"Som2ny Network","article_published_time":"2025-03-11T10:50:14+00:00","og_image":[{"width":400,"height":400,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=126119#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=126119"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Introducing GA Spy For Google Analytics","datePublished":"2025-03-11T10:50:14+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=126119"},"wordCount":3098,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=126119#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg","keywords":["Analytics","Google","Introducing","Spy"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=126119#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=126119","url":"https:\/\/fivemor.com\/?p=126119","name":"Introducing GA Spy For Google Analytics - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=126119#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=126119#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg","datePublished":"2025-03-11T10:50:14+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=126119#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=126119"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=126119#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/03\/stephen-harris.jpg","width":400,"height":400},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=126119#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Introducing GA Spy For Google Analytics"}]},{"@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\/126119","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=126119"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/126119\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/126120"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=126119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=126119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=126119"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=126119"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=126119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}