Aug 19, 2010

Read Google Analytics Cookie Script

I’ve taken some time out to write a script that provides a nice API to access Google cookies. If you’ve seen the Google cookies before, they can look pretty cryptic and will require you to memorise the syntax of how the cookies are formed which you don’t necessarily want to do to save brain space.

 I won’t really go into the intricate details of Google cookies so this post will assume you know what you’re looking for. I may write up a post to explain more in-depth how Google cookies work later on. In the mean time, you can watch this presentation by Google on cookies (it’s pretty good!) or read the documentation to find out more about Google cookies.

So how is this useful? Well it really depends. You may use it to read GA campaign values and integrate it with your CRM system to track where your leads/sales are coming from or write custom scripts that integrate with GA (i.e. custom variables). It’s really up to you!

Anyway, on to the script.

Below you will find the source code of the script I’ve written. To copy it, simply double click on the source code area (which will highlight the code) and simply do a ctrl-c to copy.

/**
 *	@author:	Danny Ng (http://www.dannytalk.com/2010/08/19/read-google-an…-cookie-script/)
 *	@modified:	19/08/10
 *	@notes:		Free to use and distribute without altering this comment. Would appreciate a link back :)
 */

// Strip leading and trailing white-space
String.prototype.trim = function() { return this.replace(/^\s*|\s*$/g, ''); }

// Check if string is empty
String.prototype.empty = function() {
	if (this.length == 0)
		return true;
	else if (this.length > 0)
		return /^\s*$/.test(this);
}

// Breaks cookie into an object of keypair cookie values
function crumbleCookie(c)
{
	var cookie_array = document.cookie.split(';');
	var keyvaluepair = {};
	for (var cookie = 0; cookie < cookie_array.length; cookie++)
	{
		var key = cookie_array[cookie].substring(0, cookie_array[cookie].indexOf('=')).trim();
		var value = cookie_array[cookie].substring(cookie_array[cookie].indexOf('=')+1, cookie_array[cookie].length).trim();
		keyvaluepair[key] = value;
	}

	if (c)
		return keyvaluepair[c] ? keyvaluepair[c] : null;

	return keyvaluepair;
}

/**
 *	For GA cookie explanation, see http://services.google.com/analytics/breeze/en/ga_cookies/index.html.
 *
 *	@return				-	<void>
 *
 *	@pre-condition		-	pageTracker initialised properly
 *	@post-condition		-	provides 'get' methods to access specific values in the Google Analytics cookies
 */
function gaCookies()
{
	// Cookie syntax: domain-hash.unique-id.ftime.ltime.stime.session-counter
	var utma = function() {
		var utma_array;

		if (crumbleCookie('__utma'))
			utma_array =  crumbleCookie('__utma').split('.');
		else
			return null;

		var domainhash = utma_array[0];
		var uniqueid = utma_array[1];
		var ftime = utma_array[2];
		var ltime = utma_array[3];
		var stime = utma_array[4];
		var sessions = utma_array[5];

		return {
			'cookie': utma_array,
			'domainhash': domainhash,
			'uniqueid': uniqueid,
			'ftime': ftime,
			'ltime': ltime,
			'stime': stime,
			'sessions': sessions
		};
	};

	// Cookie syntax: domain-hash.gif-requests.10.stime
	var utmb = function() {
		var utmb_array;

		if (crumbleCookie('__utmb'))
			utmb_array = crumbleCookie('__utmb').split('.');
		else
			return null;
		var gifrequest = utmb_array[1];

		return {
			'cookie': utmb_array,
			'gifrequest': gifrequest
		};
	};

	// Cookie syntax: domain-hash.value
	var utmv = function() {
		var utmv_array;

		if (crumbleCookie('__utmv'))
			utmv_array = crumbleCookie('__utmv').split('.');
		else
			return null;

		var value = utmv_array[1];

		return {
			'cookie': utmv_array,
			'value': value
		};
	};

	// Cookie syntax: domain-hash.ftime.?.?.utmcsr=X|utmccn=X|utmcmd=X|utmctr=X
	var utmz = function() {
		var utmz_array, source, medium, name, term, content, gclid;

		if (crumbleCookie('__utmz'))
			utmz_array = crumbleCookie('__utmz').split('.');
		else
			return null;

		var utms = utmz_array[4].split('|');
		for (var i = 0; i < utms.length; i++) {
			var key = utms[i].substring(0, utms[i].indexOf('='));
			var val = decodeURIComponent(utms[i].substring(utms[i].indexOf('=')+1, utms[i].length));
			val = val.replace(/^\(|\)$/g, '');	// strip () brackets
			switch(key)
			{
				case 'utmcsr':
					source = val;
					break;
				case 'utmcmd':
					medium = val;
					break;
				case 'utmccn':
					name = val;
					break;
				case 'utmctr':
					term = val;
					break;
				case 'utmcct':
					content = val;
					break;
				case 'utmgclid':
					gclid = val;
					break;
			}
		}

		return {
			'cookie': utmz_array,
			'source': source,
			'medium': medium,
			'name': name,
			'term': term,
			'content': content,
			'gclid': gclid
		};
	};

	// Establish public methods

	// utma cookies
	this.getDomainHash = function() { return (utma() && utma().domainhash) ? utma().domainhash : null };
	this.getUniqueId = function() { return (utma() && utma().uniqueid) ? utma().uniqueid : null };

	this.getInitialVisitTime = function() { return (utma() && utma().ftime) ? utma().ftime : null };
	this.getPreviousVisitTime = function() { return (utma() && utma().ltime) ? utma().ltime : null };
	this.getCurrentVisitTime = function() { return (utma() && utma().stime) ? utma().stime : null };
	this.getSessionCounter = function() { return (utma() && utma().sessions) ? utma().sessions : null };

	// utmb cookies
	this.getGifRequests = function() { return (utmb() && utmb().gifrequest) ? utmb().gifrequest : null }; 

	// utmv cookies
	this.getUserDefinedValue = function () { return (utmv() && utmv().value) ? decodeURIComponent(utmv().value) : null };

	// utmz cookies
	this.getCampaignSource = function () { return (utmz() && utmz().source) ? utmz().source : null };
	this.getCampaignMedium = function () { return (utmz() && utmz().medium) ? utmz().medium : null };
	this.getCampaignName = function () { return (utmz() && utmz().name) ? utmz().name : null };
	this.getCampaignTerm = function () { return (utmz() && utmz().term) ? utmz().term : null};
	this.getCampaignContent = function () { return (utmz() && utmz().content) ? utmz().content : null };
	this.getGclid = function () { return (utmz() && utmz().gclid) ? utmz().gclid : null };
}

