Rolling your own integration

The base URL for the serving API is HTTPS is supported and its use is encouraged.

Quick Overview of Terms

Zone Key:

A zone key is a unique identifer that we use to serve ads a zone


Segments can be used to add custom demographics to your traffic, which can then be used further when setting up custom targeting for ads.

For example, a blog with a "technology" category could have a segment passed to the ad server as category:technology. When setting up a campaign, one could add the category:technology segment to the campaign's targeting, requiring the campaign to only be displayed when this segment is present.

When using a segment as part of a campaign's targeting, additional segments are allowed - any single segment that matches will allow the ad to serve. For instance, if you have a campaign targeting category:technology and category:computers, it would be allowed to serve even if the only segment attached to the serving call is category:computers.

Using the Serving API

There are a few API calls available, but the one that suits most cases is /ads/<zonekey>.<format>.

For the purpose of getting familiar with the data returned by the API, you can use the zonekey CKYD623L, which we have set up as a demo.


This API call returns N ads, up to the maximum number of ads specified in the zone's configuration. It will track an impression for each ad, as well as a single zone hit for the zone in question.

Formats allowed:

  • json
  • js
  • html

Supported parameters:

  • ignore: When present, signals the ad server to NOT track an impression for each ad that is returned.
  • callback: When the format is js or json, callback is used to specify the JavaScript function name that is called
  • with all ad data as a payload.
    • If the format is json and no callback is specified, the response will be a plain object.
    • segment: ; separated list of segments to use when determining campaigns to serve.
    • forwardedip: The end-user's IP address, used to determine geographic location of the end user for campaign targeting.
    • useragent: The end-user's user-agent string.

    A full serving request URL might look like:;category:ethereum&forwardedip=

    Effectively, it would tell the ad server to:

    • do not track an impression for ads that have been selected to serve,
    • allow ads with "category:bitcoin" or "category:ethereum" to serve,
    • determine the user's country/region/(etc) from the ip

    The response to this request might look like:

    		 "callToAction":"Learn more!",
    		 "description":"Where work happens. All the tools your team needs in one place.",

    The most important parts here are the values used for displaying the campaign's ad.

    Tracking Clicks

    For tracking clicks to campaigns, you must use the "statlink" value that is returned. Note that when using the "ignore" parameter, this will be appended to the "statlink" value. In order for clicks to be tracked, this parameter must be removed from the /ads/click/x/<value> link.

    Very important: If no "statlink" parameter exists within the response, then there are no ads to serve for the current request.

    Advertiser-Supplied Pixel Tracking

    Any ad within the response may contain a "pixel" key. Pixels must be dropped in to the page when present. Multiple pixels are allowed, and will be separated by two pipes (||).

    To prevent caching, the string [timestamp] within the pixel URL must be replaced with the current time. An example of doing this is below.

    Here's how we use pixels in our wrapper:

    var time = Math.round( / 10000) | 0;
    if (typeof ad.pixel != 'undefined')
        var pixels = ad.pixel.split('||');
        for (var j = 0; j < pixels.length; j++)
    	var pix = document.createElement('img');
    	pix.src = pixels[j].replace('[timestamp]', time);
    	pix.border = '0';
    	pix.height = '1';
    	pix.width = '1'; = 'none';

    Frequency Capping

    Frequency caps must be tracked in your implementation.

    A frequency capping string should look like:

    <banner ID>:<today's impressions>,<lifetime impressions>

    Multiple frequency caps can be present, and will be separated by a semicolon ;:

    <banner ID>:<today>,<lifetime>;<banner ID 2>:<today 2>,<lifetime 2>

    Using the banner ID from the example response illustrated above, assume the user has seen this ad 2 times today, and 30 times total:


    The easiest way to track these long-term is by setting a cookie for the user, and updating it when ads have displayed.

    A full serving request URL might look like:,1;72987:5,300

    Here's a sample function you might use for frequency capping:

    window['_bsap_serving_callback'] = function(banner, zone, freqcap) {
    	var append = function(w, data, days) {
    			var c = document.cookie,
    				i = c.indexOf(w + '='),
    				existing = i >= 0 ? c.substring(i + w.length + 1).split(';')[0] + '%2C' : '',
    				d = new Date();
    			d.setTime(days * 3600000 + d);
    			data = existing + data;
    			data = data.substring(0, 2048);
    			document.cookie = w + '=' + data + '; expires=' + d.toGMTString() + '; path=\/';
    	if (freqcap) {
    		append('_bsap_daycap', banner, 1);
    		append('_bsap_lifecap', banner, 365);
    // we check for a frequency cap here and if we find one make 
    // the call to the Ad Server API with the additional info
    var ck = '';
    try { ck = decodeURIComponent(document.cookie) } catch (e) {};
    var day = ck.indexOf('_bsap_daycap='),
    	life = ck.indexOf('_bsap_lifecap=');
    day = day >= 0 ? ck.substring(day + 12 + 1).split(';')[0].split(',') : [];
    life = life >= 0 ? ck.substring(life + 13 + 1).split(';')[0].split(',') : [];
    if (day.length || life.length) {
    	var freqcap = [];
    	for (var i = 0; i < day.length; i++) {
    		var adspot = day[i];
    		// using an array here is ugly, but safer cross-browser than for(var i in...) from an obj
    		for (var found = -1, find = 0; find < freqcap.length && found == -1; find++)
    		if (freqcap[find][0] == adspot) found = find;
    		if (found == -1) freqcap.push([adspot, 1, 0]);
    		else freqcap[found][1]++;
    	for (var i = 0; i < life.length; i++) {
    		var adspot = day[i];
    		// using an array here is ugly, but safer cross-browser than for(var i in...) from an obj
    		for (var found = -1, find = 0; find < freqcap.length && found == -1; find++)
    		if (freqcap[find][0] == adspot) found = find;
    		if (found == -1) freqcap.push([adspot, 0, 1]);
    		else freqcap[found][2]++;
    	for (var i = 0; i < freqcap.length; i++)
    		freqcap[i] = freqcap[i][0] + ':' + freqcap[i][1] + ',' + freqcap[i][2];
    	if (freqcap.length) pro.src += '&freqcap=' + encodeURIComponent(freqcap.join(';'));