Looking for the #1 Tag Manager Helper?Go to GTMSPY

Race Condition
Google Analytics Segments on Fire
Google Marketing Platform

October 7, 2018

Google Analytics Segments on Fire

Personalize your website based on Analytics segments - without Optimize 360

Google recently announced new personalization features in its Optimize product. This is especially great as Optimize can directly read your Analytics audiences. That means you can create segments in Analytics, turn them into audiences, and subsequently customize your website content depending on the visitor’s behavioral segment.

As a professional online marketer you may think, that sound’s too good to be true. And it is. Google decided to make this audience-based targeting in Optimize only available to their customers of the 360 marketing suite.

As a friendly reminder, this suite will set you back more than $100k a year.

Where’s the free buffet?

Don’t worry, today I’ll show you how we can achieve similar results for free — while remaining in the ecosystem of the Google Marketing Platform. We will use Analytics, the Tag Manager, a Firebase realtime database, and Apps Script.

With some scripting we’ll achieve the following →

  1. We query the Analytics API to get all the custom segments a user (based on her client id) belongs to.
  2. We write this information into the Firebase realtime database.
  3. We use the Tag Manager API to automatically create variables in our container (type: Custom JavaScript) for each of our custom segments we have in Analytics.
  4. We add a tag to the Tag Manager that calls the Firebase realtime database to get a (returning) user’s Analytics segments.
  5. When a user visits our site and resolves her Analytics segments via our Tag Manager tag, our automatically created variables will either return yes or no, depending on the respective segment membership of that very user.
  6. It’s then possible to use these variables as a condition in other tags with which you might customize your website’s content, for example.

In case it’s not yet clear, I will give a practical example for what we can actually achieve that way. Let’s say you’re running an e-commerce site and you defined three accurate segments in Analytics which describe distinct user groups. Maybe you work with enhanced e-commerce tracking or during your analyses you have found other reasonable ways to group your users. You decide that these user groups deserve vouchers, but of different value. Group 1 gets a $5 voucher, group 2 a $10 voucher, and group 3 a $15voucher. With the help of what follows you will be able to exactly play these different vouchers for your returning users without a big hassle.

To follow the next steps I assume your setup fulfills one prerequisite. You need to track your visitors’ Client Ids in an Analytics Custom Dimension. If you’re not yet doing so, please watch Julian’s video or read Simo’s instructions to get this tracking running. Make sure that the Client Id is also available as variable in Tag Manager!

Getting started

Log-in to Firebase and create a free realtime database. Obtain the auth secret (a screenshot where to find it can be found in my article about AdWords and Slack)

You should configure the auth rules in a way that full read access is only allowed with auth while item-specific reading is generally allowed. This allows your users to grab their segments, yet evil minds cannot figure out your number of users in Analytics which translates to website traffic.

    {
      “rules”: {
        “.read”: “auth != null”,
        “.write”: “auth != null”,
        “$id”: {
          “.read”: true,
          “.write”: “auth != null”
        }
      }
    }

Make users query our database when visiting our site. Embed this query in one of your tags or create a new Custom HTML tag with the following content which will fire an Ajax request to our database in vanilla JavaScript:

<script>
  (function(){
    var xmlhttp = new XMLHttpRequest();
    var db = "https://your-database.firebaseio.com/"+{{Client Id}}.replace(/\./g,"_")+".json";

    xmlhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        window.gaIds = JSON.parse(this.responseText);
      }
    };
    xmlhttp.open("GET", db, true);
    xmlhttp.send();

  })();
</script>

For the sake of simplicity we write the user’s segments into the global window object. Of course, you can find a better solution for that. Make sure to replace the database url with a valid one and change the Client Id Tag Manager variable according to your naming.

Ideally, the tag should only fire once per session, so the tag content above could be extended to rather work with cookies, for example.

If you want to provide voucher values in tag manager for different Analytics segments and you don’t want to edit the code I provide, rename your segments to end on a V and then the respective (voucher) value, so for example a $10 segment ultimately isYour Segment V10

Create a new Google Apps Script project and follow Resources > Advanced Google services to activate the Analytics API, the Analytics Reporting API, and the Tag Manager API.

Also, you want to add the Firebase library to your scripts project by using the Resources > Libraries.. menu. The needed project key can be found here.

Into the script’s Code.gs file, insert the following code. Make sure to edit the config data for Analytics, Tag Manager, and Firebase.

/**
 * Analytics Segment Provision in Tag Manager
 * @author: Dustin Recko
 * 
*/

var TAG_MANAGER_CONFIG = {
  accountId: '',
  containerId: '',
  workspaceId: ''
};

var ANALYTICS_CONFIG = {
  profileId: "", // Your Analytics view id, e.g. "ga:137778740"
  clientId: "" // The custom dimension where you store the Client Id, e.g. "ga:dimension4" 
};

var FIREBASE_CONFIG = {
  url: "", // Your Database URL
  auth: "" // Your Database Secret
};

function main() {
  
  init();
  var db = FirebaseApp.getDatabaseByUrl(FIREBASE_CONFIG.url,FIREBASE_CONFIG.auth);
  
  var i = new UserSegments();
  i.segments = i.uaGetSegments();
  i.tmPatchVariables();
  
  for(var y = 0, segments = Object.keys(i.segments), len = segments.length; y<len; y++) {
    i.uaGetUsers(segments[y]);
  }
  db.setData("",{});
  db.setData("", i.users);
  
}

