How to do First Touch Tracking in Google Analytics

Recently, I have found myself talking quite a lot about tracking whole visitor paths in Google Analytics. Rand and I discussed the issue in a Whiteboard Friday kill the head or chase the tail and I covered how to get past last click attribution in a subsequent post. These have tended to cover the why and the (broad-brush) why. If you’re interested in what first touch, last touch or multi-touch tracking is or why you’d want to do them, the resources above are a good place to start.

The main point can be summed up by the chart below showing the visits to convert report from Google Analytics:

Visits to conversion report from Google Analytics

The point is not what those exact values are, but simply that a significant number (in this case more than half) of all conversions come from visits that aren’t the first!

Today’s post, however, is all about the details: how to get first touch tracking working to get you actionable data about real acquisition costs from Google Analytics.

You will need to be able to:

  • Import a custom .js file
  • Modify the Google Analytics embed code across your website
  • Create custom reports in GA

Note that this is going to set custom variables. If you are already using this functionality, you should be very careful with how you integrate this. Oh, and all of this is provided as is, with no warranty. I hope it will help you out, but only you are responsible for changes you make to your website and tracking code.

By default, GA attributes conversions to the last touch - i.e. the source of the visit that led to the conversion. I’m going to show you how to get the source of their first visit to your site.

Step 1

Embed a JavaScript file defining three functions:

  • distilledCheckAnalyticsCookie - in order to track first touch information, we only want to record details to custom variables on someone’s first visit. This function checks for the __utma visitor cookie
  • distilledTruncate - as I discuss in more detail over on SearchEngineLand, Google won’t allow you to set custom variables of longer than 64 characters (including the variable name) after URL encoding so this function is a slightly long way round of truncating the variable information
  • distilledFirstTouch - the heavy lifting - this is the function that sets the four variables outlined in more detail below

You can embed this with the following code anywhere above the Google Analytics code script in your page code:

<script type=“text/javascript” src=“”></script>

It’s a little clunky at the moment and I want to refine it a little to cope better with combinations of Google Analytics and Website Optimizer. If anyone has any good ideas for this, feel free to drop me a line or raise issues over at Google Code.

Step 2

Move your GA code above any Website Optimizer code or anything from Google that might write a visitor (__utma) cookie and look for:

var pageTracker = _gat._getTracker(“UA-XXXXXXX-X”); pageTracker._trackPageview();

In between those two lines, you want to put the following code:


So that your trackpageview code looks like this:

var pageTracker = _gat._getTracker(“UA-XXXXXXX-X”); // Distilled first touch tracking distilledFirstTouch(pageTracker); pageTracker._trackPageview();

Make sure you use your own UA-XXXXXXX-X identifier string!

This writes 4 custom variables (apologies for the ridiculous naming conventions - Google limits the whole of the variable name + value to 64 characters!):

  • l : original landing page (no query string)
  • s : original landing page query string
  • r : original referrer
  • q : if q=keyword+keyword is found, this contains that part of the referrer (it’s actually more complicated than that - I have taken the full list of keyword delimiters from Google help and attempted to pull them out into the fourth variable in case the full referrer is truncated by the character limit).

Step 3

The detail of this is probably best reserved for another day / another post, but suffice it to say that I have found that custom reports exported to Excel are probably the best way of analysing the data this method produces. Far be it from me to tell you what reports to create, but I suggest something like conversions or revenue by original referring keywords might be interesting!

Setting up a custom report in Google Analytics

I have found the Visitors —> Custom Variables report in GA to be flaky at best. I would advise avoiding that and creating your own reports.

Step 4

Work a tiny bit of Excel magic.

Because (as described above) Google encodes the data on the way into GA, you need to decode it to make real sense of it. I have made the assumption that Google’s URLEncode function works like JavaScript’s encodeURIComponent() function and written an Excel formula to help:

=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A1,“%2F”,“/”), “%2C”, “,”), “%3F”, “?”), “%3A”, “:”), “%40”, “@”), “%26”, “&”), “%3D”, “=”), “%2B”, “+”), “%24”, “$”), “%23”, “#”)

Just paste this formula into an empty cell and place the information you want to decode into cell A1. Manipulate as necessary. You’re welcome. And yes, I’m a (tiny bit) sorry for the nastiness of that formula.

