Retrieve Google Analytics Statistics Using .NET
This article provides a class library that can help programmers to get Google analytics data using .NET
Introduction
This article provides a class library that can help programmers to get Google analytics data using .NET. The data which is returned is of IEnumberable
class. So it can be bound to any bindable control to be viewed by the user.
The attached file contains the GAConnect DLL project along with a Winforms and Website project which you can use to retrieve your Google Analytics data.

Background
I recently launched my website and wanted to show some statistical information (e.g. pages with most views) to the user. Well, I went for the best out there: Google Analytics. The last step was to be able to connect to Google Analytics and get the data from there and show it to the user. After searching for a while, I found out that Google has changed the way it handles Google Analytics requests and unfortunately, none of the code samples worked correctly. The best thing I could find was a great blog post at mikesdotnetting.com (although it didn't work either). Major parts of my code have been borrowed from his sample. To be able to use Google Analytics Data Export API, you'd have to get the corresponding tableId
to your Analytics account. So, just knowing your trackingId
will not do the trick. So I have created a DLL for .NET developers to connect to Google Analytics more easily. It makes it easier to perform the required 3 steps (Authentication, Account Query and Profile/Report Query) to be able to retrieve your data from Google Analytics. In the following chapters, I will explain both how to use this DLL to connect to Google Analytics and also how these steps are carried out in the GAConnect DLL itself.
Using the Code
To use the code, you have to add a reference to GAConnect.dll.
- Initialize and Authenticate: Create a new object of type
GADataFetcher
. Pass in your email address and password as the parameters. This object will carry out all the necessary steps for you to be able to get the data from Google:var gaDataFetcher = new GADataFetcher(txtEmail.Text, txtPassword.Text);
The constructor tries to authenticate the user with the given credentials. If the authentication fails, it will throw an exception:
this.Email = email; this.Password = password; try { Authenticate(); BaseData.authenticationKey = AuthenticationKey; } catch { throw; }
As you can see, the authentication is handled by
Authenticate
method. The authentication URL and authentication post for Google are:private const string AuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; string AuthenticationPost = string.Format("accountType=GOOGLE&Email={0}&Passwd={1}& service=analytics&source=xxx-xxx", this.Email, this.Password);
The Authenticate URL makes a HTTP POST request which checks if the result has an
"Auth="
in it. If so, the user is authenticated and the authentication key is saved into astring
variable for further requests. - Account Query: The GAConnect DLL has a couple of classes for returning data. The first one is
GAProfile
with the following definition:
public class GAProfile { public string ID { get; set; } public DateTime Updated { get; set; } public string AccountID { get; set; } public string AccountName { get; set; } public string ProfileID { get; set; } public string WebPropertyID { get; set; } public string Currency { get; set; } public string TimeZone { get; set; } public string TableID { get; set; } public string Title { get; set; } }
Now we're ready to retrieve our account's profiles from Google. It's done by calling the following method:
IEnumerable<GAProfile> profiles = gaDataFetcher.GetUserProfiles();
The profiles object now contains everything we need to get the real data from Google Analytics, including the
TableIds
associated with our profiles.The
GetUserProfiles
calls theGetProfilesData
method of theBaseClass
. It basically sends an HTTPGet
Request to"https://www.google.com/analytics/feeds/accounts/default"
. It also adds the authentication key to the request header. Otherwise, we get a Request Forbidden result. After getting the result (the user profiles) in an XML variable, theGetUserProfiles
method callsGetProfiles
. This is where our XML result will be mapped to anIEnumberable<GAProfile>
object. The parsing/mapping is done by using lambda expressions and adding appropriate namespaces:static IEnumerable<GAProfile> GetProfiles<T>(XDocument xml) where T : GAProfile, new() { XNamespace dxp = xml.Root.GetNamespaceOfPrefix("dxp"); XNamespace dns = xml.Root.GetDefaultNamespace(); IEnumerable<GAProfile> profiles = null; profiles = xml.Root.Descendants(dns + "entry").Select(f => new GAProfile { AccountID = f.Elements(dxp + "property").Where ( x => x.Attribute("name").Value== "ga:accountId").First().Attribute("value").Value, AccountName = f.Elements(dxp + "property").Where (x => x.Attribute("name").Value == "ga:accountName").First().Attribute("value").Value, ProfileID = f.Elements(dxp + "property").Where (x => x.Attribute("name").Value == "ga:profileId").First().Attribute("value").Value, WebPropertyID = f.Elements(dxp + "property").Where (x => x.Attribute("name").Value == "ga:webPropertyId").First().Attribute("value").Value, Currency = f.Elements(dxp + "property").Where (x => x.Attribute("name").Value == "ga:currency").First().Attribute("value").Value, TimeZone = f.Elements(dxp + "property").Where (x => x.Attribute("name").Value == "ga:timezone").First().Attribute("value").Value, TableID = f.Element(dxp + "tableId").Value, Updated = DateTime.Parse(f.Element(dns + "updated").Value), ID = f.Element(dns + "id").Value, Title = f.Element(dns + "title").Value }); return profiles; }
- Profile/Report Query: The second class I mentioned before is
GAData
:public class GAData { public int Pageviews { get; set; } public int Bounces { get; set; } public int Entrances { get; set; } public int Exits { get; set; } public int NewVisits { get; set; } public double TimeOnPage { get; set; } public double TimeOnSite { get; set; } public int Visitors { get; set; } public int Visits { get; set; } public int UniquePageviews { get; set; } public string ExitPagePath { get; set; } public string LandingPagePath { get; set; } public string NextPagePath { get; set; } public string PagePath { get; set; } public string PageTitle { get; set; } public string PreviousPagePath { get; set; } public string SecondPagePath { get; set; } public string Browser { get; set; } public string BrowserVersion { get; set; } public string City { get; set; } public string ConnectionSpeed { get; set; } public string Country { get; set; } public string Date { get; set; } public string DaysSinceLastVisit { get; set; } public string Day { get; set; } public string FlashVersion { get; set; } public string Hostname { get; set; } public string IsMobile { get; set; } public string Hour { get; set; } public string JavaEnabled { get; set; } public string Language { get; set; } public string Latitude { get; set; } public string Longitude { get; set; } public string Month { get; set; } public string NetworkDomain { get; set; } public string NetworkLocation { get; set; } public string OperatingSystem { get; set; } public string OperatingSystemVersion { get; set; } public string PageDepth { get; set; } public string Region { get; set; } public string ScreenColors { get; set; } public string ScreenResolution { get; set; } public string SubContinent { get; set; } public string UserDefinedValue { get; set; } public int VisitCount { get; set; } public int VisitLength { get; set; } public string VisitorType { get; set; } public int Week { get; set; } public int Year { get; set; } public string Source { get; set; } }
The
GAData
objects contain the real statistics about our pages. To set the dimensions and metrics that we want to retrieve, twoenum
s are defined:public enum Dimension { exitPagePath, landingPagePath, nextPagePath, pagePath, pageTitle, previousPagePath, secondPagePath, browser, browserVersion, city, connectionSpeed, country, date, daysSinceLastVisit, day, flashVersion, hostname, isMobile, hour, javaEnabled, language, latitude, longitude, month, networkDomain, networkLocation, operatingSystem, operatingSystemVersion, pageDepth, region, screenColors, screenResolution, subContinent, userDefinedValue, visitCount, visitLength, visitorType, week, year, source, } public enum Metric { bounces, entrances, exits, newVisits, pageviews, timeOnPage, timeOnSite, visitors, visits, uniquePageviews }
To get the final results, first define what dimensions/metrics you're interested in and then call the
GetAnalytics
method. TheGetAnalytics
method's definition is as follows:public IEnumerable<GAData> GetAnalytics(string tableID, DateTime from, DateTime to, int max, List<Dimension> dimensions, List<Metric> metrics, Metric sort, SortDirection order)
tableID
can be retrieved from the previous section. notice that thetableID
shouldn't contain "ga:
" andmaxNumber
is the maximum number of returnedGAData
Items. For example, if you want to get the statistics for the last 30 days, you should do something like:var dimensions = new List<Dimension>(); var metrics = new List<Metric>(); dimensions.Add(Dimension.pagePath); dimensions.Add(Dimention.pageTitle); metrics.Add(Metric.pageviews); var fromDate=DateTime.Now.AddDays(-30); var toDate=DateTime.Now; IEnumerable<GAData> data = gaDataFetcher.GetAnalytics (myTableId, fromDate, toDate, 10, dimensions, metrics, metrics[0], GAConnect.SortDirection.Descending);
An important thing to note here is that not all dimension/metric combinations are allowed. If you provide an incorrect dimension/metric combination, you'll get Http 400 (Bad Request) exception. To view the valid combinations, check out this link.
Now, what happens behind the scenes when you callGetAnalytics
method, is that it calls theGetBaseData
method:public static IEnumerable<BaseData> GetBaseData (string tableID, IEnumerable<Dimension> dimensions, IEnumerable<Metric> metrics, DateTime from, DateTime to, Metric sort, SortDirection direction, int maxrecords)
The
GetBaseData
method, itself callsgetXMLData
method which retrieves pure XML data (inXDocument
format) from Google.private static XDocument getXMLData (string tableID, IEnumerable<Dimension> dimensions, IEnumerable<Metric> metrics, DateTime from, DateTime to, Metric sort, SortDirection direction, int maxrecords)
The
GetBaseData
method parses the XML retrieved bygetXMLData
method, takes out the dimension/metric pairs and passes them as aBaseData
type object to ourGetAnalytics
method. TheBaseData
class's definition is as follows:public class BaseData { public IEnumerable<KeyValuePair<Dimension, string>> Dimensions { get; set; } public IEnumerable<KeyValuePair<Metric, string>> Metrics { get; set; }
Now we have our data in an
IEnumerable<BaseData>
object. By using lambda expressions, the returned object fromGetBaseData
will be mapped to anIEnumerable<GAData>
object and will be returned to the user:return data.Select(d => new GAData { Pageviews = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.pageviews).Value), Bounces = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.bounces).Value), Entrances = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.entrances).Value), Exits = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.exits).Value), NewVisits = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.newVisits).Value), TimeOnPage = Convert.ToDouble(d.Metrics.FirstOrDefault (met => met.Key == Metric.timeOnPage).Value), TimeOnSite = Convert.ToDouble(d.Metrics.FirstOrDefault (met => met.Key == Metric.timeOnSite).Value), Visitors = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.visits).Value), Visits = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.pageviews).Value), UniquePageviews = Convert.ToInt32(d.Metrics.FirstOrDefault (met => met.Key == Metric.uniquePageviews).Value), ExitPagePath = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.exitPagePath).Value, LandingPagePath = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.landingPagePath).Value, NextPagePath = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.nextPagePath).Value, PagePath = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.pagePath).Value, PageTitle = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.pageTitle).Value, PreviousPagePath = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.previousPagePath).Value, SecondPagePath = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.secondPagePath).Value, Browser = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.browser).Value, BrowserVersion = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.browserVersion).Value, City = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.city).Value, ConnectionSpeed = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.connectionSpeed).Value, Country = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.country).Value, Date = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.date).Value, DaysSinceLastVisit = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.daysSinceLastVisit).Value, Day = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.day).Value, FlashVersion = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.flashVersion).Value, Hostname = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.hostname).Value, IsMobile = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.isMobile).Value, Hour = d.Dimensions.FirstOrDefault(dim => dim.Key == Dimension.hour).Value, JavaEnabled = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.javaEnabled).Value, Language = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.language).Value, Latitude = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.latitude).Value, Longitude = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.longitude).Value, Month = d.Dimensions.FirstOrDefault(dim => dim.Key == Dimension.month).Value, NetworkDomain = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.networkDomain).Value, NetworkLocation = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.networkLocation).Value, OperatingSystem = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.operatingSystem).Value, OperatingSystemVersion = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.operatingSystemVersion).Value, PageDepth = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.pageDepth).Value, Region = d.Dimensions.FirstOrDefault(dim => dim.Key == Dimension.region).Value, ScreenColors = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.screenColors).Value, ScreenResolution = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.screenResolution).Value, SubContinent = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.subContinent).Value, UserDefinedValue = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.userDefinedValue).Value, VisitCount = Convert.ToInt32(d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.visitCount).Value), VisitLength = Convert.ToInt32(d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.visitLength).Value), VisitorType = d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.visitorType).Value, Week = Convert.ToInt32(d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.week).Value), Year = Convert.ToInt32(d.Dimensions.FirstOrDefault (dim => dim.Key == Dimension.year).Value), Source = d.Dimensions.FirstOrDefault(dim => dim.Key == Dimension.source).Value });
Points of Interest
Using Google Analytics Data API is fun and I'd recommend you to surf the code more thoroughly and see what's going on under the hood. If you have any questions, post your questions here and I'd be glad to help.