⟵ Writing

Using Google's Measurement Protocol in .NET

2019-06-06

Google's Measurement Protocol (MP) is a product that allows developers to make direct HTTP requests to send user interaction data to Google Analytics. If you're reading this, you probably knew that, as for some reason or another, you're not using analytics.js, the JavaScript library for interacting with Google Analytics, that most developers use instead of MP.

Benefits

  • Send data directly from the server / back-end
  • Circumvent ad-blockers that block any Google Analytics related servers and or tracking tags
  • Track data from users that have JavaScript disabled
  • Simple REST endpoint, nothing crazy to wrap your head around

However, as this is all done server side, tracking events such as button clicks or user interaction on the front-end is not possible. The reason I used it was to better track eCommerce related events through Google Analytics (Transactions, Items ect).

Interacting with MP

It's pretty simple, just send a POST to their endpoint. With data_here being the parameters you want to send.

1POST /collect HTTP/1.1
2Host: www.google-analytics.com
3
4data_here
5

NOTE: These are the required variables you must send on every hit.

NameParameterExampleDescription
Protocol Versionvv=1The protocol version. The value should be 1.
Tracking IDtidtid=UA-XXXXXX-YThe ID that distinguishes to which Google Analytics property to send data.
Hit Typett=pageviewThe type of interaction collected for a particular user. E.g pageview

So, a simple pageview hit would look like this:

1POST /collect HTTP/1.1
2Host: www.google-analytics.com
3
4v=1&tid=UA-XXXXX-Y&t=pageview
5

In order to send different types of hits, just change the Hit Type value, for the next example, I'll be using a transaction hit.

Migrating from analytics.js to MP

This section is for any developers that already have a front-end implementation of analytics.js and want to see what the equivalent data would like being sent to MP.

Let's say you've got an add transaction event that looks something like this.

1ga("ecommerce:addTransaction", {
2  id: "1234",
3  affiliation: "Big Brians Bargain Biscuits",
4  revenue: "25.99",
5  shipping: "12",
6  tax: "1.50",
7})
8

In this snippet, you've got the id of the product, the affiliation, the revenue from the sale (this should include the shipping and tax costs), the shipping cost and last of all the tax. Doing this via MP is as simple as looking up the corresponding values in their Parameter Reference and adding the values needed.

We're first going to need the required values in the table above. As well as the parameters for an addTransaction event. (Which we can find in the Parameter Reference)

NameParameterExample
Protocol Version (Required)vv=1
Tracking ID (Required)tidtid=UA-XXXXXX-Y
Hit Type (Required)tt=transaction
Transaction IDtiti=1234
Transaction Affiliationtata=Big Brians Bargain Biscuits
Transaction Revenuetrtr=25.99
Transaction Shippingtsts=12
Transaction Taxtttx=1.50
Currency Codecucu=USD

The above table turns into a POST that looks like this.

1POST /collect HTTP/1.1
2Host: www.google-analytics.com
3
4v=1&t=transaction&tid=UA-XXXXX-Y&cid=555&ti=1234&ta=Big%20Brians%20Bargain%20Biscuits&tr=25.99&ts=12&tt=1.50&cu=USD
5

Simple, yeah?

Sending hits in .NET

Alright, so now for what most of you have probably read this post for. How to do this in .NET. Well, we're going to need to create a new class, call it whatever you want, but as we're interacting with an API, we'll need an HttpClient. For the purposes of this explanation, I'll show you how to send a transaction hit with hardcoded values. First, create that HttpClient I mentioned earlier.

1private static readonly HttpClient client = new HttpClient();
2

Now, as there's a few things that won't change in every hit, we might as well set them as constants as well.

1private const string AnalyticsUrl = "http://www.google-analytics.com/collect";
2private const string ProtocolVersion = "1";
3private const string GoogleAnalyticsTrackingId = "UA-XXXXXX-Y";
4

The easiest way to get all our parameters together, is to create a Dictionary with them inside, so let's do that inside a ecommerceTransaction method. Note, I'm making this async as it'll need to send and then receive data from the endpoint.

1public async Task ecommerceTransaction()
2{
3    var parameters = new Dictionary<string, string>
4    {
5        { "v", ProtocolVersion },               // The version of MP
6        { "tid", GoogleAnalyticsTrackingId },   // The tracking code 'UA-XXXXXX-Y'
7        { "t", "transaction" },                 // The hit type, e.g a 'transaction'
8        { "ti", "1234" },                       // The order number
9        { "ta", "Big Brians Bargain Biscuits" },// The affiliation
10        { "tr", "25.99" },                      // The revenue
11        { "ts", "12" },                         // The shipping
12        { "tt",  "1.50" },                      // The tax
13        { "cu", "USD" }                         // The currency
14    };
15}
16

Now we need to encode this into something we can POST to Google.

1var transaction = new FormUrlEncodedContent(paramaters);
2

And then to POST it to Google, we'll use the HttpClient we made earlier, and post it to the AnalyticsUrl.

1await client.PostAsync(AnalyticsUrl, request);
2

Now, with everything done, here's what the class you've created should look like.

1public class MeasurementProtocol()
2{
3    private static readonly HttpClient client = new HttpClient();
4
5    private const string AnalyticsUrl = "http://www.google-analytics.com/collect";
6    private const string ProtocolVersion = "1";
7    private const string GoogleAnalyticsTrackingId = "UA-XXXXXX-Y";
8
9    public async Task ecommerceTransaction()
10    {
11        var parameters = new Dictionary<string, string>
12        {
13            { "v", ProtocolVersion },
14            { "tid", GoogleAnalyticsTrackingId },
15            { "t", "transaction" },
16            { "ti", "1234" },
17            { "ta", "Big Brians Bargain Biscuits" },
18            { "tr", "25.99" },
19            { "ts", "12" },
20            { "tt",  "1.50" },
21            { "cu", "USD" }
22        };
23
24        var transaction = new FormUrlEncodedContent(paramaters);
25        await client.PostAsync(AnalyticsUrl, request);
26    }
27}
28