API reference:

  • getDomainHash() – returns the domain hash that GA uses to uniquely identify each host name.
  • getUniqueId() – returns a unique id set by GA.
  • getInitialVisitTime() – returns timestamp (seconds since 1 June, 1970 – otherwise known as ctime) of your first visit.
  • getPreviousVisitTime() – returns timestamp of your last visit.
  • getCurrentVisitTime() – returns timestamp of your current session.
  • getSessionCounter() – returns the number of sessions you’ve had on the site.
  • getGifRequests() – returns the number of times a GIF request is sent. This is how GA communicates with the Google servers.
  • getCampaignSource() – returns the campaign source.
  • getCampaignMedium() – returns the campaign medium.
  • getCampaignName() – returns the campaign name.
  • getCampaignTerm() – returns the campaign term (or keyword).
  • getCampaignContent() – returns the campaign content (which can be set by using &utm_content when doing custom tagging).
  • getGclid() – returns the gclid value (if you run Adwords and have auto-tagging enabled).

Usage:

Copy and paste the source code into a javascript file and upload it to your server. Import the script into your code by doing <script type=”text/javascript” src=”PATH YOU UPLOADED TO” />.

After that, all you have to do is make an instance of gaCookies class to start accessing it’s public methods.

Note: Make sure you create the instance after pageTracker or _gaq (async script) has been initialised because you can only access the cookies once the cookies has been set by GA.

<script type="text/javascript">
	var gac = new gaCookies();
	alert(gac.getCampaignSource());
</script>

Known Issues:

Currently one of my helper methods, crumbleCookie() isn’t able to accommodate cookies with the same name since it is using an associative array to store the cookie name & value. For example, if there are 2 utma cookies, you will only get access to the latest one (as the later one overrides the former one).

This can happen with GA when cookies (multiple sets of GA cookies) are set to the root domain, sub-domain or even when you use the methods _setDomainName(‘none’) or _setAllowHash(false), doing stuff like cross-domain tracking.

Google uses an internal hashing method that produces an unique hash to identify the domain the cookie is set to, hence they’re able to distinguish which is which. Unfortunately I can’t be bothered to try and decrypt their cryptic javascript file so that I can use their hashing method internally, and for the time being I can’t think of an elegant solution to overcome this problem. One of the major problems with the javascript cookie object is that there is no way to actually read the domain it has been set to – annoying!

Well, that’s all from me. If this has been helpful to you, do let me know!

Tags: , , ,

