Use Google Analytics API to show information in C#

49,632

Solution 1

I did a lot of search and finally either looking up code from multiple places and then wrapping my own interface around it i came up with the following solution. Not sure if people paste their whole code here, but i guess why not save everyone else time :)

Pre-requisites, you will need to install Google.GData.Client and google.gdata.analytics package/dll.

This is the main class that does the work.

namespace Utilities.Google
{
    public class Analytics
    {
        private readonly String ClientUserName;
        private readonly String ClientPassword;
        private readonly String TableID;
        private AnalyticsService analyticsService;

        public Analytics(string user, string password, string table)
        {
            this.ClientUserName = user;
            this.ClientPassword = password;
            this.TableID = table;

            // Configure GA API.
            analyticsService = new AnalyticsService("gaExportAPI_acctSample_v2.0");
            // Client Login Authorization.
            analyticsService.setUserCredentials(ClientUserName, ClientPassword);
        }

        /// <summary>
        /// Get the page views for a particular page path
        /// </summary>
        /// <param name="pagePath"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="isPathAbsolute">make this false if the pagePath is a regular expression</param>
        /// <returns></returns>
        public int GetPageViewsForPagePath(string pagePath, DateTime startDate, DateTime endDate, bool isPathAbsolute = true)
        {
            int output = 0;

            // GA Data Feed query uri.
            String baseUrl = "https://www.google.com/analytics/feeds/data";

            DataQuery query = new DataQuery(baseUrl);
            query.Ids = TableID;
            //query.Dimensions = "ga:source,ga:medium";
            query.Metrics = "ga:pageviews";
            //query.Segment = "gaid::-11";
            var filterPrefix = isPathAbsolute ? "ga:pagepath==" : "ga:pagepath=~";
            query.Filters = filterPrefix + pagePath;
            //query.Sort = "-ga:visits";
            //query.NumberToRetrieve = 5;
            query.GAStartDate = startDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
            query.GAEndDate = endDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
            Uri url = query.Uri;
            DataFeed feed = analyticsService.Query(query);
            output = Int32.Parse(feed.Aggregates.Metrics[0].Value);

            return output;
        }

        public Dictionary<string, int> PageViewCounts(string pagePathRegEx, DateTime startDate, DateTime endDate)
        {
            // GA Data Feed query uri.
            String baseUrl = "https://www.google.com/analytics/feeds/data";

            DataQuery query = new DataQuery(baseUrl);
            query.Ids = TableID;
            query.Dimensions = "ga:pagePath";
            query.Metrics = "ga:pageviews";
            //query.Segment = "gaid::-11";
            var filterPrefix = "ga:pagepath=~";
            query.Filters = filterPrefix + pagePathRegEx;
            //query.Sort = "-ga:visits";
            //query.NumberToRetrieve = 5;
            query.GAStartDate = startDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
            query.GAEndDate = endDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
            Uri url = query.Uri;
            DataFeed feed = analyticsService.Query(query);

            var returnDictionary = new Dictionary<string, int>();
            foreach (var entry in feed.Entries)
                returnDictionary.Add(((DataEntry)entry).Dimensions[0].Value, Int32.Parse(((DataEntry)entry).Metrics[0].Value));

            return returnDictionary;
        }
    }
}

And this is the interface and implementation that i use to wrap it up with.

namespace Utilities
{
    public interface IPageViewCounter
    {
        int GetPageViewCount(string relativeUrl, DateTime startDate, DateTime endDate, bool isPathAbsolute = true);
        Dictionary<string, int> PageViewCounts(string pagePathRegEx, DateTime startDate, DateTime endDate);
    }

    public class GooglePageViewCounter : IPageViewCounter
    {
        private string GoogleUserName
        {
            get
            {
                return ConfigurationManager.AppSettings["googleUserName"];
            }
        }

        private string GooglePassword
        {
            get
            {
                return ConfigurationManager.AppSettings["googlePassword"];
            }
        }

        private string GoogleAnalyticsTableName
        {
            get
            {
                return ConfigurationManager.AppSettings["googleAnalyticsTableName"];
            }
        }

        private Analytics analytics;

        public GooglePageViewCounter()
        {
            analytics = new Analytics(GoogleUserName, GooglePassword, GoogleAnalyticsTableName);
        }

