What I found out about the tracking-pixel in digital advertising

Past year I’ve been fortunate enough to solely be able to work on the technology for cross-platform and multi-channel ad-sales agencies.

Finding ways to track events such as how many times an ad was exposed (impressions), how much of a video was played before it was skipped, or how many times a mobile-ad was swiped.

I’m going to drill through the methods I went through and assemble a list of problems I encountered; And at the end the solution I find working best.

Tracking on the server-side

Easiest way to track impressions is on the server-side; For every request with a successful ad-query, you increment impressions by one.

This method does work, but isn’t very reliable from a media-buyers perspective, as the impression will count even though the ad itself doesn’t load for some reason; And the buyer ends up paying for more exposure than they actually got.

  1. Unreliable stats for media-buyers.

Tracking impressions on the client-side

Then you turn to what’s considered best practice by IAB, the use of tracking pixels (i.e. transparent 1x1 pixel images) on the client-side.

Easiest way to do this is to serve image-tags with the ad-content.

1
<img src="http://my.adserver.com/trk.png?aid=1" />

You will sooner or later realize, serving millions and millions of tracking pixels quickly ends up taking up a lot of really unnecessary bandwidth (and disk-reads) for your servers.

Note that disk-reads can be bypassed by responding with a binary base64 string instead of reading an image from disk.

Another somewhat trivial issue, if it is a website the loading time will increase with each image tag as they don’t load asynchronously.

  1. Unreliable stats for media-buyers.
  2. Waste of unnecessary bandwidth and disk reads.
  3. In some cases with image-tags load time increased.

Tracking other events

XMLHttpRequest

When it comes to tracking user initiated events, an obvious approach for me was to use XHR requests.

So instead of requesting “/trk.png” I also implemented “/trk” with a JSON response:

1
2
3
{
  status: "ok"
}

Unfortunately some devices and browsers did not like the approach even with proper cross-domain implementations.

  1. Unreliable stats for media-buyers.
  2. Waste of unnecessary bandwidth and disk reads.
  3. In some cases with image-tags load time increased.
  4. XHR requests doesn’t work on all devices/browsers.

Write image-tags

The other is to render image-tags with “document.write” when an event is fired.

A couple of interesting problems arose from this solution after introducing around five to ten new image-tags to the DOM:

  • The browser started to timeout, which meant missed tracking events
  • and the page was littered with broken images.

The solution to the list of problems

Let’s go through the problems and have a look at the solution.

  1. Unreliable stats for media-buyers.
  2. Waste of unnecessary bandwidth and disk reads.
  3. In some cases with image-tags load time increased.
  4. XHR requests doesn’t work on all devices/browsers and has some cross-domain issues.
  5. DOM littered with broken or working hidden image-tags.

After tinkering in the console, changing from “document.write” to “.appendChild” I noticed that when setting “src” on an image object, the url was requested before the object was attached to the DOM. Which I also confirmed by Googling a bit.

  • Instead of requesting /trk.png we request /trk
  • Instead of serving a 1x1 image we respond with 204 No-Content
  • Instead of writing out image-tags we take advantage of javascript Image object, which asynchronously fetches the image without having to attach it to the DOM.

For tracking impressions on client-side:

1
(new Image()).src = (location.protocol == "https:" ? "https:" : "http:") + "//my.adserver.com/trk?aid=1&action=clientSideImpression";

For tracking other events on client-side:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createTrackingPixel(action) {
  (new Image()).src = (location.protocol == "https:" ? "https:" : "http:") + "//my.adserver.com/trk?aid=1&action="+action;
}

video.addEventListener("play", function(e) {
  createTrackingPixel("play");
});

video.addEventListener("pause", function(e) {
  createTrackingPixel("pause");
});

swipe.addEventListener("swipeleft", function(e) {
  createTrackingPixel("swipeleft");
});

Conclusion

The tracking end-point now supports all sorts of calls from different implementation standards such as VAST, Flash, JavaScript and Image tags without unnecessary fluff.

If you have any ideas to improve it further or have any suggestions or questions, please leave a comment below.

Have a good one!

Comments