A New Method to Track Keyword Ranking using Google Analytics

In the past I’ve written about different ways to customize Google Analytics for SEO. This post is all about a new way to track keyword ranking using Google Analytics.
A little background…

There are lots of paid tools that will track where your content ranks in your search results. But my friend AJ Kohn wanted to try and develop a free way to measure rank with Google Analytics.

Actually, he had a brilliant idea: measuring the individual keyword rank, group keywords together into indexes and then track the average rank of those indexes over time – all with Google Analytics.

Track the SEO rank of keyword groups, or indexes, using Google Analytics.

Track the SEO rank of keyword groups, or indexes, using Google Analytics.
Image courtesy of AJ Kohn.

Here’s how AJ describes a rank index:

A rank index is the aggregate rank of a basket of keywords that represent a type of query class that have an impact on your bottom line. For an eCommerce client you might have a rank index for products and for categories. I often create a rank index for each modifier class I identify for a client.

It becomes not about any one term but the aggregate rank of that index. That’s a better conversation to have in my opinion. A rank index keeps the conversation on how to move the business forward instead of moving a specific keyword up.

After a little brainstorming and testing we think we have a method to do this and would love your feedback.

How This Works

Here’s the general idea:

1. Someone clicks on an organic search results
2. The user lands on your site
3. A custom piece of code that you install on your site collects the keyword and the rank of the result using a Google Analytics event
4. Google Analytics will automatically calculate the average position for the result
5. You create indexes of keywords and analyze the data using the Event reports OR Excel and the GA API

That’s it. Nothing more.

Let’s dig into some of the details of how this work.

The Code

The code is relatively simple. All it does is looks at the referring URL and, if it’s from Google Organic, plucks out the location of the search result and sends it to Google Analytics using an Event.

Here’s the code – feel free to copy it and use it. You do not need to customize the code for your site.

This section of the code parses out the keyword and the search result location. The search result location is stored in a query string parameters named cd.

var myString = document.referrer;
var r = myString.match(/cd=(.*?)&/);
var rank = parseInt(r[1]);
var kw = myString.match(/q=(.*?)&/);

Next there is a check to see if the keyword is (not provided). If it is (not provided) then we make sure that we track the keyword as (not provided).

if (kw[1].length > 0) {
var keyWord = decodeURI(kw[1]);
} else {
keyWord = "(not provided)";
}

Finally we send the data to Google Analytics using an event:
_gaq.push(['_trackEvent', 'RankTracker', keyWord, p, rank, true]);

For those of you that have not used events, there are 5 parts to an event:

  • Category: This helps you categorize the events and separate them in the reports.
  • Action: What happened that you want to track?
  • Label: Provides more information about the Action.
  • Value: Some numerical value for this event.
  • Noninteractive: This is a flag, it tells Google Analytics if this event should impact bounce rate and time calculations. If you have no idea what this means read about how Google Analytics calculates time.

For this tracking technique we’re sending the following data with the event:

  • Category: RankTracker – all of the data will be grouped under the Event named RankTracker.
  • Action: [keyword] – the action will collect the search term the user entered on Google.
  • Label: [landing page] – the label will be the landing page that the user landed on.
  • Value: [SERP rank] – rank of the search result will be collected for each keyword. More on this in a second.
  • Noninteractive: TRUE – this event will NOT change your bounce rate calculations.

The most important thing to understand is that the rank of the search engine result will be tracked as the value of the event. So as you get more and more visits from a keyword the TOTAL VALUE of the event for that keyword will increase.

Google Analytics will also create an Average Value metric for each keyword event. This value will actually represent the Average Rank for each search result.

Let me say that again.

Using this technique Google Analytics will automatically calculate the average position of your content in the search results.

I also want to point out that this data is generated from real clicks to your site. It’s actual people visiting your site and (hopefully) converting. To me, this makes it a lot more valuable than just tracking ranking that does not result in clicks or conversions.

So that’s how this technique actually works. As I mentioned, all you need to get the data is install the code on your site. Just make sure it appears AFTER the standard Google Analytics page tag.

Raw Ranking data in Google Analytics

Ok, let’s look at some data. It’s all event data, so let’s go to the Content > Events > Top Events report. I’m looking for the RankTracker event category.