        #region IPageViewCounter Members

        public int GetPageViewCount(string relativeUrl, DateTime startDate, DateTime endDate, bool isPathAbsolute = true)
        {
            int output = 0;
            try
            {
                output = analytics.GetPageViewsForPagePath(relativeUrl, startDate, endDate, isPathAbsolute);
            }
            catch (Exception ex)
            {
                Logger.Error(ex);
            }

            return output;
        }

        public Dictionary<string, int> PageViewCounts(string pagePathRegEx, DateTime startDate, DateTime endDate)
        {
            var input = analytics.PageViewCounts(pagePathRegEx, startDate, endDate);
            var output = new Dictionary<string, int>();

            foreach (var item in input)
            {
                if (item.Key.Contains('&'))
                {
                    string[] key = item.Key.Split(new char[] { '?', '&' });
                    string newKey = key[0] + "?" + key.FirstOrDefault(k => k.StartsWith("p="));

                    if (output.ContainsKey(newKey))
                        output[newKey] += item.Value;
                    else
                        output[newKey] = item.Value;
                }
                else
                    output.Add(item.Key, item.Value);
            }
            return output;
        }

        #endregion
    }
}

And now the rest is the obvious stuff - you will have to add the web.config values to your application config or webconfig and call IPageViewCounter.GetPageViewCount

Solution 2

It requires a bit of setup on the google side but it's actually quite simple. I will list step by step.

First you will need to create an application in the Google cloud console and enable the Analytics API.

  • Go to http://code.google.com/apis/console
  • Select the drop down and create a project if you do not already have one
  • Once the project is created click on services
  • From here enable the Analytics API

Now that the Analytics API is enabled the next step will be to enable a service account to access your desired analytics profiles/sites. The service account will allow you to log in without having to prompt a user for credentials.

  • Go to http://code.google.com/apis/console and choose the project you created from the drop down.
  • Next go to the "API Access" section and click the "Create another client id" button.
  • In the Create Client ID window choose service account and click create client id.
  • Download the public key for this account if it doesn't start the download automatically.You will need this later on when you code for authorization.
  • Before exiting copy the service accounts auto generated email address as you will need this in the next step. The client email looks like @developer.gserviceaccount.com

Now that we have a service account you will need to allow this service account to access to your profiles/sites in Google Analytics.

  • Log into Google Analytics.
  • Once logged in click on the Admin button to the bottem left on the screen.
  • In Admin click the account drop down and select the account/site you would like your service account to be able to access then click on "User Management" under the account section.
  • Enter the email address that was generated for your service account and give it read and analyze permission.
  • Repeat these steps for any other account/site you would like your service to have access to.

Now that the setup is done for the service account to access Google Analytics through the API we can start to code.

Get this package from NuGet:

Google.Apis.Analytics.v3 Client Library

Add these usings:

using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Services;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using System.Collections.Generic; 
using System.Linq;

Some things to note are.

  • The keyPath is the path to the key file you downloaded with a .p12 file extention.
  • The accountEmailAddress is the api email we got earlier.
  • Scope is an Enum in the Google.Apis.Analytics.v3.AnalyticService class that dictates the url to use in order to authorize (ex: AnalyticsService.Scope.AnalyticsReadonly ).
  • Application name is a name of your choosing that tells the google api what is accessing it (aka: it can be what ever you choose).

Then the code to do some basic calls is as follows.

public class GoogleAnalyticsAPI
{
    public AnalyticsService Service { get; set; }

