Using Perl for Google Analytics API


We use Google Analytics at work, and it has always been painful to create any kind of automated reports using that data. To date I have had to setup scheduled .csv reports to myself, parse them and import the into a database. It was definitely a pain to setup and maintain, so I was thrilled to see Google finally released a API for Google Analytics. I decided to put this together to help give people a starting point for using their Google Analytics information in their Perl programs.

The first thing you will need to do is request a token from Google. This is what you will use to authenticate yourself on all data feed requests for your session.

#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
use XML::Simple;

# this sub will return the token you need to authenticate api requests
# you need to pass your ga login and password to it
sub gaGetToken {
    # arguments passed to this function
    my $user = $_[0];
    my $pass = $_[1];

    # create user agent object
    my $ua = LWP::UserAgent->new;
    $ua->agent("MyApp/0.1 ");

    # Create a request
    my $req = HTTP::Request->new(POST => 'https://www.google.com/accounts/ClientLogin');
    $req->content_type('application/x-www-form-urlencoded');
    $req->content("accountType=GOOGLE&Email=$user&Passwd=$pass&service=analytics&source=companyName-applicationName-versionID");

    # Pass request to the user agent and get a response back
    my $res = $ua->request($req);

    # declare variable
    my $token;

    # Check the outcome of the response
    if ($res->is_success) {
        # look at the result
        if ($res->content =~ m/(?<=Auth=).*/im) {
            # store token so it can be used in subsequent requests
            $token = $&;
        }
    }
    else {
        # return the error if there was a problem
        return "error: ". $res->status_line;
        die;
    }

    # return the token
    return $token;
}

# authenticate with the API to receive token
my $token = &gaGetToken('username','password');

This will give you the token you need to make other requests. Now you have the token, you can request a data feed of the analytics account information that is tied to your Google account. The way I chose to handle this is the parse the feed and return an array of account names and account numbers.

# this sub will return an array of all your ga accounts
# you need to pass your token to it
sub gaAccounts {
    # the token you passed to this sub
    my $token = $_[0];

    # create user agent object
    my $ua = LWP::UserAgent->new;
    $ua->agent("MyApp/0.1 ");

    # add authorization to headers
    my @headers = (Authorization => "GoogleLogin Auth=$token");

    # request the accounts feed
    my $res = $ua->get("https://www.google.com/analytics/feeds/accounts/default", @headers);

    # define accounts array
    my @accounts;

    # if the request was successful...
    if ($res->is_success) {
        # declare variables
        my ($content, $e);

        # this is the xml it returns
        $content = $res->content;

        # create a xml object for the response
        my $xml = new XML::Simple(KeyAttr=>[]);
        my $tree = $xml->XMLin($content);

        # iterate through each entry
        my $x = 0;
        foreach $e (@{$tree->{entry}})
        {
            # add the account to the array
            $accounts[$x][0] = $e->{title}->{content};
            $accounts[$x][1] = $e->{'dxp:tableId'};
            $x++
        }
    } else {
        # return the error if there was a problem
        return "error: ". $res->status_line;
        die;
    }

    # return the array of accounts
    return @accounts;
}

# get array of all sites in your account
my @sites = &gaAccounts($token);

# list all the sites in the array
foreach(@sites) {
    print $_->[0] ." => ". $_->[1] ."\n";
}

# going forward, we will use the first account number
my $website = $sites[0][1];

Now you have a token and account number, you have everything you need to make data feed requests for the Google Analytics information for that account. Here is a example of getting visits by day:

# this sub will return the xml from a datafeed request
# you need to pass your token to it
sub gaDataFeed {
    # arguments passed to this function
    my $url = $_[0];
    my $token = $_[1];    

    # create user agent object
    my $ua = LWP::UserAgent->new;
    $ua->agent("MyApp/0.1 ");

    # add authorization to headers
    my @headers = (Authorization => "GoogleLogin Auth=$token");

    # request page
    my $res = $ua->get($url, @headers);

    my $content;

    # if the request was successful...
    if ($res->is_success) {
        # this is the xml response
        $content = $res->content;
    } else {
        # return the error if there was a problem
        return "error: ". $res->status_line;
        die;
    }

    # return the xml
    return $content;
}

# to test, lets request visit by day
my $pageviews = &gaDataFeed("https://www.google.com/analytics/feeds/data?ids=$website&dimensions=ga%3Adate&metrics=ga%3Avisits&start-date=2009-07-27&end-date=2009-08-10&max-results=365", $token);

# create a xml object for the response
my $xml = new XML::Simple(KeyAttr=>[]);
my $tree = $xml->XMLin($pageviews);

# iterate through each entry in the xml
foreach my $e (@{$tree->{entry}})
{
    # date value
    print $e->{'dxp:dimension'}->{value};
    print " : ";
    # visitors value
    print $e->{'dxp:metric'}->{value};
    print "\n";
}

That is the basics, you can use the above to access just about anything in your Google Analytics account. There is a good query explorer that you can use to generate and test data feed request URI’s. I hope this helps you get started with Google Analytics and Perl.

  1. #1 by Alexandr Ciornii on August 11th, 2009

    new XML::Simple ()
    is better written as
    XML::Simple->new()

  2. #2 by Dan Wolfgang on September 9th, 2009

    I’m not having much luck with this. in the gaGetToken sub, the request doesn’t go through, and throws an error:
    500 Can’t connect to http://www.google.com:443 (Invalid argument)

    Any suggestions?

  3. #3 by Dan Wolfgang on September 12th, 2009

    FYI to anybody else, this error was fixed by installing the Crypt::SSLeay module.

  4. #4 by Polprav on November 2nd, 2009

    Hello from Russia!
    Can I quote a post “No teme” in your blog with the link to you?

  5. #6 by Pheobe on January 14th, 2010

    Thanks. Sooooo helpful!

  6. #7 by Patrick on January 15th, 2010

(will not be published)
Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 126 bad guys.