Custom dimensions and metrics in Universal Analytics

Updated on 03/03/2014 to clarify the difference between visit ids and visits.

Universal Analytics allows having up to 20 custom dimensions and 20 custom metrics (more in a premium account). In case you are unfamiliar with what exactly a custom dimension or metric is check the available information in the Google Analytics documentation.

The great thing about custom dimensions is that they are fairly easy to implement, are easily accessible in the reports and can also be used for advanced segments. I also hope that, once Universal Analytics allows to manage remarketing lists in analytics, we can have remarketing lists based on custom dimensions.

General technical remarks

Custom dimensions or metrics must be sent together with hits like pageviews, events, social or e-commerce – there is no way to send a custom dimension or metric on its own. There are two option to send a custom dimension/metric.
Option 1:

ga('set', 'dimensionXX', 'dimensionValue A'); 
ga('set', 'dimensionYY', 'dimensionValue B'); 

This ensures that the information is sent with every hit during the lifetime of the tracker object, so also for any subsequent event, social or e-commerce hits on the same page.

Option 2:
If you want that a dimension or metric is only sent via a specific hit, then you need to supply an object with the dimensions/metrics as argument to the call to send:

ga('send', 'pageview', {
  'dimensionXX' : 'dimensionValue A',
  'dimensionXY' : 'dimensionValue B',
  'metricXX' : metricValue

For details check the Google Analytics documentation about sending dimensions and metrics.

Important: For dimensions it is usually fine to use ga(‘set’,…), but for metrics this would mean that the same data might be sent multiple times and thus counted multiple times. So for metrics only the second approach should be used.

Also keep in mind that if a custom dimension is not set for some hits, then these hits will not appear in the reports when you use this custom dimension as primary or secondary dimension for the report. If that is a problem, then you should consider sending a string like ‘not set’ or similar if the dimension has no value, but then be careful that you do not overwrite any previously set session-level or visitor-level dimensions.

Below are the custom dimensions and metrics that I have already used or consider to use for clients. As you can see in this list I mainly use custom dimensions – I do not use custom metrics much (yet). I will add to the list whenever I find more useful ideas. The list is in no particular order – depending on the client some ideas might be absolutely useless and some might be extremely useful.

Track the full referrer

Google Analytics makes it fairly difficult to get the full referrer of a visit. It seems that by default any query parameters are stripped from referring urls and if campaign tracking is used, then the referrer information is not visible at all. Having the full referrer information could be helpful to see e.g. on which pages exactly a paid banner was placed or on which forum page exactly a link to the website was placed.

Important: The referrer information is not always available e.g. because some proxies strip the information. For paid banners it can also happen that the reported referrer is the url of the ad server, not the original website.  For referrals from Facebook you get the url of the redirect script that Facebook uses, not the actual Facebook page (although I am wondering whether this is in the encoded part of the referrer url).


The setup is really simple and does not require any changes on the website.

Step 1: Set up a session-level custom dimension ‘full referrer’:

Universal Analytics- create custom dimension

Step 2: Set up an advanced view filter that copies the referral information into this custom dimension:
Filter Field A: Referral
Filter Pattern: (.+)
Filter Field B: leave empty
Output Field: full referrer
Output constructor: $A1

Universal analytics - set up filter to track full referrerNote: Custom dimensions are set up per web property, not per account. If you want to create a filter that uses a custom dimension then this filter needs to be set up for a view – it cannot be set up on an account level.

Use view filters for filters using custom dimensions

Google provides local versions of their search engine, e.g. google.fr for France or google.de for Germany. For websites targeted to multiple countries in can therefore be useful information to see which traffic came from which local version of Google.

In classic Analytics it is possible to adjust the way organic traffic sources are recognised by calling _addOrganic in the tracking code. Brian Clifton made an excellent plugin that made it easy to have the complete list of search engines in the traffic sources report.

In Universal Analytics this can be managed in the backend – here a screenshot (with just a few search engines):

Universal Analytics - set up organic search enginesIn general this approach is fine, but it can be fair bit of work to set up properly, especially since the order of the entries must be carefully managed to ensure that e.g. google.com.au and google.com are correctly reported.

I also do not like crowding the main traffic source report with the various local versions of search engines, so custom dimensions come to the rescue. The domain is part of the referring url, so the information is already available if the full referrer is tracked. But having the domain in a separate custom dimension can be quite a time saver, so if the limit of custom dimensions has not been reached, I would implement it.


The set up is pretty similar to tracking the full referring url:
Step 1: Set up a session-level custom dimension ‘referring domain’.

Step 2: Set up an advanced view filter that copies the domain of the referring url into this custom dimension:
Filter Field A: Referral
Filter Pattern: ^https?://([^/]+)
Filter Field B: leave empty
Output Field: referring domain
Output constructor: $A1

Note that this will extract the referring domains for all traffic sources with a referrer, no matter whether organic or not. But restricting the filter to organic traffic would mean extra work so it is easier to simply track the referring domain of all referrers.

If someone comes to your website via a organic search result from Google, then the referrer contains some potentially useful information in the query parameters. The parameter cd denotes the position of the clicked entry in the SERP. Keep in mind that the cd parameter on its own is tricky – a value of 5 could mean that the clicked entry was the fifth entry on this SERP or that the clicked entry was the fourth sitelink of the first entry. Similarly a position of 11 could mean the last sitelink of the first entry, some entry on the first page if the first entry contained less than 10 sitelinks or the first entry on the second page.


The set up for tracking the CD parameter is pretty similar to tracking the full referring url:
Step 1: Set up a session-level custom dimension ‘Google organic pos’.

Step 2: Set up an advanced view filter that copies the cd parameter of the referring url into this custom dimension:
Filter Field A: Referral
Filter Pattern: [\?&]cd=([^&]*)
Filter Field B: leave empty
Output Field: Google organic pos
Output constructor: $A1

The ved parameter contains information about the page number of the SERP and the type of entry that was clicked (sitelink, image, news etc.), but unfortunately it is encoded. Here a couple of interesting blog posts about this parameter and how to decode it:

The article from moz.com also explains how to decode the ved value via JavaScript which can be used to decode the value client side and then directly fill suitable custom dimensions in Universal Analytics. I do lots of custom reporting via the API so I do the decoding server side and use the values only in my custom reporting. Therefore I have not tested this client-side approach yet, but it looks very interesting.

Filter internal visits

To really focus on the target audience it is often desirable to exclude any internal traffic from being reported in Google Analytics. If all internal traffic comes via a limited set of fixed IP addresses then view filters can be set up to exclude any traffic coming from these IP addresses. But some companies might not have a fixed IP or might have many offices whose IPs change once in a while.

In this case another approach can be used: Many company websites have a specific part that can only be accessed by their employees. This can be used to identify the browsers of the employees and mark them as internal.

Step 1: Set up a hit-level custom dimension ‘is internal’ in the backend.
Step 2: On the internal pages of the website set a cookie ‘is_internal’ with a life time of two years or longer.
Step 3: Adjust your tracking such that the cookie ‘is_internal’ is checked and if it exists, the custom dimension ‘is internal’ is set to ‘yes’.
Step 4: Set up a view filter in the backend that excludes all traffic where the custom dimension ‘is internal’ has a value of ‘yes’.

Example code for step 3 (assuming that ‘is internal’ is dimension number 1)

//check the cookie
var isInternalVisit = (r=RegExp('(^|; )'+encodeURIComponent('is_internal')+'=([^;]*)').exec(document.cookie))?r[2]:null;
ga('create', 'UA-XXXX-YY', 'auto'); 
//use the result of the cookie check to set or not set the custom dimension
if(isInternalVisit) {
  ga('set', 'dimension1',  'yes');  
ga('send', 'pageview');  

Tracking client/visit/hit ids

This information is extremely useful for debugging. It could also be used in combination with the API e.g. to create custom multi-channel reporting that looks back further than the current maximum lookback time of 90 days.

Client id

Google Analytics does not expose the client id in the backend which makes it difficult to track a specific client. Google uses cookies to track visitors, so keep in mind that a client means a specific browser on a specific computer – based on cookies it is not possible to tell whether two different persons use the same browser or whether the same person uses multiple computers.

To track a client id in a custom dimension you could either use the client id that is used by the tracking code itself or provide your own.

If you want to use the client id that is used by tracking code itself then you need to adjust your tracking code such:

ga('create', 'UA-XXXX-YY', 'auto');  
ga(function(tracker) {
  ga('set', 'dimension1', tracker.get('clientId'));  
  ga('send', 'pageview');  

The use of


allows to defer executing the code inside the function until the GA library has loaded. This makes it possible to use tracker.get(‘clientId’) to retrieve the client id and set it as custom dimension.
The number of the dimension needs to be adjusted to whatever (visitor-level) dimension was set up in the backend of Universal Analytics.

Instead of changing the standard page tracking code it is also possible to use an extra event to send the client id as custom dimension to Universal Analytics:

ga(function(tracker) {  
  ga('send', 'event', 'dummy-event', 'dummy-action', {
    'nonInteraction': 1, 
    'dimension1': tracker.get('clientId')

If you want to use your own client id, then just use your server-generated client id as value for the custom dimension. To be consistent your server should then store this generated client id in a cookie on the browser and reuse it in the next visits made from this browser. Keep in mind that client ids identify a browser and not a person – user specific information is handled a few sections further down.

On a side note: The client id generated by Universal Analytics already contains the date and time of the first visit (as timestamp, meaning as number of seconds since 01/01/1970). For server-generated client ids it should therefore be considered to add similar information to the generated id.

Visit id

Tracking visit ids in Analytics allows to follow a specific visit from start to end (especially in combination with the time of hit mentioned below).

Universal Analytics does not create any visit ids on the client side. Therefore this needs be managed by the website:

Example code to generate/read a visit id:

function getGaVisitId() {
  //check whether we have a visit cookie
  var cName = 'gaVisitId', r,
  visitId = (r=RegExp('(^|; )'+encodeURIComponent(cName)+'=([^;]*)').exec(document.cookie))?r[2]:null;
  //if we do not have a cookie, set one
  if(!visitId) {
    visitId = new Date().toUTCString() + '-' + Math.random();
    document.cookie = cName + '=' + visitId + '; path=/';
  return visitId;

This will generate a visit id that starts with the date and time like this ‘Tue, 25 Feb 2014 18:00:24 GMT’ (which is RFC-1123) and ends with a random number to make it more unique. This id is then stored in a session cookie to ensure that the same id is returned during the entire visit. High volume websites might need a better generator to get a truly unique number, but for most websites this should be ok.
Note: If the website has already JavaScript functions that can read and set cookies then these functions should be used.

Use this function to set the custom dimension in the tracking code:

ga('create', 'UA-XXXX-YY', 'auto');  
ga(function(tracker) {
  ga('set', 'dimension1', tracker.get('clientId'));  
  ga('set', 'dimension2', getGaVisitId());  
  ga('send', 'pageview');  

or send it via an extra event:

ga(function(tracker) {  
  ga('send', 'event', 'dummy-event', 'dummy-action', {
    'nonInteraction': 1, 
    'dimension1': tracker.get('clientId'),
    'dimension2': getGaVisitId()

Potential improvement: Use a long-term cookie to count the number of visits and use this information in the visit id.

Since this information is valid for the entire session it is enough if the information is sent once during the visit – it does not need to be sent with every hit. Sending it with every hit does not really have negative effects, it just means that a few unnecessary bytes are sent to Analytics.

Important note (added on 03/03/2014): The code above will set a session-level cookie for the visit id. This might not be the same as a session in Google Analytics. Differences: By default Google Analytics closes a session after 30min of inactivity where as the cookie in the example code will remain active until the browser is closed. Google Analytics also opens a new session if there is a new referrer, new campaign information or a new gclid in the url which is also not considered in the visit id as outlined above.

To align the cookie for the visit id with the 30min session timeout in Google Analytics is doable – just give the cookie an expiration date of 30 minutes in the future and always reset the cookie on each hit. Creating a new visit id when there is a new referrer is more complicated because you would need another cookie to store the original referrer. So I would do this only if there are big discrepancies between visit ids and visits. To see the number of visits per visit id create a custom report with the custom dimension for the visit id as primary dimension and ‘visits’ as the metric.

Exact time of hit

The exact time of the hit is extremely useful e.g. to make your own flow analysis and (with the API) create your own flow visualisation.

The code is really simple.
For the pageview:

ga('create', 'UA-XXXX-YY', 'auto');  
ga(function(tracker) {
  ga('set', 'dimension1', tracker.get('clientId'));  
  ga('set', 'dimension2', getGaVisitId());  
  ga('set', 'dimension3', new Date().getTime().toString());
  ga('send', 'pageview');  

For an event:

ga('send', 'event', 'event category', 'event action', 'event label', {
  'dimension3': new Date().getTime().toString()

This will report the date and time as timestamp including milliseconds (which means the number of milliseconds since the 01/01/1970). This allows to easily sort the e.g. the pages by the exact time when they happened. Using a human-readable format like the RFC1123 used for the visit id might be nicer to read, but does not allow such sorting.

Obviously the dimension used for this information (here dimension3) must be a hit-level dimension. Ideally this dimension should be added to every hit, no matter whether pageview or event and maybe even social, e-commerce and any other hits.

Weather info

Having weather information in Google Analytics can be quite useful to see if there is any correlation between the current weather and the behaviour of the customers on the website. This information could then be used e.g. to lower advertising bids if the weather is good because the Google analytics proves that if the weather is good then everyone is outside instead of shopping online.

To get the weather information you need to use some third-party service that supplies this information based on the IP or geo-location of the visitor. Retrieving the weather information takes time, so this should be asynchronously to ensure that the website is not slowed down by this.

The exact code depends on which third-party service is used and whether the information retrieval is done via JavaScript or on the server. Therefore just a rough outline of the steps:

  • Step 1: Check whether you have retrieved this information already for this visit (see step 4). If yes, skip the other steps.
  • Step 2: Have a JavaScript on the website that fetches the weather information via Ajax from your server or use whatever JavaScript API is offered by the third-party service.
  • Step 3: Once you have the information send an event to Google Analytics that submits the custom dimension (and/or metric):
    ga('send', 'event', 'weather-info', 'sunny', '19 degrees', {
    'nonInteraction': 1, 
    'dimension4': 'sunny',
    'metric1': 19

    In this example the description of the weather (sunny) is sent both as event action and as custom dimension and the temperature is sent as event label and as custom metric. It is not really necessary to use the weather info in the event action and label, but since we need to send an event anyway, we might as well have the weather info in the events as well.

  • Step 4: Set a session level cookie that indicates that the weather information has been sent to Google Analytics to avoid fetching it at every page load in this visit.

Useful links:

Other ideas for custom dimensions/metrics

  • Payment method: Track how many people pay with Visa, Mastercard, Paypal, etc. This should be a hit-level dimension. This could also be tracked as event, but tracking it as custom dimension makes it easier e.g. to see correlations between payment type and traffic source/country/basket size/…
  • Basket/cart behaviour: Use custom metrics to track the number of products added/removed from the cart within a visit. This way you can compare the number of products added to to the cart with the number of purchased products.
  • Profit margin or product cost: Tracking this information can be useful to to see the actual profit that is made e.g. by traffic source instead of seeing just the revenue. If you should decide to make this data available as custom metric in Google Analytics, then you should definitely use server-side tracking (using the measurement protocol) for this – otherwise everyone can see your profit margins when they make a transaction. Also consider that you give potentially valuable internal information to Google – you might or might not want to do that.
  • For international websites: Use a custom dimension to see whether the current day is a public holiday or a school-holiday. It can be difficult sometimes to keep track of which days are holidays in which country, so pushing this information automatically into Analytics makes it easier to check what is going on. Of course this needs to be set up server-side (or with a third-party API).
  • Data for logged in visitors: If your website requires a log in then the actual user can be clearly identified. This allows to track user-specific data to Google Analytics. Note that the terms of service for Google Analytics forbid to track any information that allows Google to identify a person, so any tracked information must be anonymised or generic enough. Here some examples:
    • unique user id. This would allow correlating visits from multiple devices to one single user. In theory Google Analytics already allows to track a user id and it is planned that UA to have user-centric reports. However, this is still in closed beta, so it might take some time until this is available (especially in the free version of Google analytics).
    • agegroup
    • gender
    • customer type (e.g. new/returning/excellent customer)
    • days since previous purchase
    • average basket size
    • zip code (unless you have the very precise UK zipcodes – then this information might be against the ToS)
    • number of friends

Note: For page-specific data it is often more useful to use content grouping instead of custom dimensions. Examples:

  • page author
  • page topic/category
  • publish date
  • page language
  • price range of viewed product page


That’s it for now. If you would like to share some of your custom dimensions and metrics please use the reply form below – I am looking forward to get more ideas :-)


  1. Jerry says:

    I would like to also thank you for the time you spend helping. Every time I read one of your posts I learn something.

  2. Stefan says:

    Interesting post Claudia. I am interested in your final note that page-specific content is better in a content group than as a dimension. Do you have any other posts or references that discuss this further?

    I am considering page topic, publish date, and unique ID as a Custom Dimension mainly to be used in reporting through the API. AFAIK, Content Groups cannot be reported against via the API, but would you say there are particular drawbacks to using Custom Dimensions instead?

    And finally, I wondered what you thought about tracking page-level publish date as a Custom Metric instead of a Custom Dimension. The benefit I see here is that you could then use numeric filters (greater/less than) on the YYYMMDD date values, as opposed to the text filters available for dimensions.

    Looking forward to your thoughts!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.