See also:
Table of contents:
Introduction
There are various Cxense APIs. At the very bottom of the stack, we have the Cxense HTTP API which we can think about as being the transport vehicle. It has all the generic functionality required for sending a function name (URL path) and the parameter list to go with it, over to Cxense Insight. On top of the HTTP API we have the JavaScript API that just like the HTTP API underneath has generic transport functionality. In this case provided via a JSONP function able to send whatever function name (path) and parameters (request object) to Cxense Insight using the same generic functionality in the HTTP API below it. In addition to this generic pass-through functionality, it also offers some functionality in its own right (see section JavaScript API Functions below for a list of functions). The Cxense Insight API Specification above it just lists and describes the functions and parameters that can be transported to Cxense using the generic transport functionality of either of the two layers below it. For native mobile apps (that neither renders HTML or uses HTML as the information source for what it displays) we have another API provided in the shape and form of a Mobile SDK (Android and iOS only).
What we just described so far is the left side of the figure above. To the right we see how a specific piece of the Cxense Insight API have been provided with a special transport mechanism (the /dmp/push function can be sent as a pixel request - a special variant of the HTTP API one could say).
In this tutorial we will start out by running API calls via the GUI, then use the Cxense provided command line tool cx.py, before finally getting into writing our own code. Web page code will be written using JavaScript and backend code using Python 3.x.
The "Hello World!" Cxense API Example
Let's start simple with the Hello World! version of the Cxense Insight API. Via the GUI we can see a site group with the name Hello World! as shown in the graphic below. In the next few examples, we are going to obtain that site group name as part of the site group information we are going to retrieve.
Running API Functions via the GUI
To obtain the site group information we will be running the /site/group function. The maybe easiest way of doing that is to run the function from the GUI as shown below. For any site group that you at least have read access to, you can obtain the information as shown here provided you know the site group id:
Running API Functions using the cx.py Tool
Cxense has prepared a Python based command line tool that one can download and use following the instructions given in Installing the cx.py Tool, The command line syntax is a bit different from OS to OS. Below we see the unIx/Mac version on the first line and the Windows version on the second line of the same function as we ran via the GUI above.
|
.
Running API Functions from within a Python 3.x Script
In both of the two cases above where we ran the /site/group function, either via the GUI or using the cx.py tool from the command line, we did not have to deal with authentication directly. That was already taken care of when we logged into the portal or by storing our credentials in the file .cxrc in our home directory. When we write our own script however, then we need to deal with authentication directly.
Below we see the Python 3.x code for retrieving the same site group (id 1130529259612938212) as in the two previous examples:
|
We make the following observations:
-
Line 8 through 10 is spent on constructing the HMAC based Cxense specific authentication HTTP headers field X-cXense-Authentication:
headers = {"X-cXense-Authentication": "username=<username> date=<date> hmac-sha256-<encoding>=<signature>"}The time used, both standalone in line 9 and as part of the signature in line 10, must be in sync with the time used by the Cxense server. The none authenticated API function /public/date can be used to obtain the time used by the Cxense server if you are unable to obtain correct time using your computer clock.
-
The format of the data both sent (line 12) and received (line 15) is JSON.
-
Two result variables are being returned (line 17), an HTTP status code (variable status on line 14), and the resulting data (variable response on line 13).
-
Any status code other than 200 is an error (line 20)
If we want to pick up the credentials from the same .cxrc file that the cx.py tool uses, then we can add this functionality to the script:
|
Other Programming Languages
In the example above we were using Python 3.x as the programming language. However, give that the API is based on the HTTP protocol, practically any programming language can be used and below we are showing the Hello World! example from above in other languages (including the 2.x version of Python). For languages not covered here, consult API authentication.
|
|
|
The instructions at How to Test PHP Code Locally shows how one easily can test one's PHP code locally on a Windows machine using XAMPP.
For the function file_get_contents() in the PHP example to not throw an error, the setting allow-url-fopen should be set to 1 (or to 'On') within the php.ini configuration file. This is the default value, but some hosting providers changes it. For the Windows XAMPP tool described in previous paragraph, the file is stored at xampp\php\php.ini.
Batch Mode
For many type of operations, executing the API function for one entity at the time will be highly inefficient. That is why it is possible to use batches of up to 100 request objects at the time. Notice how our single dictionary from the previous examples now has become an array (called a list in Python) of the same type of dictionaries.
|
Assuming that one has access to the first site group and knowing that there is no site group id with only the digit 9 in it, we end up with the response shown below. Notice that unlike when we sent a single request object and was returned a single dictionary, this time around we are returned an array of dictionaries.
|
Below we see the Python version of the batch approach (only what is different from the non-batch approach is included). Notice how we had to change the error handling in order to be able to handle both individual results as well as a batch of them.
|
Error Handling and Retries
So far we have either aborted due to an error or displayed the result. For some task that is all that is needed. However, in many cases we need to be able to deal with errors in some graceful manner and then continue. This may include retrying depending on the nature of the error reported. Below we see code that up on failure retry with less and less frequency until giving up after a maximum number of tries.
|
Notice how the code above deals with three types of error. Error independent of Cxense and our script (typical connection issues like the socket.gaierror shown below), Cxense Server errors (for instance HTTP error 503, Service Unavailable) and usage errors (for instance, we referring to a site group id in our script that does not exists).
To test the error handling and retry code we can disconnect the computer we run on (disconnecting cable and/or turning of the wireless). Below we see the output. Notice the how the retries are less and less frequent for each retry:
|
Using the API from within a Web Page
There is two ways of accessing Cxense API functionality from within a web page. The preferred way is to use the Cxense JavaScript API provided via cx.js (better looked at in it its more human readable version at cx_plain.js). However, in cases where the use of JavaScript is restricted in some way, then the Cxense HTTP API is a good plan B. The left side of the figure below shows how the Javascript API sits on top of the HTTP API whereas the right side iilustrates how one can use the HTTP API directly.
The principle of both side is the same. In one way or another, create a URL where the URL path holds the function name (above shown as /some/function), then followed by the URL query with function parameters (not shown in the example above) and the callback function (callback=callbackFunction). The callback function is the name of the function that the JSONP mechanism will call with the returned result as the only input parameter.
Using the JavaScript API
The challenge we face when using Cxense API functionality from within a web page is the authentication as we cannot place our user name and secret api key in the JavaScript code for anyone to see and possibly misuse. To protect us against that we will use a so-called persisted query. A persisted query is a API call that one has stored within Cxense Insight and can access later from a web page by giving a particular key. For more details, consult with the Persisted Query Tutorial.
Below we see how to create the persistent query for the API function call site/group {"siteGroupId": "1130529259612938212"}:
And below we see JavaScript code making use of it. Notice how the JavaScript API function jsonpRequest() takes the API function as a URL as its first input parameter and the function to process what is return from Cxense as the second one. The returned data can be accessed via the function's input parameter data.
|
As will be seen upon opening the html page and as shown in the screenshot below, the output is identical to what saw within the GUI in the very first example at the beginning of this tutorial.
In the code above we did not include the site group id as it was already made part of the persisted query. However, sometime we want to be able to change a function parameter from within the web page. Imagine we want to be able to set the site id there after having received the id from the user via an input box or having been selected from a drop down. The next example show the id being set in the web page:
|
However, when opening the page we receive the error message below rather than site group information:
|
To fix this we have to go back to the persistent query wizard and explicitly allow the parameter to be set from the web page as shown below:
After this the web page is not only going to show the same result as before, we are now also free to change the site group id in the code to that of any site group that we have access to and the information returned will be for that site group instead.
Using the HTTP API
Instead of using the HTTP API indirectly via the JavaScript API, we can instead use the HTTP API directly. For instance, using the network traffic option of any browser's built-in debugger, we can look up the JSONP GET request from the previous example. If we do so, we will find something like this:
|
For improved readability, let's reverse the URL encoding:
|
Now we more clearly see the components of the URL used to communicate with the Cxense server:
|
Component/Parameter |
Value |
|---|---|
|
server |
|
|
path |
site/group |
|
callback |
cXJsonpCBixa75cstfh59sdwv |
|
persisted |
3d05ed2fb191be8b9a55b06af227b25438b48189 |
|
json request object |
{"siteGroupId":"1130529259612938212"} |
The callback function name clearly indicate that it is dynamically created as cXJsonp + some random string. That means that the next time we use the JavaScript API, the callback function will have a new and different name. However, if we implement it all ourselves, then we can set the function name to something fixed as shown in the code below where we have named it myCallbackFunction.
|
Functionality wise the code above will produce the same output as the previous example (and all other examples in this tutorial up to this point).
Using the Pixel API
Above we learned how to get around any restrictions on the use of the JavaScript API by simply using the Cxense HTTP API directly. There are however some functionalities that Cxense does not want customers to access using the HTTP API and one of these is the DMP performance event tracking functionality for which Cxense has provides yet another JavaScript free API alternative: the Cxense Pixel API.
|
Above we see how the Cxense logo is doing the job of being an placeholder for a real ad. The purpose of the pixel request is to report that the ad has been seen (type=impression) at some particular place or context (origin=dha-pixelDemo). The dha part is the customer prefix. The persisted query is different from the one that we have used in the previous /site/group example. This one is created to allow the use of the /dmp/push function, the only function for which the pixel API will work.
JavaScript API Functions
So far, with the exception of calling the /dmp/push function using the Pixel API in the last example above, all examples, JavaScript or not, have been using the /site/group API function. Since this latter function is not directly supported by any dedicated JavaScript function, we used the generic JavaScript API function cX.jsonpRequest() in order to execute the specific API function. More or less all the function listed at Cxense Insight API has to be executed this way. However there are a few exceptions to this as well as additional functionality not covered by the Cxense Insight API that have dedicated functions in the JavaScript API. In the table below we list a few of them. By clicking on the function names, one can get to more information and/or sample code.
|
Function |
Description |
|---|---|
|
Used with single page web application to create the effect of a new page view. |
|
|
Mandatory to use before sendPageViewEvent(). Decides for which site id the page view will be registered. |
|
|
Sends a pixel request to Cxense Insight with all the user information that has been obtained up to that point about the current visitor. |
|
|
getUserId(createIfMissing) |
Used to obtain the Cxense cookie based user id for the current visitor to the web page. Example: alert(cX.getUserId()) |
|
addExternalId(params) |
Used to sync the current visitor's Cxense user id with the id used by the customer or some third party. |
|
setCustomParameters(parameters, prefix) |
Adds additional custom parameters to the page view data |
|
invoke(func) |
Mainly used to call synchronous code asynchronous |
|
jsonpRequest() |
Generic function used to pass in any Cxense API function and its parameters. |
|
sendEvent(type, customParameters, providedArgs) |
Used to send Cxense Insight DMP performance events/user engagement data |
|
insertWidget(requestObject) |
Used for deploying a Cxense recommendation widget on a web page |
|
getUserSegmentIds(requestObject) |
Used to obtain the list of all DMP audience segments that a user is member of |
Another good resource for reading up on what and how in regard to the JavaScript API is Event data.
Synchronous vs. Asynchronous use of the JavaScript API
The cx.js file can be loaded in a synchronous and an asynchronous way. Let's first load it synchronous.
|
Notice how we first load the cx.js file before making use of any of the functions within that file.
Contrast this to the asynchronous approach where it does not matter if we call the functions before or after we have loaded the cx.js file. The reason for this is that all function calls are added to a queue which is not processed before the library file has been fully loaded. In the example below we call the functions before cx.js has been loaded. The execution of the code (the processing of the queue) is delayed though until the library has been loaded.
|
A third alternative is to load cx,js asynchronously, but execute (most of) the code synchronously. This is done via the cx,js invoke functions as shown below:
|
In this case the call to invoke is asynchronous (the call to invoke is placed in the queue for later processing). However, once the call is finally being processed, all function calls within the invoke function are executed synchronous (are not put in the queue, but acted upon there and then).
Single Page Application
Single Page Applications (SPA) are becoming more and more common with the increased use of SPA frameworks such as AngularJS, EmberJS and MeteorJS just to mention a few. The challenge for Analytics Tools such as Cxense Insight is that this makes users keep visiting one and the same page over and over. Visually for the user it appears as visits to different pages, but for Cxense Insight that uses the URL as the page identifier, all page views ends up being credited to one and the same page. To overcome this, it is necessary to programmatically overwrite the current location URL and the referrer URL in order to align what is to be reported with the visual user experience.
Below we see an sample page with some of the required building blocks required to achieve this:
|
The main difference between the tracking scripts examples we have seen earlier and the one here above is the call to the function initializePage() and the parameters location and referrer added to the sendPageViewEvent() function call and the fact that we have wrapped it all up in the function singlePageApplicationPageViewEvent().
-
Cxense Insight normally ignores all page view events reported for the same URL during one and the same page visit. The purpose of the initialPage() function is to tell Cxense Insight that in this case it is indeed a new page view that should not be ignored.
-
The parameters location is set to the URL that is to represent the current virtual page, and likewise, the parameter referrer is to be set to the URL of the previous virtual page that the user came from.
-
The wrapping of the tracking code in the function singlePageApplicationPageViewEvent() is done so that the programmer has a handy way of reporting a new page view from wherever that makes sense in his or her page rendering code
Infinite Scroll Pages
Another increasingly common page type is infinite scroll pages where one scroll straight out of one news story and into the next one. This causes very much the same kind of issues as the single page application pages discussed in the previous section. That is, one has to operate with various logical URLs for one and the same physical URL in order to go from one virtual page to next as one scrolls down the physical page. Below we see an example of such a page with 3 news stories:
|
Notice how the implementation is very much an extension of the previous SPA example. The main difference is in how one detects that a new page has been accessed. For SPA that is normally based on a user clicking on some link or button, whereas for infinite scroll pages one have to detect when the next page is entered. In the example above we the cx.js library function trackElement() to do this.
Mobile Devices
As seen in the last few sections, Cxense track user traffic at any site by having the site owner deploy the Cxense script on all pages that are to be tracked. For default deployments, the site id would be the only difference in the deployed script from one site deployment to another. This approach requires two things:
-
That one has web pages that can run JavaScript
-
That the Cxense library file cx.js with Cxense functionality is available
For any regular web site this is never a problem. However, on mobile phones things can be a bit trickier. In the context of Cxense Insight and user tracking, there are these 3 categories of mobile applications:
-
Web Apps
-
Native Apps
-
Hybrid Apps
Web Apps offer web content via web browsers the very same way as desktop browsers with the exception of possibly paying more attention to dimensions and sizing issues (referred to as responsiveness) to accommodate for the smaller screen size. In both the case of desktops and mobile devices, the 3 core technologies are HTML, CSS and JavaScript. Native Apps on the other hand offers the content via an OS specific app and any consumed internet content needs to be retrieved using a content source specific API (often provided in the form of an SDK).
The advantage of the cross-platform Web App is the low cost to develop and maintain them. The disadvantage is the inability to make use of device specific resources and the uneven quality of browsers across the various mobile platforms.
Hybrid Apps are native apps that can read, understand, and if desired, also execute HTML, CSS and JavaScript, but that do not display this information in a browser fashion. Instead it treats the content more as pure information and puts its own look, feel and user operations/interactions to it. And more importantly, just as native apps, hybrid apps have full access to all device resources and is able to present information in a way a Web Apps would not be able to regardless of platform.
Mobile SDK
To do user tracking, there are no extra requirements for Web Apps and Hybrid Apps beyond what is already required for desktops and tablets. The Cxense script will work out of the box as-is. However, in the case of Native Apps, the Cxense script will not work, and for that reason Cxense provides a Mobile SDK so that the customer can implement their own user tracking. The same is also true for Hybrid Apps when it comes to more advanced functionality beyond simple user tracking.
The Mobile SDK has support for products listed below and comes with its own set of tutorials.
-
Cxense Insight
-
Cxense Content
-
Cxense DMP
-
Cxense Advertising
-
Cxense Display
Mobile Web Page Accelerators
A web accelerator is a proxy server that reduces web site access time. As the regular Cxense script cannot be used, Cxense provides special user tracking scripts for two mobile web page accelerators, Google's Accelerated Mobile Pages (AMP) and Facebook Instant Articles (FIA).
Deploying the Cxense Script using a Tag Manager
The Cxense Script will work as-is with most tag managers. However, for tag managers that places the tags in functions of their own (such as for instance Optimizely) one should instead use the code below where we have changed the first line of code to ensure that cX remains a global variable no mater where the code ends up (notice how var cX is replaced by window.cX):
|
Troubleshooting
Since all API usage, regardless of what API we are talking about, will end up with information being sent back and forth over the HTTP API (the transport layer as we called it earlier), having a look at that traffic can often give the answer to what went wrong when something fails.
|
Above we have purposely introduced an error into a previous working example. Notice how the persisted query has been replaced with s sequence of Xs. Since the API is used from within a web page, then we can use the browser's built-in debugger (activated for most modern day browsers by clicking F12 while the mouse is hoovering over the browser window) to look at the traffic over the network. Notice how the error is spelled out in the traffic data (click on the image for larger size): "Invalid persisted request id".