On a side note

I was amused to see the following tweet from @jaamit:

First click attribution is a bit like crediting your first girlfriend with your current marriage

(Attributed to @avinashkaushik’s keynote).

Swiftly rebutted by @Philipbuxton:

Last-click attribution is like selling Alonso because he doesn’t score enough goals.

On that note, I’ll leave you to go ahead and implement and let me know what you think.

This documentation also appears on the Google code wiki.

Will Critchlow

Will Critchlow

Will founded Distilled with Duncan in 2005. Since then, he has consulted with some of the world’s largest organisations and most famous websites, spoken at most major industry events and regularly appeared in local and national press. Will is part...   read more

Get blog posts via email


  1. Hi Will,

    Many thanks for sharing your approach. Attribution management is definitely something a lot of people are interested in, but I would like to play devil's advocate and ask how you would make attribution management actionable.

    First of all I think you will need a lot of data to actually see patterns emerging, so I am not sure this is all that applicable for smaller sites. Many people don't even use segmentation so moving on to attribution modeling is quite a step up...

    It is undoubtedly valuable to find assist keywords and referrers that come earlier in the sales cycle and which may have been hidden by branded search terms that are closer to the transaction. If anything you can use this type of data to explain with Adwords conversion tracking can give you different results than GA. However, for modeling, don't you need to analyze ALL visit histories, including those that did NOT result in a transaction? That's a lot of data to crunch.

    Which brings me to my last point. How would you approach the modeling? Presumably the reason for doing this is so that you can come up with a model that would allow you to allocate marketing spend better. But is first-click attribution always appropriate? If the first visit is Google Adwords and the converting visit is my email campaign, should the email campaign not get at least some credit? Also, how far back in time do you go? Should a referral from a shopping comparison engine that sent a visit 364 days ago get any credit?

    I don't think there is a right or wrong way, and a model is just a model. Would be great to continue a conversation about the model itself.


    reply >
  2. Excellent points Michael. You could definitely build some pretty complex models, and maybe we'll get into that more in future posts...

    I also agree that first touch is not always the right answer (especially a long way in the past as you point out). One of the things I want to do next is get closer to quantifying 'assists'.

    At its most basic, however, I want to just clear up a single conundrum we see a lot which is clients not seeing the value of long-tail unbranded searches as "they don't convert". If most conversions appear to come via direct / email / branded search, there is clearly an acquisition channel not getting all the credit it could!

    As I build out some excel models, I'll be sure to share what I can. In the meantime, I'd love to hear more of your thoughts.

    reply >
  3. I've not even read this post yet but I'm so excited about it!

    I've been waiting for a post like this for ages, so thanks Will. Now if only I wasn't so damn busy over the next few days I could actually read the thing and dive into my analytics... :-)

    reply >
  4. Thanks for sharing this info. I was waiting for that, since you first addressed it in the Whiteboard Friday.

    This is just for the first touch tracking and not for the entire path, right?

    reply >
  5. Ricardo

    Would this code:

    be ok to use on site with SSL?


    reply >
  6. Derek

    Can you please briefly elaborate on whether this code can work with web site optimizer. I'm under the impression I can only use one or the other on a page, but not both.

    Thanks for all the great info.


    reply >
  7. Will - excellent information!

    One potential problem I see is with GA only allowing 5 slots for the custom variables. With the first touch tracking filling up four of those slots, you're left with only 1 additional visitor-level slot. (This is merely a limitation of GA - nothing against your first touch tracking method!)

    So I would be able to do first touch tracking, and set a custom variable for member = yes/no, but if I wanted to set a visitor-level custom variable for anything else, say membership level, I'd be out of luck.

    Any thoughts on this? Am I overlooking some (easy) solution?

    (By the way, can I blame you when my wife asks why I'm getting home so late on a Friday? )

    Happy weekend!

    reply >
  8. Kevin Hillstrom ( once said: "Sales attribution is like focusing on measuring every grain of sand. We lose sight of the whole beach when we do this! And just when we've attributed every grain of sand, a rogue wave comes in and we have to start attribution all over from scratch!"

    reply >
  9. Looks like I'll be helping to implement first-touch tracking soon!!! Quite excited... Is that sad?

    reply >
  10. Will,
    Thanks for providing this info. I'd be very interested in hearing more about step #3 but I also have a question in regards to one of your statements:

    "The point is not what those exact values are, but simply that a significant number (in this case more than half) of all conversions come from visits that aren’t the first!"

    I'm looking to solve the high conversion issue with branded terms. So when you create the custom report in Google Analytics, are you saying you will not be able to find out what the exact keyphrase the person initially searched for? If that is correct, is there a way to find out what the person's initial kephrase search was to help the long tail analysis?
    Michael Zukewich

    reply >
  11. Will,

    We've just taken over a site that uses the model you have outlined (found this page by googling the code used in the page!).

    A question though - I'm looking at the results and they vary wildly in terms of the number of visits that have this information attached. For instance looking into a custom report using the Dimension (l : original landing page (no query string)) we only get 15,000 visits reported for it out of 41,000 actual visits.

    Could you help explain that to me a little? I love the idea of first touch to aid decisions, but there's a big data gap there!

    Thanks Will.

    reply >
  12. Hi Rob,

    I am not 100% certain I have tracked down the issue, but I believe it is arising because of a truncation problem in the javascript (i.e. long referring URLs were causing an error in my code).

    I have uploaded a new version that I believe fixes this issue and so hopefully the data should start coming through better. I'll come back here and update if I catch additional issues. Let me know what you see.

    I am also working on a project to define reports that turn the captured data into something useful - subscribe to the blog to get the announcement when I have managed that.

    Thanks for stopping by - I hope we can get this working how we want it.

    reply >
  13. I'll look out for that Will thanks.

    reply >
  14. I refined the script by adding a line in distilledTruncate() to replace '%20' or '+' or ' ' with '_'. This way it will reduced the string size and be readable in GA.

    function distilledTruncate(input) {
    var byteLength = 63;
    input = input.replace(/s|%20|+/ig,"_");

    reply >
  15. Hi Raymond,

    I don't think that will help as Google urlencodes everything on the way in... Have you tested that change?

    Thanks for the idea...

    reply >
  16. Yes. It works. My keyword(custom var) are displayed as "this_is_my_keyword". I looked up the url spec. "_" will not be encoded.

    reply >
  17. You read the manual? Isn't that cheating?

    Nice idea - I'll try to make that change.

    reply >
  18. Hello,

    Is this using the traditional Google tracking code and if so is it possible to do it with the new one?



    reply >
  19. AG

    Hi Will,

    Awesome post and thanks for sharing the code it is great. I am currently testing it to measure the real ROI of PPC advertising and especially to outline the number of goals/sales achieved from first PPC click. For example if someone comes to a site through paid search initially, then leave and convert through organic search.
    I have found your method really useful, but I am kind of struggling to easily get the actual source/medium (cpc, email, organic) from the first touch.
    Would you have any suggestions to achieve this?

    Many Thanks

    reply >
  20. Reiner

    Thank you a lot for another groundbreaking post, Will!

    Yet there are still two questions I'm crunching on. First: how to use this code together with the asyncronus GA Code? While with the non-asyncronus GATC your code is placed between gat.getTracker and trackPageview I doubt that - when using asyncronus code - it would be enough to place it between _setAccount and trackPageview.

    In case I'm less interested in "l" and "s" for the initial landing page information but rather want to leave these two slots for other purposes: as I understand in your code I just need to allocate "r" to the first and "q" for the second slot and allocate no other slots. Correct?

    Would be great if you could shed some light on these questions.


    reply >
  21. Nice script, will be giving this a try...

    As for not having more than 5 custom variables, could you not use a second account & page tracker? Not ideal but prevents the custom variables being overwritten.

    reply >
  22. Thanks heaps for this code Will. I have written up a tutorial for including first touch tracking in Magento over on my blog.

    Also, @Reiner as a side effect of integrating with Magento I have modified it to handle the async GA code. You can get my fix from my blog in the immediate short term. I've sent my patch to Will too, so I hope he'll soon have it available in trunk. And you're right about the slots.

    reply >
  23. @Reiner I have integrated the async stuff Ashley mentioned into the hosted code so you should find that does what you need...

    reply >
  24. Will,

    Love the idea as we're trying to determine now if it's worth it for us to use Adwords as a relevant source of traffic. Just a little back story, I took over the web marketing for my current company last December. We were dabbling here and there in Adwords, Facebook and Youtube but throwing most of our resources into traditional media which just became too expensive and unreliable. My experience in the past told me that Adwords should be the first step that we jump right in with so that's what I did first. However, the conversion rates are pretty abysmal. Yet when I pull back on spend our overall sales on the site drop significantly and so do our phone calls which led me to believe that people were just coming back multiple times which was wiping out the first touch data (I didn't know that it was called first touch at the time). By running a report in Analytics, I can see that roughly 25% of my sales come from people who come back at least 1 time.

    Here's my question, let's say a user goes to our website via an Adwords ad and leaves. Then 3 days later comes back via an organic search on our brand name and purchases, will Adwords track that? Or will the organic search knock out the Adwords cookie on the user's machine? I would think it would b/c technically it's the organic search that generated the sale, despite the fact that the user never may have heard of us before click on the Adwords ad.

    reply >
  25. Google has answered this question in Adwords search funnels, well.. tried to answer, but it only takes ppc data into account. You can view how visitors went on a click rampage before they finally converted, some even 7 clicks on different ads before converting. Attributing first click and decision making based on that data might not be the ideal answer, but it's a start, least we have two touch points covered, just need to work out the inbetweeners :) Great job Will, thanks a lot for sharing this! Wish Avinesh can raise this to Google Team, he's the Analytics evangelist after all.. atleast for Urchin.

    reply >
  26. Just on the Visits to Purchase in GA. This is really a poor metric. It's visits to purchase from last campaign, so doesn't really give a true reflection of how many visits it takes your traffic to convert. For example, this

    5 visits: 1st visit: Google CPC > 2nd visit: Google Organic > 3rd visit: Bing CPC > 4th visit: referral > 5th visit: Google Organic (conversion)

    Would be 1 Visit to Purchase. The best solution is to create segments for Visit Counts and look at that report again, it breaks out the true percentages.

    reply >
  27. Any way to get the custom variables outputting simple first touch "Source/Medium" instead of the gclid/keyword info...?

    BTW we have integrated this code and it seems to be working...sometimes if there NO DATA, that's yourlot, you can;t drill down anymore, but so far so good.

    reply >
  28. This is pretty neat stuff. I have been look to implement this for a while. Thanks so much for posting it. I just learned about this from a SEOmoz webinar.

    reply >
  29. Would love a follow up for this post will on how to read the data you get from the four custom variables above mentioned. After having implemented them we are struggling sorting them out

    Great stuff as usual

    reply >
  30. Hey Will, as GWO introduced a new asynchronous code in the scripts can your solution still work and where the extra line should be added in the new version?

    reply >
  31. Hello, wonderfool article and method. Thanks.
    I use your script and function asyncDistilledFirstTouch(asyncStack) for asyncronus GA code but it doesn't work(( Besides I have no data in my GA account since I've been using this method.
    Final version for GA script in my website is (I track domain with subdomains and some Organic Sources)

    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'UA-XXXXXXXX-X']);
    _gaq.push(['_setDomainName', '']);
    _gaq.push(['_addOrganic', '', 'q', true]);
    _gaq.push(['_addOrganic', '', 'text', true]);
    _gaq.push(['_addOrganic', '', 'text', true]);
    _gaq.push(['_addOrganic', '', 'q',true]);
    _gaq.push(['_addOrganic', '', 'q']);
    _gaq.push(['_addOrganic', '', 'q']);
    _gaq.push(['_addOrganic', '', 'query']);
    _gaq.push(['_addOrganic', '', 's']);
    _gaq.push(['_addOrganic', '', 'q']);
    _gaq.push(['_addOrganic', '', 'query']);
    _gaq.push(['_addOrganic', '', 'p']);

    // Distilled first touch tracking

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

    Where could the problem appear? Thank you.

    reply >
  32. Hi guys,

    In light of the recent announcements by Google ( I think I will stop maintaining this functionality - it seems like it's getting superseded pretty soon.

    Thanks for using it and spreading the word up to now. Good luck in the future!

    reply >

Leave a Reply

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


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>