function UserSegments() {
  
  this.parent = "accounts/"+TAG_MANAGER_CONFIG.accountId+"/containers/"+TAG_MANAGER_CONFIG.containerId+"/workspaces/"+TAG_MANAGER_CONFIG.workspaceId;
  this.segments = [];
  this.users = {};
  
  // Analytics Functions
  
  this.uaGetSegments = function() {
    var list = {};
    var segments = Analytics.Management.Segments.list();
    for (var i = 0, segment; segment = segments.items[i]; i++) {
      if(segment.created) // = Custom Segment
        list[segment.segmentId] = segment.name
    }
    return list;
  }
  
  this.uaGetUsers = function(segmentId) {
    var startDate = new Date().yyyymmdd(-90,'-');
    var endDate = new Date().yyyymmdd(0,'-');
    
    var tableId = ANALYTICS_CONFIG.profileId;
    var metric = 'ga:sessions';
    var options = {
      'dimensions': ANALYTICS_CONFIG.clientId,
      'segment': segmentId,
      'max-results': 10000
    };
    
    var report = Analytics.Data.Ga.get(tableId, startDate, endDate, metric, options);
    if (report.rows) {
      for(var i = 0, len = report.rows.length; i<len; i++) {
        var clientId = report.rows[i][0].replace(/\./g,'_');
        if(this.users[clientId]) {
          this.users[clientId].push(segmentId);
        } else {
          this.users[clientId] = [segmentId];
        }
      }
    }
    
  }
  
  // Tag Manager Functions
  
  this.tmGetVariables = function() {
    var arr = TagManager.Accounts.Containers.Workspaces.Variables.list(this.parent).variable;
    var obj = {};
    for(var i=0, len = arr.length;i<arr.length;i++) {
      obj[arr[i].name] = arr[i].variableId;
    }
    return obj;
  }
  
  this.tmPatchVariables = function() {
    var existingVariables = this.tmGetVariables();
    
    for (var segmentId in this.segments) {
      var value = this.segments[segmentId].replace(/^.*\sV([0-9]+)$/g,'$1');
      value = (value.match(/^[0-9]+$/g)) ? parseInt(value) : 0;
      var name = "User Segment - "+this.segments[segmentId].replace(/[^0-9a-z\s]/gi,'').replace(/\sV[0-9]+$/g,'')
      var variableResource = {
        "name": name,
        "type": "jsm",
        "parameter": [{
          "type": "template",
          "key": "javascript",
          "value": "function() {\n if(window.gaIds === undefined) return 'n|0'; return (window.gaIds.indexOf(\""+segmentId+"\") > -1 ? 'y' : 'n')+'|"+value+"';\n}"
        }]
      };

     if(Object.keys(existingVariables).indexOf(name) > -1) {
        TagManager.Accounts.Containers.Workspaces.Variables.update(variableResource, this.parent+'/variables/'+existingVariables[name]);
      } else
        TagManager.Accounts.Containers.Workspaces.Variables.create(variableResource, this.parent);
    }
  }
  
}

function init() {
  Date.prototype.yyyymmdd = function(days,separator) {
    separator = separator || '';
    if(days) {
      this.setDate(this.getDate() + days);
    }
    return Utilities.formatDate(this, Session.getScriptTimeZone(), ['yyyy','MM','dd'].join(separator));
  }
}

Add a trigger to the main function for regular execution, for example once per day during the night.

What happens now is not only that your Firebase database is being filled with a lot of information. Moreover, for each of your custom Analytics segments you will find new variables in your Tag Manager container. These variables’ names have “User Segment -” as a prefix, followed by your name in Analytics. So something like “User Segment - High Value Prospect V15” if you also want to provide a value. These variables are Custom JavaScript and will look like this:

    function() {
     if(window.gaIds === undefined) return 'n|0'; return (window.gaIds.indexOf(“gaid::_QspZKaOQZ-z_icvCq2Dkw”) > -1 ? ‘y’ : ‘n’)+’|0';
    }

The string “gaid::_QspZKaOQZ-z_icvCq2Dkw” represents the ID of the respective Analytics segment and will be different for each variable. Also, the ‘|0’ string is segment-dependent. All this happens in the Google Apps Script you added.

The only thing you must know here is that these variables return a string which contains two pieces of information that are separated by a pipe.

The first part is always y or n, depending on the current user is member of the respective segment or not. The second part is a numeric value which is extracted from your segment naming. If no value is found 0 is returned.

So, such a variable can look like ‘n|0’ if the user doesn’t belong to this segment or ‘y|15’ if the user does belong to the segment and the value is 15.

Congrats! If you made it through all these (not too detailed) instructions you have a great starting point for website customization via Tag Manager, based on Analytics segments.

At this point, you’re on your own. You can use the provided variables in Tag Manager as a condition to fire other tags that modify your website’s content (i.e., only show this and that if a user belongs to this or that user segment). Plus, you could extract the value after the pipe if you have different vouchers that you want to play.

If you find this interesting but you’re stuck at this point, now knowing how to customize your website or how to play different vouchers, maybe this video by Julian will be a good next step for you.

I wish you a lot of fun when making Analytics actionable.

Limitations

Of course, this solution cannot fully replace the Optimize 360 capabilities. We can only work with user segments, not audiences. In Analytics, these user segments are limited to only consider the previous 90 days’ sessions. Plus, the manual load of user data to Firebase implies some inaccuracy between the real Analytics data and your database, depending on how frequently you can let the script run to update your database.

Want to learn more about utilizing Analytics Segments in Optimize in German? I can highly recommend this awesome guide by my colleague Michaela Linhart!