by Chris


Google Analytics

GA, GTM & Optimizely – technical details

Following on from our previous post about a technique to integrate GA, GTM and Optimizely, here’s the technical detail for those who are interested.

To start with we’ll explain how we get the experiment information from Optimizely, using the Optimizely JS API.

Try this:

  1. Go to a page that is running an Optimizely experiment.
  2. Open a JavaScript console.
  3. Type in window[‘optimizely’]

Assuming that the Optimizely code is running on the page, there will be a wealth of data available to you in window[‘optimizely’]. From any page on the site, you’ll be able to see information about all of the experiments served by Optimizely.

For tracking purposes, we’re only interested in experiments that the user is included in. Handily, there is an API method that allows us to see which of the experiments is active on the current page:


This will return an array of experiment IDs that are being served on the page. An empty array would indicate that the user is not being tested on this page. If the array is populated, we need to act.

We want to send the name of the experiment and details of the variation shown to the user to GA. Armed with the experiment ID(s) by iterating through the activeExperiments, this data can be extracted using further API calls and then exposed to GTM via a dataLayer.push():

var experiments = window['optimizely'].data.state.activeExperiments;
if (experiments.length > 0) {
  for (var i=0; i

It’s then up to you to decide what to do with this data. We’re firing a non-interaction event, using the following tag, trigger and variables.

To run our JavaScript code, we use a Custom HTML tag firing on the All Pages trigger.

To ensure that our solution works irrespective of how the Optimizely code is included (it’s likely it might not have time to execute fully before our Custom HTML runs), we add some logic to check for the existence of window[‘optimizely’].

If it can’t be found, we try again at 0.1 second intervals until it is found.

If it’s still not found after 10 seconds, we stop trying.

var isOptimizelyReady = null;
setTimeout(function() { clearTimeout(isOptimizelyReady) }, 10000);

// Defer our code until window['optimizely'] becomes available.
if(typeof onOptimizelyReady !== 'function'){
  function onOptimizelyReady(method) {
    if (window['optimizely']) method();
    else isOptimizelyReady = setTimeout(function() { onOptimizelyReady(method) }, 100);

  var experiments = window['optimizely'].data.state.activeExperiments;
  if (experiments.length > 0) {
    for (var i=0; i

This solution has been tested for both A/B and Multivariate test types.

The only caveat with this implementation relates to tests that use redirect variations. This redirection typically happens too quickly for our code to run, and once on the destination page, the experiment will no longer technically be active (an experiment is deemed active if the page URL matches its URL Targeting conditions).

To work around this, adjust the URL Targeting of your experiment such that pages you are redirecting to are included as well:

To prevent the variation from getting stuck in a redirect loop, we also make a small adjustment to the redirect code Optimizely generates so that it only redirects when the destination does not match the current page:

Being brutally honest, we think this solution could be even more perfect (if there is such a thing). We’d prefer not to have to modify the experiment code and URL targeting.

We’d love to see full details regarding the experiment (type – redirect or not, and what experiment treatment the user will be exposed to) in window[‘optimizely’].

As it stands, this is a super useful variable to exploit but with a little more experiment meta-data, it’d be fantastic.

@Optimizely, how about it?


Leave a comment

Your email address will not be published. Required fields are marked *