    public GoogleAnalyticsAPI(string keyPath, string accountEmailAddress)
    {
        var certificate = new X509Certificate2(keyPath, "notasecret", X509KeyStorageFlags.Exportable);

        var credentials = new ServiceAccountCredential(
           new ServiceAccountCredential.Initializer(accountEmailAddress)
           {
               Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
           }.FromCertificate(certificate));

        Service = new AnalyticsService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentials,
                ApplicationName = "WorthlessVariable"
            });
    }

    public AnalyticDataPoint GetAnalyticsData(string profileId, string[] dimensions, string[] metrics, DateTime startDate, DateTime endDate)
    {
        AnalyticDataPoint data = new AnalyticDataPoint();
        if (!profileId.Contains("ga:"))
            profileId = string.Format("ga:{0}", profileId);

        //Make initial call to service.
        //Then check if a next link exists in the response,
        //if so parse and call again using start index param.
        GaData response = null;
        do
        {
            int startIndex = 1;
            if (response != null && !string.IsNullOrEmpty(response.NextLink))
            {
                Uri uri = new Uri(response.NextLink);
                var paramerters = uri.Query.Split('&');
                string s = paramerters.First(i => i.Contains("start-index")).Split('=')[1];
                startIndex = int.Parse(s);
            }

            var request = BuildAnalyticRequest(profileId, dimensions, metrics, startDate, endDate, startIndex);
            response = request.Execute();
            data.ColumnHeaders = response.ColumnHeaders;
            data.Rows.AddRange(response.Rows);

        } while (!string.IsNullOrEmpty(response.NextLink));

        return data;
    }

    private DataResource.GaResource.GetRequest BuildAnalyticRequest(string profileId, string[] dimensions, string[] metrics,
                                                                        DateTime startDate, DateTime endDate, int startIndex)
    {
        DataResource.GaResource.GetRequest request = Service.Data.Ga.Get(profileId, startDate.ToString("yyyy-MM-dd"),
                                                                            endDate.ToString("yyyy-MM-dd"), string.Join(",", metrics));
        request.Dimensions = string.Join(",", dimensions);
        request.StartIndex = startIndex;
        return request;
    }

    public IList<Profile> GetAvailableProfiles()
    {
        var response = Service.Management.Profiles.List("~all", "~all").Execute();
        return response.Items;
    }

    public class AnalyticDataPoint
    {
        public AnalyticDataPoint()
        {
            Rows = new List<IList<string>>();
        }

        public IList<GaData.ColumnHeadersData> ColumnHeaders { get; set; }
        public List<IList<string>> Rows { get; set; }
    }
}

Other Links that will prove helpful:

Analytic API Explorer - Query API From The Web

Analytic API Explorer version 2 - Query API From The Web

Dimensions and Metrics Reference

Hopefully this helps someone trying to do this in the future.

Solution 3

This answer is for those of you who want access to your own Analytics account and want to use the new Analytics Reporting API v4.

I recently wrote a blog post about how to get Google Analytics data using C#. Read there for all the details.

You first need to choose between connecting with OAuth2 or a Service Account. I'll assume you own the Analytics account, so you need to create a "Service account key" from the Google APIs Credentials page.

Once you create that, download the JSON file and put it in your project (I put mine in my App_Data folder).

Next, install the Google.Apis.AnalyticsReporting.v4 Nuget package. Also install Newtonsoft's Json.NET.

Include this class somewhere in your project:

public class PersonalServiceAccountCred
{
    public string type { get; set; }
    public string project_id { get; set; }
    public string private_key_id { get; set; }
    public string private_key { get; set; }
    public string client_email { get; set; }
    public string client_id { get; set; }
    public string auth_uri { get; set; }
    public string token_uri { get; set; }
    public string auth_provider_x509_cert_url { get; set; }
    public string client_x509_cert_url { get; set; }
}

And here's what you've been waiting for: a full example!

string keyFilePath = Server.MapPath("~/App_Data/Your-API-Key-Filename.json");
string json = System.IO.File.ReadAllText(keyFilePath);

var cr = JsonConvert.DeserializeObject(json);

var xCred = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(cr.client_email)
{
    Scopes = new[] {
        AnalyticsReportingService.Scope.Analytics
    }
}.FromPrivateKey(cr.private_key));

using (var svc = new AnalyticsReportingService(
    new BaseClientService.Initializer
    {
        HttpClientInitializer = xCred,
        ApplicationName = "[Your Application Name]"
    })
)
{
    // Create the DateRange object.
    DateRange dateRange = new DateRange() { StartDate = "2017-05-01", EndDate = "2017-05-31" };

    // Create the Metrics object.
    Metric sessions = new Metric { Expression = "ga:sessions", Alias = "Sessions" };

    //Create the Dimensions object.
    Dimension browser = new Dimension { Name = "ga:browser" };

    // Create the ReportRequest object.
    ReportRequest reportRequest = new ReportRequest
    {
        ViewId = "[A ViewId in your account]",
        DateRanges = new List() { dateRange },
        Dimensions = new List() { browser },
        Metrics = new List() { sessions }
    };

    List requests = new List();
    requests.Add(reportRequest);

    // Create the GetReportsRequest object.
    GetReportsRequest getReport = new GetReportsRequest() { ReportRequests = requests };

    // Call the batchGet method.
    GetReportsResponse response = svc.Reports.BatchGet(getReport).Execute();
}

