Click here to Skip to main content
15,886,137 members
Articles / Programming Languages / XML

Retrieve Google Analytics Statistics Using .NET

Rate me:
Please Sign up or sign in to vote.
4.80/5 (14 votes)
5 Nov 2010CPOL4 min read 120.5K   3.6K   39   65
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:

    C#
    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:

    C#
    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:

    C#
    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:
    C#
    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:

    C#
    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:

    C#
    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:
    C#
    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:

    C#
    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:

    C#
    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:

    C#
    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:

    C#
    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.

    C#
    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:

    C#
    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:

    C#
    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)


Written By
Chief Technology Officer Banicomm
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

 
Questionauth= not found Pin
deepak2190328-Jan-20 22:56
deepak2190328-Jan-20 22:56 
QuestionGoogle Analytics Pin
manish_sanandiya1-Mar-15 23:21
manish_sanandiya1-Mar-15 23:21 
GeneralArticle needs to be updated Pin
Abhishek Pant13-Oct-14 20:54
professionalAbhishek Pant13-Oct-14 20:54 
Question(400) Bad Request (403) forbidden eror Pin
Member 99988893-Aug-14 20:03
professionalMember 99988893-Aug-14 20:03 
GeneralMy vote of 3 Pin
mahmoud wafy21-May-14 10:24
mahmoud wafy21-May-14 10:24 
QuestionError getting on production environment Pin
Member 963102718-Mar-14 20:23
Member 963102718-Mar-14 20:23 
AnswerRe: Error getting on production environment Pin
rachitkapoor1-Apr-14 20:19
professionalrachitkapoor1-Apr-14 20:19 
QuestionError Pin
teenhappy11-Nov-13 8:20
teenhappy11-Nov-13 8:20 
AnswerRe: Error Pin
Member 184536615-Jan-14 6:07
Member 184536615-Jan-14 6:07 
GeneralRe: Error Pin
Member 963102712-Mar-14 0:40
Member 963102712-Mar-14 0:40 
QuestionIts giving Error 404. Pin
Sam_IN5-Sep-13 19:39
Sam_IN5-Sep-13 19:39 
AnswerRe: Its giving Error 404. Pin
MrSadin31-Oct-13 23:09
professionalMrSadin31-Oct-13 23:09 
GeneralRe: Its giving Error 404. Pin
Shreya Nag26-Dec-13 23:39
Shreya Nag26-Dec-13 23:39 
QuestionUnable to acces the data from Google Analytics..Error in 'GetProfilesData' method. Pin
Member 781298911-Apr-13 20:34
Member 781298911-Apr-13 20:34 
QuestionError 404 Pin
winrunner12-Feb-13 20:56
winrunner12-Feb-13 20:56 
GeneralVery Nice Pin
S.H.Yadegari5-Feb-13 7:03
S.H.Yadegari5-Feb-13 7:03 
GeneralVery Nice Pin
S.H.Yadegari5-Feb-13 7:01
S.H.Yadegari5-Feb-13 7:01 
QuestionRecieved Error 404 Pin
Harshanumaan29-Jan-13 4:54
Harshanumaan29-Jan-13 4:54 
Hi , i'm receiving an error stating "The remote server returned an error:(404)NOT FOUND " i 've changed the api to Google Anlytics API version v3,now please tell me why i'm recieving such error.
please help me to get rid off such a situation thanks
QuestionI'm getting Object reference not set to an instance of an object error. Pin
Harpreet_12523-Jan-13 22:28
Harpreet_12523-Jan-13 22:28 
QuestionI'm getting Object reference not set to an instance of an object. Pin
chinnu1111-Dec-12 20:06
chinnu1111-Dec-12 20:06 
GeneralMy vote of 5 Pin
Dg!Mortal25-Nov-12 20:52
Dg!Mortal25-Nov-12 20:52 
QuestionAuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; Pin
Member 947935023-Nov-12 0:16
Member 947935023-Nov-12 0:16 
AnswerRe: AuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; Pin
Akis Kontakis26-Nov-12 5:45
Akis Kontakis26-Nov-12 5:45 
GeneralRe: AuthenticationUrl = "https://www.google.com/accounts/ClientLogin"; Pin
Member 947935027-Nov-12 19:42
Member 947935027-Nov-12 19:42 
Questionabout average time format Pin
dipangi15-Jun-12 21:10
dipangi15-Jun-12 21:10 

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

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