Rank tracking data in Google Analytics.

You can find rank tracking data in the Google Analytics Event report.

Clicking on RankTracker I see all the actions for that event, which are really just the search terms.

The search terms and ranking are stored as Google Analytics Events.

The search terms and ranking are stored as Google Analytics Events.

Here’s the cool thing. Remember that the Average Value metric is actually the Average Position for each of those keywords. So for the time range that I’m looking at I’m seeing the average position for each keyword.

Let’s play with this.

Change the data over time graph to plot the metrics Average value. Now you can see the average ranking for all your search terms over time.

Plot your average search result rank.

Change the plotted metric to see your average search result rank change over time.

You can also select multiple rows and plot the Average Value to see how the ranking changes for specific keywords over time.

Plot the keyword rank of multiple keywords.

You can view the changing rank of your keywords by plotting multiple rows.

Another cool type of analysis you can do is use a secondary dimension to view the landing page for each keyword. The landing page is stored in the Label portion of the event. This is something a lot of people do to measure (not provided)

Keyword, landing page and Average SEO rank.

View keyword, landing page and Average SEO rank in Google Analytics.

Now we have keyword, landing page and the average ranking in one report.

We can also group keywords together into an index. The easiest way to do this in Google Analytics is using an Advanced segment. Again, a lot of credit to AJ for explaining how he does this.

For something simple like my blog I can create a segment based on my name and the Analytics Talk brand. That makes it easy to view the performance of that group of keywords.

Advanced segment for a keyword index.

Create an Google Analytics Advanced Segment to group keywords together into an index.

While I can do analysis within Google Analytics, the real value is exporting this data to excel and producing more detailed report. AJ covers that nicely in his post. I’ll post some other methods next week.

Actionability

The entire reason you should collect this data is to measure how your actual content-creation tactics are working. It’s granular data that helps support your overall strategy of “content marketing.”

If you’re not getting valuable traffic from content and keywords that you are optimizing for then you need to re-evaluate your tactics and change.

Caveats & Things to Remember

First, this data is only for traffic coming to your site. It does not give you the ranking for search terms if you are not getting traffic.

As mentioned above I actually like this approach. If I’m not getting traffic/conversions then my ranking sucks. I need to create better content.

Second, Google blocks the referring keyword data for secure search queries. This includes logged in users, people using Safari on iOS 6 and Firefox. There are no analytics tools in existence that will provide the blocked keywords. In Google Analytics you’ll see the keyword (not provided) in your data. You’ll also see (not provided) in this data.

So there you have it. We (me and AJ) hope that this techniques helps people track rank freely and more effectively. Feel free to innovate and develop on top of it and let us know what you think.

Be Sociable, Share!

    Like this post? Sign up to get posts delivered to your inbox.

    Comments

    1. says

      Great idea. I may use this in a couple of areas, although I would also like to collect Bing search data. I haven’t parsed the referring URL before to collect data in this way. Does Bing pass along the same info in the same way?

    2. Sebastien Brodeur says

      Instead of using document.referrer.match(/google\.com/gi) for detecting Google referrer, it may be a better option to use document.referrer.match(/google\./gi) so it would work for google.ca, google.fr, google.es, etc also.

    3. Colby Phillips says

      This seems really promising. At our agency, we take rank tracking data for groups of keywords (we call them keyword buckets) and run them through a proprietary spreadsheet that assigns a percentage of visibility score based on the keyword rank. This produces an aggregated visibility score for each keyword bucket. We find it much more productive to talk to clients about SERP visibility for groups of keywords that represent product or solution groups, as opposed to having them focused on the rankings for a specific keywords. I think this is analogous to the keyword index as you describe it. I would really be interested in developing this kind of report in GA.

      • says

        @Colby: You can source the data for your spreadsheet using the code in this blog post. Once it’s in Excel you can use your proprietary methods to massage it into buckets and calculate visibility. In fact my post next week will be about automatically extracting the data using the Google Analytics API and putting it into Excel.

    4. says

      Hi, thanks for this Justin, a great idea, I didn’t realise ranking position came through in the referrer too. Handy!

      One small change I would recommend is to replace the “(/google\.com/gi)” with “(/google\./gi)” just because then it will track the localised versions of google search also (i’m using google.co.uk and your script will not track me as it is, if you make that change it will track me)

      Thanks again, great use of events here. I’ll definitely be using it.

      • says

        @Ross: Great suggestion. Sebastian also made the same suggestion – and I agree that it could be very useful to segment the data. I think we’re going to collect feedback for a while and then modify the script. Another suggestion was to isolate image search traffic.

    5. Placo says

      Can’t wait to put this in place. Thanks!

      Last time I looked, there looked like there was a decreasing amount of q= parameters in my data set, but I suppose data from some is better than none.

      Can you somehow figure out how to do this for AdWords too?

    6. says

      Can you provide me a screenshot of how the code may look like? I’m little bit confused with the usage of this code. How exactly it should be placed in footer or header file? A screenshot would help a lot.

      • says

        @Kinjai: Sorry for any confusion. But as long as it appears after the standard Google Analytics page tag it will work. So you can put it in the header or the footer. Hope that helps.

      • says

        @Nikhil: Yoast’s method (which I like very much) tracks the number of the first result of the page the keyword was on. this method tracks the actual rank locations. But the big advantage of this method is that it will automatically calculate the average rank for each keyword over time. That’s the advantage of using the Event. It collects the rank as an actual metric, a number, that is averaged over time.

        I hope that clarifies the difference.

        Thanks for the question.

        • says

          Thanks Justin for clarifying my doubt. It seems like I provided the wrong link in my previous comment. I was using a filter which tracks the position of my webpage for a particular keyword when user clicks on Google search results. That method is explained here in Yoast http://yoast.com/track-seo-rankings-and-sitelinks-with-google-analytics-ii/ But had its disadvantages. Because it doesn’t add the position for every keywords. The reason may be Google doesn’t add the cd parameter everytime in its search result URLs.
          I have implemented this event tracking in one of my website. Thanks Justin!

    7. Rajeshwari says

      Wow!! It is great..I’ll use in my current project and see the result..

      Thanks a lot Justin for sharing this info…..

    8. Sjors Peerdeman says

      I’m using a GA filter to extract the keyword ranking information, using the user defined dimension. I can’t remember where I got the idea from originally, but here’s the settings:

      Custom Filter > Advanced
      Field A : Campaign Medium // organic
      Field B : Referral // (\?|&)cd=([^&]*)
      Output to: User-Defined // $B2
      Yes // Yes // Yes // No

      As we’re working with clients, I’ve always preferred to keep the code changes on their websites to a minimum.
      What are your thoughts on the pros and cons of each method?

      • says

        @Sjors: That’s a method that my friend Yoast blogged about a while ago. It certainly works great. As I mentioned in another comment the big benefit of this method is that it will actually calculate the average rank for each keyword. Then you can trend that over time, group keywords together into an index and trend the index over time, etc. Or you can export the events to Excel and create some nice reports. More on this in a later blog post.

      • says

        @Andrea: Love this question! If you’re using the Google Analytics template then NO, it will not work. The reason is that GTM executes all of the tags asynchronously. However, there is a workaround:

        1. Take your GA code and past it into a Custom HTML template
        2. Add this rank tracking script to the same Custom HTML template
        3. Add the template to all the page on your site

        Hope that makes sense and thanks for the great question.

        • says

          Brilliant idea and quite safe, because i could add an additional profile and keep the event tracking inside that profile. :)
          If the GA code is inside the pages (the usual) , may i add a custom html TAG to track events inside that profile ?

          • says

            @Andrea: Yes, you could do that. You could create a new web property in Google Analytics, then use a Custom HTML template to add the new GA tag and this tracking script to your site.

            • says

              I’m using the Google Tag Manager plugin for WordPress and have created the Custom HTML template with the GA tracking code and the added code for tracking keyword ranking below it in the same template. I am getting no data. Is there a workaround for the WordPress plugin?

              • says

                @Michael: The problem with GTM is that the tags load asynchronously. It may be that your custom tag is loading before your GA tag. Try removing your GA tag and putting your GA code in the custom container. Test, test and then test some more. Hope that helps.

    9. says

      Wonderful.
      The main interest I see is to know the exact mean position your keyword has in Google. With traditional tools you only get your position in private navigation without the “local” correction.
      I am sure that you get very interesting stats. We will try this very soon.
      Thank you.

    10. says

      Another version :

      if (document.referrer.match(/google\.([a-zA-Z]{2,5})/gi) && document.referrer.match(/cd/gi)) {
      var myString = document.referrer;
      var r = myString.match(/cd=(.*?)&/);
      var rank = parseInt(r[1]);
      var kw = myString.match(/q=(.*?)&/);
      var moteur = myString.split(‘/’)[2];

      if (kw[1].length > 0) {
      var keyWord = decodeURI(kw[1]);
      } else {
      keyWord = “(not provided)”;
      }

      var p = document.location.pathname;
      _gaq.push(['_trackEvent', moteur, keyWord, p, rank, true]);
      }

    11. says

      Hey Justin (and AJ) – Thanks for the great post!

      Forgive my noobie question, but will ecommerce data come into this event? Basically, will I be able to look at visits, average position, and revenue and transaction data for each keyword from this code?

      • says

        @Jess: I like the way you think :)

        If you’re an ecommerce company, and you’re using Google Analytics ecommerce tracking, then there should be a tab for ecommerce metrics at the top of the event report. Just click that tab and you’ll see revenue, per visit value, etc. for each keyword.

        HOWEVER, you can not view the avg. event value, along with the revenue, in a single report. Those metrics do not mix.

    12. cedric says

      Hello, First time I read you.
      I have made a modification to your code to get host google.

      First line :
      if (document.referrer.match(/google\.com/gi) && document.referrer.match(/cd/gi))

      To :
      if (document.referrer.match(/google\.([a-zA-Z]{2,5})/gi) && document.referrer.match(/cd/gi))
      After i create a new var
      var moteur = myString.split(‘/’)[2];
      And finally modify track event like this : _gaq.push(['_trackEvent', moteur, keyWord, p, rank, true]);

      In google analytics i made a custom board to track some information :
      https://www.google.com/analytics/web/permalink?uid=mPKMZ_rER4K2PmmX6lowxQ

      Like this you can have each ranking by google.* (I’m french and .be, .lu are important.).

      The JS with modification :
      if (document.referrer.match(/google\.([a-zA-Z]{2,5})/gi) && document.referrer.match(/cd/gi)) {
      var myString = document.referrer;
      var r = myString.match(/cd=(.*?)&/);
      var rank = parseInt(r[1]);
      var kw = myString.match(/q=(.*?)&/);
      var moteur = myString.split(‘/’)[2];

      if (kw[1].length > 0) {
      var keyWord = decodeURI(kw[1]);
      } else {
      keyWord = “(not provided)”;
      }

      var p = document.location.pathname;
      _gaq.push(['_trackEvent', moteur, keyWord, p, rank, true]);
      }

      • says

        I was having the same problem with the code showing on the page too! I found that if I put before the code and after the code, then the code wasn’t visible on the page anymore. Being a newbie I’m not sure this is right – could someone confirm?

      • says

        @Coltrane: The code in my post will only work on Google.com. BUT some other folks have posted new versions in the comments that will track other Google domains (.au, .uk, etc.) I have not tested the code, but it looks good. Based on feedback over the next few weeks I’ll probably make some updates.

    13. says

      This is a great method because it helps you understand the breadth of your current rankings versus the limiting approach of loading up a ranking tool with specific keywords to track. With the not provided increasingly limiting value of branded/generic keyword analysis and etc; this is a great way to add value back into GA for tracking SEO success.

      Thanks for sharing Justin! We have already started implementing for clients.

    14. Yougender says

      Hi Justin,

      I had one question, since we are capturing ‘cd’ parameters to track rankings, does it mean that the local listings/ universal results will also be counted along with the organic listings?

      If yes, is there a way to exclude such results and just get the organic listing?

      Thank you to You & AJ Kohn for the new method.

      • says

        @Yougender: Yes, every search, coming from a Google domain and containing the ‘cd; parameter will be tracked. I guess you could exclude the local listings/universal results if you can identify them via some query parameter in the referral URL. You’d need tweak the tracking code. Perhaps this is a feature for V2.

    15. says

      Hi Justin, Thanks for writing up this guide, it’s fantastic. It inspired me to write my own guide to event tracking (http://www.koozai.com/blog/analytics/the-complete-google-analytics-event-tracking-guide-plus-10-amazing-examples/) as there are so many reasons to use event tracking! I’ve included a link to this post from there as there was no point me trying to explain it myself :) I’m loving the data available with this, it’s much more flexible than using filters as I’ve done in the past.
      Anna.

    16. lax says

      did Google stop serving part of the referral information. I get
      document.referrer.match(/google\.com/gi) returning ["google.com"] and
      document.referrer.match(/cd/gi) returns null in my console

    17. Mike says

      Unfortunately, as Yougender pointed out, any type of keyword tracking using the “cd” parameter is going to include universal results. These can really skew the results!

      For example, if there are 3 news listings on the first results page, and you appear as the last organic result on the page, you’re going to have rank = 13. If you get bumped one spot though, you’ll land as first result on the second page, but since there are no news listings there, you’ll appear as having rank = 11 (since the cd parameter starts at 11 on the second page, regardless of how many news/picture/local/sitelink results there are on the first page). So basically, even if you lost ranking by 1, in Analytics you’ll see it increased by 2.

      This is even a bigger problem since some universal results are removed/added from time to time, further giving false increase/decreases in Analytics ranking.

      I’ve been trying to find a workaround to remove universal results, but can’t seem to figure it out. Guess we’ll have to rely on GWT data for now…

    18. Mike Yin says

      when i google in Firefox and IE, it seems referrer dont’ have “cd=” , and for chrome, all searches are encrypted. this js can’t extract any ranking data from google

      • says

        @Mike: You may want to check with AJ – but coverage with the CD parameter should be pretty good, that includes encrypted searches. In my data I see a search term of (not provided) and the ranking for the result. It’s not perfect, but I’ve been capturing ~ 50% of the queries.

      • says

        @Vijay: That’s the average rank for your keywords. If things are working right, then it means that many of your keywords are long-tail keywords and appearing later in the search results.

    19. says

      Awesome, I tried it, it’s so useful & it works well. Thank you Justin & AJ.

      I modified a little the script to prevent incomptibilities if you like:

      (function (d) {
      var myString = d.referrer,
      r, kw, keyWord;
      if (window._gaq && myString.match(/google\./gi) && myString.match(/cd/gi)) {
      r = myString.match(/cd=(.*?)&/);
      kw = myString.match(/q=(.*?)&/);
      keyWord = (kw[1].length > 0) ? decodeURI(kw[1]) : “(not provided)”;
      window._gaq.push([‘_trackEvent’, ‘RankTracker’, keyWord, d.location.pathname, parseInt(r[1], 10), true]);
      }
      }(window.document));

      • Victor says

        Mathias:

        Looking to test your version of the code. How is it different from what the original does? I am also getting lots of javascript validation errors with our code, so not sure where its broken. thx.

    20. says

      very inventive. I used to have a setup somewhat similar but this seems more elaborate. I used the custom variable feature provided in GA. The only good thing about that setup was that no code had to be written/changed for that. Will definitely give this method a try.

    21. says

      Hi Justin, first thanks for this guide, just awsome.

      I want you advice beacuse tried to install on blogger and I got this Error:

      Error analizing XML, line 903, column 47: The entity name must immediately follow the ‘&’ in the entity reference.site

      Question: Doesn´t work on a blog site?
      http://seoyanalytics.blogspot.com/

      Thanks a lot,

      Juan

      • says

        @Juan: Thanks for the heads up. Unfortunately I’m not a Blogger expert, I use WordPress. But it seems like the code may needed to be encoded for Blogger to accept it.

        Anyone else have any feedback for how this might work on Blogger?

    22. says

      First off, this is awesome. Thanks so much to you and AJ for figuring this out and sharing. Just to clarify, the code you provided – should that be placed inside of … tags just after the analytics script? Or should this be placed inside the closing tag of the analytics script? Thanks for any clarification.

    23. says

      That’s a brilliant piece of thinking and a great article. Paid services like Web Position will hate seeing this kind of thing… Cheers for sharing!

    24. faye says

      Hi,
      Great article!
      How do I paste the code on my website if I use yoast google analytics wordpress plugin ?
      many thanks

    25. Victor says

      @Justin : I notice that not all my google organic search keywords are showing up in my event tracking report for this – I am referring to the ones that actually do have a keyword value available. For example, on any one day, I see , say, 50 organic keywords from google specifically in my Traffic sources report. Then in my event tracking report for keyword tracking, I’d see 5. Looks like very few make it through. Its working but not all the time. Is that your experience as well?

      btw – you mentioned a version 2 coming? Many thanks for your insights here ..

      • says

        @Vistor: Thanks for the heads up. It should be a lot more than that. I have to do a bit more investigating, most people are getting more than that. The coverage of the ranking parameter should be on most Google sites, so the code should work.

    26. Rachel Sweeney says

      Hi,

      nice post, really great to see this.

      Two questions,

      1. Do you see the cost uploader feature being extended in such a way that we can upload 3rd party rank checking? The ability to choose average instead of some on upload would be amazing!

      2. What does this offer that integrating with webmaster tools does not? I guess it’s easier to segment when wemaster tools metrics are unavaliable?

      thanks,
      Rachel

      • says

        @Rachel: Cost-data upload runs on a new feature called Dimension Widening. This feature, when released, will let you import almost any type of data into Google Analytics. You basically specify a key (like campaign ID) and then upload data to link to that key. The benefit over Webmaster tools is that all of the dimensions that you upload via dimension widening become first-class dimensions. You can use them in custom reprots, segments, etc.

    27. says

      @Rachel Keep in mind that while webmaster tools is good for some general trend insights the data is highly unreliable. The data tracked using this new GA ranking method has proven to be more reliable from what we have seen to date. Of course, it isn’t perfect as others have mentioned significant drawbacks based on local and universal search listings that can misrepresent the true ranking.

    28. says

      this is what I am looking for.. firstly thanks for the share.. currently I am using yoast method to track rankings but this one is far superior..

    29. Nice Idea But No says

      I like your idea but sadly with https secure google searches it makes this idea useless. For those with smaller websites the amount of plain ol http referrers is too small to measure with any granularity. You might as well stick to traffic sources / organic which provides the same information without any extra code.

      • says

        We are having success with this technique. It can be very helpful at times to validate search ranking data and see fluctuations in rankings. Let me know if you have any specific questions and I will do what I can to provide more details.

    30. says

      @Ross
      I know you mentioned that replacing the “(/google\.com/gi)” with “(/google\./gi)” will also show the localised version of google.
      I would like to restrict it to only my local Google here in South Africa. Would I keep it as is or change the “/google\.com/gi” to “/google\.co\.za”

      Thanks in advance

      • says

        @Nikitha: This script will not work with Universal Analytics. I’m trying to decide if I will update it for Universal. You – and a few others – have requested an update. Thanks for the note!

    Trackbacks

    1. [...] Justin Cutroni und AJ Kohn haben eine neue elegante Methode entwickelt, wie die Daten auch in Google Analytics gut aufbereitet und reportet werden können. Trackt man das SEO Ranking mit GA Events ergeben sich auch einfachere Auswertungsmöglichkeiten. Der Durchschnitt der Rankingposition wird so automatisch bereits durch GA errechnet. Ein entscheidender Vorteil gegenüber der bisherigen Methode mit Filtern. Und zum automatisierten Monitoring können nun auch Google Alerts (Benachrichtigungen) eingerichtet werden. So können bestimmte Positionierungsveränderungen speziell bei sogenannten Money Terms überwacht werden und E-Mail Alerts erfolgen, wenn der Durchschnittswert fällt. [...]

    2. [...] Analytics Talk is the personal blog of Justin Cutroni, the Analytics Advocate at Google, so he’s in a great position to provide relevant information on this topic. This is a good place to get a handle on what the latest Google Analytics updates mean for you. Recent posts cover universal analytics, cohort analysis and the Analytics tag manager and this recent post (which includes a code snippet) outlinesA New Method to Track Keyword Ranking using Google Analytics. [...]

    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>