We start by deserializing the service account key information from the JSON file and convert it to a PersonalServiceAccountCred object. Then, we create the ServiceAccountCredential and connect to Google via the AnalyticsReportingService. Using that service, we then prepare some basic filters to pass to the API and send off the request.

It's probably best to set a breakpoint on the line where the response variable is declared, press F10 once, then hover over the variable, so you can see what data is available for you to use in the response.

Solution 4

I was hoping just to add a comment to the answer for v3 Beta, but rep points prevent that. However, I thought it would be nice for others to have this information so here it is:

using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Services;

These name spaces are used throughout the code in that post. I always wish people would post name spaces more often, I seem to spend a good bit of time looking for them. I hope this saves some people a few minutes of work.

Solution 5

I've setup something pretty similar to the above answer in a nuGet package. It does the following: - connects to a "Service Account" you set up in the API Console - Pulls any Google Analytics data you would like - Displays that data using Google's Charts API and it does all of this in a very easy to modify way. You can see more here: https://www.nuget.org/packages/GoogleAnalytics.GoogleCharts.NET/.

Share:
49,632
VinnyG
Author by

VinnyG

Owner and developper @ www.spektrummedia.com

Updated on April 06, 2020

Comments

  • VinnyG
    VinnyG about 4 years

    I been looking for a good solution all day but google evolve so fast that I can't find something working. What I want to do is that, I have a Web app that has an admin section where user need to be logged in to see the information. In this section I want to show some data from GA, like pageviews for some specific urls. Since it's not the user information that I'm showing but the google analytics'user I want to connect passing information (username/password or APIKey) but I can't find out how. All the sample I found use OAuth2 (witch, if I understand, will ask the visitor to log in using google).

    What I found so far :

    Maybe I'm just tired and that tomorrow it will be easy to find a solution but right now I need help!

    Thanks

  • VinnyG
    VinnyG about 12 years
    Thanks for the help! To download the dll : code.google.com/p/google-gdata note that there's also GOOD sample in .net!
  • VinnyG
    VinnyG about 12 years
    Do I need to activate API access or something else?
  • MoXplod
    MoXplod about 12 years
    No, i dont remember doing anything for that, but please mention it if you did.
  • craastad
    craastad over 10 years
    AMAZING post! The holy grail after my day of swimming in Google .NET API soup. Were you using Google.Apis.Oauth2.v2 by any chance? I'll try it out once I get my email address permission added. XD
  • LiquaFoo
    LiquaFoo over 10 years
    The OAuth2Authenticator is actually from Google APIs OAuth2 Client Library.
  • craastad
    craastad over 10 years
    I asked, because i'd like to know which Nuget package you downloaded (and add links in the response).
  • LiquaFoo
    LiquaFoo over 10 years
    @craastad the package is called Google APIs OAuth2 Client Library if you are searching within visual studio. If you want to download from NuGets site you can get it here: nuget.org/packages/Google.Apis.Authentication/1.5.0-beta
  • Elan Hasson
    Elan Hasson over 10 years
    Nice work. Thank you for wading through the docs and presenting this.
  • WhoAmI
    WhoAmI about 10 years
    Nice. What changees did you make in the web.config file and what data exactly are you displaying, total pageviews and not visits? Thx
  • WhoAmI
    WhoAmI about 10 years
    Thanks. What is public GoogleAnalyticsAPI(ConnectionInfo info)? It cant find ConnectionInfo namespace.. I don't use AUTH to access as i dont log in. I use: var credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(serviceAccountEmail) { Scopes = new[] { AnalyticsService.Scope.Analytics } }.FromCertificate(certificate));
  • LiquaFoo
    LiquaFoo about 10 years
    ConnectionInfo is simply an object I created that gets populated at a higher level with the PrivateKeyPath, ServiceAccountEmail, and ApplicationName. Thus making it easier to scale this out to other google apis if necessary.
  • Ian Davis
    Ian Davis about 10 years
    A couple other using's that are needed: using System.Collections.Generic; using System.Linq; for the .First piece of code.
  • Max Favilli
    Max Favilli almost 10 years
    This should be the accepted answer. And you saved my night!
  • Olaj
    Olaj over 9 years
    Awesome! Stackoverflow always to the rescue when Google / Facebook and so on has the worst documentation ever. Thanks!
  • Aliasgar Rajpiplawala
    Aliasgar Rajpiplawala about 9 years
    Hi i am using the above code and I am getting an error as bad request I did everything that is mentioned. Please suggest
  • LiquaFoo
    LiquaFoo about 9 years
    @AliasgarRajpiplawala whats the whole error it could be that the request isn't authenticating properly. Hard to tell with out knowing the exact error.
  • anna
    anna about 9 years
    @LiquaFoo I am also getting an error: An unhandled exception of type 'Google.Apis.Auth.OAuth2.Responses.TokenResponseException' occurred in Google.Apis.dll Additional information: Error:"invalid_grant", Description:"", Uri:"" I am using the service e mail and poiting to my p12 key so not sure what I m doing wrong. any help appreciated!
  • LiquaFoo
    LiquaFoo about 9 years
    @anna It's hard to say exactly what causing the error due to it being so vauge, but google addresses invalid grant errors in their docs and what to check for. Take a look at the Invalid Grant section at the bottom of this link. developers.google.com/analytics/devguides/reporting/core/v2/‌​…
  • Liam
    Liam almost 9 years
    This helped me a lot, have 50 rep... :)
  • Arpita
    Arpita over 8 years
    Really helpful. Thanks a lot.
  • Temp O'rary
    Temp O'rary over 8 years
    This is helpful! I'm facing some issue and was hoping if someone could help me here: stackoverflow.com/q/34330721/2642351
  • Nash
    Nash about 8 years
  • Radmation
    Radmation about 8 years
    @LoganG. I know this is an old thread but I have one question. The keyPath portion, are you saying to change the file extension from .json to .p12 for the downloaded json file? And make sure my app can access it?
  • Lee Englestone
    Lee Englestone almost 8 years
    What is 'table' in this context?
  • LiquaFoo
    LiquaFoo almost 8 years
    @Radmation sorry for the late response. This answer was actually edited and use to say this: "The Private key path is the path to the key file you downloaded when you created your service account. This will be a .p12 file extention." With that said you should't have to change any file extension. Take a look at the second bolded section and it describes how to get this key.
  • Hassan Faghihi
    Hassan Faghihi almost 8 years
    "API Access" is missing, and is exists sections, i don't see "Create another client id" button...
  • LiquaFoo
    LiquaFoo almost 8 years
    @deadManN Yea they have update the UI since this was written. I believe they have whats called API Manager now that lets you turn on certain API's. Check for analytics or reporting analytics and turn it on then you'll get a warning stating you need to setup up credentials with a link to do so.
  • Hassan Faghihi
    Hassan Faghihi almost 8 years
    depend on the way you start it, i saw many way to create the key, and that's what's on my nerve, which is best for what... what they provide what not,... what kind of access i need for analystics, etc. those are the question in my mind...
  • osullivj
    osullivj about 7 years
    @LoganG: thanks for a truly excellent answer. I've posted your code, with acknowledgements of course, here: github.com/SpreadServe/ganalytics
  • Nash Carp
    Nash Carp over 6 years
    Sorry but this is absolutely not a full example. To begin with: where do I put the second block of code and what is the data that we are going to use/show etc.
  • John Washam
    John Washam over 6 years
    The second block of code goes wherever you want to retrieve data from a Google API. Since my project was an ASP.NET MVC project, I put it in a Controller Action. You can put it wherever you want. As I wrote in the last paragraph, put a breakpoint on the line with the response variable, and when it is hit, put your mouse over it to see what properties it has, and you can build your code to pull out the data you need.
  • Nash Carp
    Nash Carp over 6 years
    I have followed this guide: markwemekamp.com/blog/c/…
  • Phil
    Phil over 6 years
    I think this solution is sub-optimal since you've posted the secret token in plain view in the web page! That's fine if you own the google analytics data and the only person able to access the web page but if it's share dI wouldn't have thought that's a good idea.