The Way Server-side Analytics Should Be


By Ilya Volodarsky, Ian Storm Taylor

“We can’t spare developer time right now.”

That’s what you’ll hear if you ask a startup why they aren’t tracking their funnels, why they aren’t sending drip-email campaigns, or why they haven’t installed live-chat. Even services like Intercom or that are practically guaranteed to increase your conversion rates still get punted.

There are lots of different useful analytics services out there: event tracking, email remarketing, live-chatting, etc. These services are key to understanding your users and increasing your conversion, but no one wants to spend extra developer time setting them up. We’ve heard from companies who are two months into an analytics integration and still aren’t finished!

That’s the status quo: Everyone loses.

Your startup loses out on missed conversion opportunities and on having real conversations with your users. Your users lose out on the first-time learning experience with the product. And analytics services lose out on potential paying customers.

Lowering that integration barrier was why we built Analytics.js in the first place, and today we’re bringing the same clean API to PythonRubyNode.jsC#/.NETand Java!


Just like Analytics.js, our server-side libraries route your data to any analytics service you enable with the click of a button. Our goal is to give you a clean, simple API that works no matter what language you code in. Because server-side analytics is no man’s land.

No seriously, server-side analytics is crazy.

Installing server-side analytics tracking is like The Odyssey: it should be a quick job, but you’re in for some real suprises along the way. You’ll come face to face with some of the nastiest APIs (and docs!) in analytics.

The first thing you’ll find is that most modern analytics services don’t offer official server-side libraries. You might stumble across a community-supported library, but it probably hasn’t been touched in months.

If you’re lucky, you’ve decided to use a service that maintains a well-documented JSON API. If you’re not so lucky, you’re stuck trudging through SOAP or XML. (Looking at you Marketo and Salesforce!)

Even with a clean REST API it’s not smooth sailing. Since you’re wrapping their REST API, all of your calls will result in HTTP requests that you don’t want slowing down your controller code. And you’ll find a note like this in their documentation:

We strongly encourage you to use non-blocking solutions, such as a queue service like Kestrel or RabbitMQ.

What that means is that you’ll need to write more code that:

  • Submits analytics calls to a new queue that you have to maintain.

  • Adds workers that read from the queue and send out HTTP requests.

  • Keeps these new parts of your cluster alive.

What should have been a before-lunch task can quickly turn into a multi-week epic. Those are the kind of problems we want to eliminate.

We’ve had all of these problems ourselves.

We know these problems all too well. Really. We used to work on our own analytics service and getting people integrated was near impossible. So when we started building our server-side libraries, saving developer time was our number one priority.

One of the best ways to achieve that is to make our libraries performant, so that you never have to build your own wrapper around them. Each of our libraries uses an internal queue to make identify and track calls non-blocking and fast for the calling thread. They also take advantage of our batching REST API and asynchronous flushing.

The result is a set of high-performance analytics libraries that you can drop right into web server code that’s serving hundreds of requests a second. No more queuing code. No more workers.

Another huge time saver is that we use the same API (and the same docs) for all of our libraries. With the release of our server-side libraries, you can now use exactly the same API in every language you write in.

That means you don’t need to spend time reading through a new set of (crazy) API docs every time you want to add a new service. Once you know one, you know them all. Seriously, look how similar they are:


analytics.track('Completed Purchase', { 'Revenue': 39.95, 'Shipping Method': '2nd-decade' });


analytics.track('', 'Completed Purchase', { 'Revenue': 39.95, 'Shipping Method': '2nd-decade' })


Analytics.track( user_id: '', event: 'Completed Purchase', properties: { 'Revenue' => 39.95, 'Shipping Method' => '2nd-decade' } )


analytics.track({ userId: '', event: 'Completed Purchase', properties: { 'Revenue': 39.95, 'Shipping Method': '2nd-decade' } });


Analytics.track("", "Completed Purchase", new EventProperties() .put("Revenue", 39.95) .put("Shipping Method", "2nd-decade"));

C# / .NET

Analytics.Client.Track("", "Completed Purchase", new Properties() { { "Revenue", 39.95 }, { "Shipping Method", "2nd-decade" } });

It’s an investment that pays dividends immediately.

We’re excited to be releasing libraries that we know will simplify all of your analytics tasks, server-side and client-side.

All of our libraries are open-source too, so you can check them out on Github. They’re also fully (and cleanly) documented, client-sideserver-sideintegrations, everything!

You can signup here. And, as always, send your questions and feedback to!

PS. If the insane APIs got you reminiscing, we’ve got a post coming soon with the weirdest things we’ve seen while reading through all these complicated analytics APIs. Things that even Poseidon wouldn’t have wished on anyone.

The state of personalization 2023

The State of Personalization 2023

Our annual look at how attitudes, preferences, and experiences with personalization have evolved over the past year.

Recommended articles


Want to keep updated on Segment launches, events, and updates?