15 Comments

  • You’re a genius and have saved my day ;)

    Thanks

  • UPDATE: :))

    I love this script so much!

    In regards to Universal Analytics, I found a FIX: (use if you have universal analytics)

    Include the old ga.js code, but don’t include your property number, keep it like this: ‘UA-XXXXX-X’. This will make sure that the code WORKS, without sending information to your GA account. Which means, your google analytics will get info from the universal analytics, but you still create classic analytics cookies, that you can read thanks to Danny’s marevelous code.

    ATTENTION (FIX NEEDED):

    If you’re going to use this code, make sure you include the fix that Ryan Platte, have mentionned: Replace

    utmz_array = crumbleCookie(‘__utmz’).split(‘.’);

    with
    {
    var rawSplit = srcValue.split(‘.’);
    utmz_array = rawSplit.slice(0,4).concat(rawSplit.slice(4).join(‘.’));
    }

    This makes sure the code doesn’t break when the traffic source has “.”, like facebook.com ; yahoo.co.uk; etc…

    Other than that, this is an amazing work. And it STILL gonna work, until google discontinues classic analytics, which can be at least another two years!

    Enjoy!

  • Hello,

    Thanks very much for sharing this code to access analytics cookies data. I tried your script with the new google analytics universal tracking code and it does not work. Do you mind to share a new version that could be working for the new analytics universal tracking ?

    Thanks.
    -Fadi

  • […] Skrypt do czytania ciasteczek – przyda się Wam np. do połączenia Waszego Google Analytics z systemem CRM. […]

  • Source values like “facebook.com” were breaking this script. Had to replace this line:

    utmz_array = crumbleCookie(‘__utmz’).split(‘.’);

    with

    var rawSplit = srcValue.split(‘.’);
    utmz_array = rawSplit.slice(0,4).concat(rawSplit.slice(4).join(‘.’));

  • […] Jeżeli chcecie zastosować powyższy skrypt u siebie, to oprócz powyższego kodu musicie pamiętać o funkcji, która będzie wyciągała odpowiednie informacje z ciasteczek. Więcej na ten temat w artykule pod tytułem Read Google Analytics Cookie Script. […]

  • Hi Danny, Many thanks for your reply, I’ve just got it working and your script will really help with my call tracking product.

    I used the original google script then changed it to this below. I do not know why the Google supplied script did not work.

    var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
    document.write(unescape(“%3Cscript src='” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));

    try {
    var pageTracker = _gat._getTracker(“UA-xxxxxx-6″);
    pageTracker._trackPageview();
    } catch(err) {}

    The google supplied script which needed a page refresh

    var _gaq = _gaq || [];
    _gaq.push([‘_setAccount’, ‘UA-xxxxx-1′]);
    _gaq.push([‘_trackPageview’]);

    (function() {
    var ga = document.createElement(‘script’); ga.type = ‘text/javascript'; ga.async = true;
    ga.src = (‘https:’ == document.location.protocol ? ‘https://ssl’ : ‘http://www’) + ‘.google-analytics.com/ga.js';
    var s = document.getElementsByTagName(‘script’)[0]; s.parentNode.insertBefore(ga, s);
    })();

    • Hi Peter,

      I’m glad this is working great for you :)

      My guess is the reason why I think the async code isn’t working for you is probably because the _gaq array is only initialised once the page has been loaded (the whole point of async). This means that during the page render, your call to the GA cookies didn’t work because GA is waiting until the page has loaded before initialising (cookies get set then).

      That’s probably why pageTracker works for you since it gets fired as it gets called (non-async). Most likely if you want to integrate with async code, you’ll need to write a wrapper function and use _gaq’s push for function objects (read http://code.google.com/apis/analytics/docs/gaJS/gaJSApi_gaq.html#_gaq.push).

      Hope this clears things up.

  • Hello, Can you please advise me how to get the cookie value getCampaignSource when the cookie is set for the first time, without a page refresh. Thanks in advance peter

    • Hi Peter,

      The function getCampaignSource() should return the campaign source if you’ve called it after the pageTracker initialisation. This is because this script relies on the campaign cookies to be created and written first.

      By then, the cookies would’ve already been established by GA so you don’t need a page refresh first.

  • […] solution with KISSinsights and Google Analytics involves using Danny Ng’s work to return a GA session ID into KISSinsights along side a visitor’s response. The responses in […]

  • Wrong… I would have to check, too, if it is 2nd pageview, because if not I cannot extract the “new” campaign medium – or am I thinking wrong?

  • Hi danny, thanks for this! :) Having seen this I want to try out something and have a question right away… If I want to write all campaign media into a custom variable (like: referral-google-organic-google-paid …) in order to see the first and previous campaigns of the current campaign I would have to use _getVisitorCustomVar for the corresponding index number, right? But I guess I would have to do the following first: 1) Check if it is a new visit (how?), 2) check if custom variable is set, 3) if not set write a new one – or – if set update. I hope I could explain well and am looking forward to your answer :) Thanks a lot!

  • Slightly off topic but that Google logo with the Cookie Monster is my favourite one!!

Leave a comment

Twitter Updates