Click here to Skip to main content
Click here to Skip to main content
Go to top

Retrieve Google Analytics Statistics Using .NET

, 5 Nov 2010
Rate this:
Please Sign up or sign in to vote.
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.

GAConnect-Winforms2.JPG

Select_Dimensions-Metrics.JPG

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.

  1. 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 a string variable for further requests.

    GAConnect1.JPG

  2. 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.

    GAConnect2.jpg

    The GetUserProfiles calls the GetProfilesData method of the BaseClass. It basically sends an HTTP Get 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, the GetUserProfiles method calls GetProfiles. This is where our XML result will be mapped to an IEnumberable<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;
            } 
  3. 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, two enums 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. The GetAnalytics 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 the tableID shouldn't contain "ga:" and maxNumber is the maximum number of returned GAData 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 call GetAnalytics method, is that it calls the GetBaseData 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 calls getXMLData method which retrieves pure XML data (in XDocument 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 by getXMLData method, takes out the dimension/metric pairs and passes them as a BaseData type object to our GetAnalytics method. The BaseData 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 from GetBaseData will be mapped to an IEnumerable<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
    }); 

    GAConnect4.jpg

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Kamyar
Software Developer (Senior) Atitav
Iran (Islamic Republic Of) Iran (Islamic Republic Of)
Microsoft Visual Studio .NET Application Programmer, Web Developer. I'm also part of YetAnotherForum's (YAF) Development Team.

Comments and Discussions

 
Question(400) Bad Request (403) forbidden eror PinmemberMember 99988893-Aug-14 20:03 
GeneralMy vote of 3 Pinmembermahmoud wafy21-May-14 10:24 
QuestionError getting on production environment PinmemberMember 963102718-Mar-14 20:23 
AnswerRe: Error getting on production environment Pinmemberrachitkapoor1-Apr-14 20:19 
QuestionError Pinmemberteenhappy11-Nov-13 8:20 
AnswerRe: Error PinmemberMember 184536615-Jan-14 6:07 
GeneralRe: Error PinmemberMember 963102712-Mar-14 0:40 
QuestionIts giving Error 404. Pinmembersameer_s5-Sep-13 19:39 
AnswerRe: Its giving Error 404. PinprofessionalMrSadin31-Oct-13 23:09 
GeneralRe: Its giving Error 404. PinmemberShreya Nag26-Dec-13 23:39 
QuestionUnable to acces the data from Google Analytics..Error in 'GetProfilesData' method. PinmemberMember 781298911-Apr-13 20:34 
QuestionError 404 Pinmemberwinrunner12-Feb-13 20:56 
GeneralVery Nice PinmemberH_SS5-Feb-13 7:03 
GeneralVery Nice PinmemberH_SS5-Feb-13 7:01 
QuestionRecieved Error 404 PinmemberHarshanumaan29-Jan-13 4:54 
QuestionI'm getting Object reference not set to an instance of an object error. PinmemberHarpreet_12523-Jan-13 22:28 
QuestionI'm getting Object reference not set to an instance of an object. Pinmemberchinnu1111-Dec-12 20:06 
GeneralMy vote of 5 PinmemberDg!Mortal25-Nov-12 20:52 
QuestionAuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; PinmemberMember 947935023-Nov-12 0:16 
AnswerRe: AuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; PinmemberAkis Kontakis26-Nov-12 5:45 
GeneralRe: AuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; PinmemberMember 947935027-Nov-12 19:42 
Questionabout average time format Pinmemberdipangi15-Jun-12 21:10 
Questionhow to specify and use Advanced Segments of Google Analytics PinmemberMember 87274492-Apr-12 2:08 
QuestionHow to fetch Some more Information from Google Analaytics PinmemberMember 858745822-Jan-12 1:51 
QuestionWhat is the metric for unique users of the site? Pinmemberabril17-Jul-11 1:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 5 Nov 2010
Article Copyright 2010 by Kamyar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid