I've blogged a few times now about Google's Analytics Embed library. I really dig it as it makes working with their Analytics API pretty darn simple. It really opens up the ability to create interesting mashups and custom dashboards on your sites with just plain JavaScript. Recently, a reader asked me a question about how he could have more control over the data displayed by the library.
His question involved formatting of the "TABLE" display supported by the library. This is a format I had not used myself yet so I built up a quick demo (and as the reader supplied some code it made it a bit easier). Let's look at the code and then I'll show the result.
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
</head>
<body>
<!-- Step 1: Create the containing elements. -->
<section id="auth-button"></section>
<h2>RaymondCamden.com</h2>
<section id="timeline"></section>
<!-- Step 2: Load the library. -->
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
gapi.analytics.ready(function() {
// Step 3: Authorize the user.
var CLIENT_ID = '818125206534-g1r0datdtu9serq2pf9cp5vkuih3h8pv.apps.googleusercontent.com';
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: CLIENT_ID,
userInfoLabel:""
});
// Step 5: Create the timeline chart.
var timeline = new gapi.analytics.googleCharts.DataChart({
reportType: 'ga',
query: {
'dimensions': 'ga:date',
'metrics': 'ga:sessions,ga:avgSessionDuration',
'start-date': '30daysAgo',
'end-date': 'yesterday',
'ids': "ga:31405"
},
chart: {
type: 'TABLE',
container: 'timeline'
}
});
gapi.analytics.auth.on('success', function(response) {
//hide the auth-button
document.querySelector("#auth-button").style.display='none';
timeline.execute();
});
});
</script>
</body>
</html>
I'm assuming you are somewhat familiar with my older posts, but if not, the basic idea here is that the Embed library will handle authentication and it provides rendering capabilities. You can see the DataChart call there handling both a query (what data to fetch) and how to render it (a table). Here is the result:

Nice, but what if you wanted more control over the rendering? Specifically, the user wanted to change the seconds column into a report that showed the minutes instead. Unfortunately, you don't get the ability to modify the format of the table. Fortunately, Google makes it easy to get the data manually and do whatever the heck you want. Let's look at an updated version of the script.
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="moment.min.js"></script>
</head>
<body>
<div class="container">
<section id="auth-button"></section>
<h2>RaymondCamden.com</h2>
<div id="status"></div>
<table id="dataTable" class="table">
<thead><tr><th>Date</th><th>Sessions</th><th>Avg. Session Duration</th></tr></thread>
<tbody></tbody>
</table>
</div>
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
var $tbody;
var $status;
var intl = false;
$(document).ready(function() {
if(window.Intl) intl = true;
$tbody = $("#dataTable tbody");
$status = $("#status");
$status.html("<i>Please stand by - loading data...</i>");
gapi.analytics.ready(function() {
var CLIENT_ID = '818125206534-g1r0datdtu9serq2pf9cp5vkuih3h8pv.apps.googleusercontent.com';
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: CLIENT_ID,
userInfoLabel:""
});
gapi.analytics.auth.on('success', function(response) {
//hide the auth-button
document.querySelector("#auth-button").style.display='none';
gapi.client.analytics.data.ga.get({
'ids': 'ga:31405',
'metrics': 'ga:sessions,ga:avgSessionDuration',
'start-date': '30daysAgo',
'end-date': 'yesterday',
'dimensions':'ga:date'
}).execute(function(results) {
$status.html("");
renderData(results.rows);
});
});
});
});
function formatDate(str) {
var year = str.substring(0,4);
var month = str.substring(4,6);
var day = str.substring(6,8);
if(intl) {
var d = new Date(year,month-1,day);
return new Intl.DateTimeFormat().format(d);
}
return month + "/" + day + "/" + year;
}
function formatNumber(str) {
if(intl) return new Intl.NumberFormat().format(str);
return str;
}
function formatTime(str) {
var dur = moment.duration(parseInt(str,10), 'seconds');
var minutes = dur.minutes();
var seconds = dur.seconds();
return minutes + " minutes and " + seconds + " seconds";
}
function renderData(data) {
var s = "";
for(var i=0;i<data.length;i++) {
s += "<tr><td>" + formatDate(data[i][0]) + "</td>";
s += "<td>" + formatNumber(data[i][1]) + "</td>";
s += "<td>" + formatTime(data[i][2]) + "</td></tr>";
}
$tbody.html(s);
}
</script>
</body>
</html>
The biggest change here is that now I ask for the data so that I can do whatever I want with it. You can see this in the gapi.client.analytics.data.ga.get
call. Once I have the data, I'm free to format it as I want. I decided to get a bit fancy.
First, I made use of Intl to format the dates and numbers nicely. This is a cool web standard (see my article here - Working with Intl) that is incredibly easy to use. It doesn't work on iOS yet, but luckily you can do 'snap to' with CSS in iOS which is far more important than internationalization.
To handle the minute display, I made use of the awesome MomentJS library. It has a duration
API that makes showing the parts of a span of time very easy. I'm not terribly happy with how I showed it - but obviously you could modify this to your needs. Here is the result.

There ya go. Hopefully this helps. I'd share an online version but it is currently tied to my web property so it wouldn't work for you. To be clear, you can easily change the property, or add support for letting the user select their property.
Archived Comments
Can this API be used (in client-side JS) to show your website’s stats to all your visitors, or would one have to do that on the server (assuming the site has a Node backend)?
No, it is 100% tied to you logging in. For what you want you would need to use the "old" API and do it server side, which isn't terribly hard either, just not as slick as this. ;)
Mr. Camden, thank you for sharing these great examples. I was wondering if it would be possible to learn how to render percentages (%) and cost per conversion ($) as well. At the moment, percentages and dollar figures are not rounded. Is there a better way to illustrate these figures?
If you can build a demo like I did, I can work on fixing it.
Excellent, thank you. Would it be possible to email you the demo links? The demo domain origin is private, unfortunately. I could also post the code somewhere, if that would be better. Thank you again for your time.
If you could add me to the account it would be quicker as I could use your IDs. If you can't, just post the code online and change the IDs to an empty value I can modify myself.
Thank you, Mr. Camden. I've posted the information here: http://lawnetic.com/dashboa...
Please pardon the mess, I'm not very code savvy :-)
See this JSBin:
http://jsbin.com/savone/edi...
The first new function renders the percentage, the second does the currency. Note you have to specify a locale/currency type, which makes sense as you can't just change "1" from USD to Euros as they aren't the same.
Great!!! I'll give it a try, thank you.
Hi Raymond, awesome examples, thanks so much you got me nearest to fix my problem.
I want to hide the Account selector (because i don't want to let user change the views to other sites)
but what if the user still have not permissions to see the site data ? right now Analytics auto-assign him, his first active account and display the chart. That sucks, i mean, would be better a message/alert like this "You have no permission to see the charts of this site, talk with the owner" or something like that.
PD: I speak english very bad, hope you can understand me :), again thanks for the post really helpful
It would definitely be possible to handle this type of error (current user can't see site X), but I don't have code prepared to demonstrate that now. Sorry.
don't worry, thanks anyway, but you know where can i find a solution ?
I am reading the Embed Api and this link https://developers.google.c...
but still nothing :(
I believe they use a Google Group for support?
is there a way to get the google analytics data using Core Reporting API? I cant make out how to obtain the data. Without using a server.
I'm not quite sure I understand you. This example *is* getting data.
Actually i want to get the data directly from google analytics by making a request. So that i can use that data to render graphs within a mobile application.
I was able to do the same with embed api however the google requires me to specify a localhost or a url only then i can obtain the data.
So is it possible to obtain the data directly from google analytics by making an api call?
Yes- they support a REST API. I haven't used it in a while, but it is doable.
I am actually concerned about how the authentication would be handled. I was looking at the query parameters which enables me to get various type of data but how do I make a request so that i get the data back in some format.
Please see the APIs as it discusses how to authenticate. Start here: https://developers.google.c...
Raymond if I show you some code could you help me with problem I am having. I mean I can explain it but, don't want to lose you in the process lol.
Sorry, but typically that type of work is a paid engagement and I'm completely booked up right now.
how change language for months in reports ?
I'm using the Intl standard which your browser may not support. If so - you can switch to a library. Moment.js has this support.