kissmetrics

The Kissmetrics Developer Hub

Welcome to the kissmetrics developer hub. You'll find comprehensive guides and documentation to help you start working with kissmetrics as quickly as possible, as well as support if you get stuck. Let's jump right in!

Support    
 

Welcome to the Kissmetrics Developer Documentation. Here you will find all the technical documentation necessary for integrating data with Kissmetrics - as well as getting data out of Kissmetrics. You will find information here about the Kissmetrics data model, our various tracking APIs as well as our Query API - the main channel for getting data out of Kissmetrics.

Getting Data into Kissmetrics

If you are a brand new Kissmetrics user, you will likely be most interested in sending data from your product to Kissmetrics. The most common way of doing this is via our Javascript tracking library. Please review the Javascript Library section below to become comfortable how we track data. In this section, you will also find specifications for sending data via server-side integrations as well.

We also recommend you review the details of the Kissmetrics data model. This will definitely help you understand how to best implement your data tracking to derive the move value from the Kissmetrics platform.

Pulling Data from Kissmetrics

You can programmatically access any data sent and stored in your Kissmetrics account via our REST API. This is a very powerful API that allows you to query your data in many different ways. Please review our REST API documentation to understand all the ways you can access your data via API.

With that said, we hope you find this documentation helpful and we look forward to working with you!

Suggest Edits

Introduction

Javascript Library

 

Our Javascript Library is our most full-featured library and is what we recommend for most users. It has extra provisions for things like automatically handling identities and aliasing, tracking common web events and running A/B tests.

Head over to the Setup to access your tracking snippet.

The JavaScript library loads asynchronously, in the background, just like Google Analytics’ Asynchronous Tracking. Asynchronous loading does not affect your page’s load times. It is also compatible with pages served using both HTTP or HTTPS. The // before the path of your JS files is a protocol-independent absolute path, which detects whether your site uses HTTP or HTTPS.

You can place the JavaScript snippet anywhere in the HTML document, but we ask that you place the JavaScript snippet in the <head> to let our script load as soon as possible, to be able to send events before the visitor leaves. This also lets you queue events to be triggered when the library finishes loading. This prevents JavaScript errors from cropping up if you try to call events before the script has loaded: ReferenceError: _kmq is not defined.

Calling a Kissmetrics Function

All function calls (identify, record, set, etc.) and their arguments are passed as an array to the _kmq.push(fnArray) method. The first element of fnArray is the function to be called and the following elements are arguments to be passed to that function. For example:

_kmq.push(['identify', 'test@kissmetrics.com']) // Identify call

Javascript Library Examples

We have documented most examples using Codepen which you can use to try out our Javascript Library. Codepen is a front-end playground that allows users to interact with code as they use it. We use it in editor mode which will enable you to follow the steps on the screen and copy and paste code to get an example Intercom app installed on the page.

 

_kmq.push(['identify', identity])

identify connects the current person with a unique ID, and attributes future events from this browser to this provided ID. If the current person is ‘anonymous’, it also connects their ‘anonymous’ ID to the provided ID so we recognize both as being the same person (aliasing). Calling identify does not count as an “event”.

See the Pen KM JS Library - Identify by Kissmetrics (@kissmetrics) on CodePen.

Parameters

Name
Type
Description

identity*

string

A unique ID to identify the current person.

_kmq.push(['set', traits, callback])

set sets traits for the current user.

See the Pen KM JS Library - Set by Kissmetrics (@kissmetrics) on CodePen.

Parameters

Name
Type
Description

traits*

object

The traits to set for the current user.

callback

function

Function to be called after the traits are set.

_kmq.push(['clearIdentity'])

clearIdentity clears the current person's identity and generates a new anonymous ID for their browser. Does nothing if the current person is currently ‘anonymous’.

See the Pen KM JS Library - Clear Identity by Kissmetrics (@kissmetrics) on CodePen.

_kmq.push(['record', name, properties, callback])

record records an event and its properties.

See the Pen KM JS Library - Record by Kissmetrics (@kissmetrics) on CodePen.

Parameters

Name
Type
Description

name*

string

Name of the event to be tracked.

properties

object

Properties related to the event being tracked.

callback

function

Function to be called after the event is tracked.

_kmq.push(['trackClick', selector, name, properties])

trackClick tags an HTML element to record an event when it is clicked.

See the Pen KM JS Library - Track Click by Kissmetrics (@kissmetrics) on CodePen.

Parameters

Name
Type
Description

selector*

string

The ID or class of the element to track.

name*

string

The name of the event to record.

properties

object

Properties related to the event being recorded.

_kmq.push(['trackClickOnOutboundLink', selector, name, properties])

trackClickOnOutboundLink tags a link that takes someone to another domain and provides enough time to record an event when the link is clicked, before being redirected.

This builds in time to send the event by:

Canceling the original click event’s default behavior
Sending the data to Kissmetrics
Waiting 250ms
Redirecting the browser by setting document.location
For this reason we don’t recommend this for usual click tracking. Please make sure to test this with your site so that this performs as expected.

<a href="http://othersite.com" id="link1">Visit Other Site</a>
<script type="text/javascript">
  _kmq.push(['trackClickOnOutboundLink', 'link1', 'Visited Other Site']);
</script>

Parameters

Name
Type
Description

selector*

string

The ID or class of the link to tag.

name*

string

The name of the event to record.

properties

object

Properties related to the event being recorded.

_kmq.push(['trackSubmit', selector, name, properties])

trackSubmit tags a form to record an event when it is submitted.

_kmq.push(['trackSubmit', '.contact-form', 'Submitted Contact Form', { company: 'Kissmetrics }]);

Parameters

Name
Type
Description

selector*

string

The ID or class of the form to tag.

name*

string

The name of the event to record.

properties

object

Properties related to the event being recorded.

Automatically Tracked Events

The JavaScript library automatically tracks certain events and properties by default - meaning as soon as you insert the snippet into your pages, you will automatically start sending data to Kissmetrics:

  • Visited Site
    • The Property KM Referrer indicates the URL the visitor came from
    • The Property URL indicates the URL of the page they started browsing your site
    • We record Visited Site once for each visitor’s browsing session. After 30 minutes of inactivity, the next time the visitor comes back will trigger a new Visited Site event, and they will also have the km_returning trait set to “Returning”.
    • km_returning is a built-in property that we calculate based on when the user recorded “Visited Site.” The first time a user visits your site, km_returning will be set to “new.” On every subsequent visit, km_returning will be set to “returning.”
  • Form Fields
    • If you set up an event to log a form submission (either through the API call trackSubmit or the Event Library’s ‘Submits a Form’), then we also capture the data from the form’s fields.
    • Each <input> field will be saved as Property; we use the name attribute of the <input>’s to name each Property.
    • Sensitive fields (passwords, credit card numbers, and social security numbers) are NOT captured.
  • Ad Campaign Hit
    • Triggered if a visitor reaches your site via a Google Ad Campaign (eg. paid search). We detect this by checking the URL for ?gclid or ?utm_ parameters, which indicate a URL tagged for ad purposes.
    • All the utm variables will be captured as Traits, if they are present in the URL.
      • Campaign Source
      • Campaign Medium
      • Campaign Terms
      • Campaign Content
      • Campaign Name
  • Search Engine Hit
    • Triggered if a visitor reaches your site via a search engine, through organic search.
    • The Property Search Engine indicates which search engine was used.
    • The Property Search Terms indicates the search terms used.
  • Page Views
    • (disabled by default!)
    • Triggered every time a visitor views any page on your site.
    • The Property Viewed URL indicates the page viewed.
    • The Property KM Previous Page indicates the page last viewed.

View all the events here: https://app.kissmetrics.com/product.js_settings. You can enable/disable these events on this page, too.

 

KM.ab(traitName, variations)

KM.ab initiates an A/B test and sets the trait that indicates which variation was randomly picked.

See the Pen KM JS Library - AB by Kissmetrics (@kissmetrics) on CodePen.

Parameters

Name
Type
Description

traitName

string

The name of the experiment to record as a trait for the current user.

variations

array

The list of variations to be randomly assigned to users.

Response

KM.ab returns which variation is picked, to reuse as a JavaScript variable. To remember which variation was used, KM sets a cookie. Please look at our page on Developing Locally if you are testing KM.ab locally.

Suggest Edits

Utilities

 

KM.i()

KM.i returns the visitor’s Kissmetrics ID, in case you need to pass that along to another function.

If the JavaScript library has never seen a visitor before, it will create a randomized identity for them to attribute all of that person’s events to the same browser. This lets you track this particular person’s activity across sessions.

The generated ID is Base64 encoded, so the IDs are generated with only these characters: a-z, A-Z, 0-9, +, /, and =.

See the Pen KM JS Library - KM.i() by Kissmetrics (@kissmetrics) on CodePen.

KM.ts()

KM.ts returns the current Unix timestamp in seconds. This is what we use to determine the current time.

See the Pen KM JS Library - KM.ts() by Kissmetrics (@kissmetrics) on CodePen.

Releases

https://github.com/kissmetrics/KISSmetrics-iOS-SDK/releases

Setup

  • Add the KISSmetricsSDK.framework to your project.
  • Within AppDelegate.m, import the framework like so:
#import <KISSmetricsSDK/KISSmetricsAPI.h>
  • Still in the AppDelegate.m file, find the didFinishLaunching or didFinishLaunchingWithOptions
    method and add the following code as the first line in that method. Be sure to apply your API key.
[KISSmetricsAPI sharedAPIWithKey:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxx"];

Initializing with Development and Production keys

#ifndef DEBUG
    [KISSmetricsAPI sharedAPIWithKey:@"yourDevelopmentProductKeyHere"];
#elif RELEASE
    [KISSmetricsAPI sharedAPIWithKey:@"yourProductionProductKeyHere"];
#endif

Example Calls

// Initialize the API
[KISSmetricsAPI sharedAPIWithKey:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxx"];
// Identifying Users
[[KISSmetricsAPI sharedAPI] identify:@"name@email.com"]; // Replace @"name@email.com" with code that gets the identity of your user!
// Track Event
[[KISSmetricsAPI sharedAPI] record:@"My Event"];
[[KISSmetricsAPI sharedAPI] recordEvent:@"My Event"];    // recordEvent() is being deprecated
// Track Event with Properties
NSDictionary *myEventProperties = [[NSDictionary alloc]
  initWithObjectsAndKeys:@"Value", @"My Property", nil];
[[KISSmetricsAPI sharedAPI] record:@"My Event With Properties"
  withProperties:myEventProperties];
// Set Properties
NSDictionary *myEventProperties = [[NSDictionary alloc]
  initWithObjectsAndKeys:@"Value", @"My Property", nil];
[[KISSmetricsAPI sharedAPI] set:myEventProperties];
[[KISSmetricsAPI sharedAPI] setProperties:myEventProperties];  // setProperties() is being deprecated
/* New Functions as of v2 */
// Track an event only once per set identity. After an identity is cleared or a new identity is set, the condition will pass once again.
[[KISSmetricsAPI sharedAPI] record:@"Viewed feature" onCondition:KMARecordOncePerIdentity];
// Track an event only once per install
[[KISSmetricsAPI sharedAPI] record:@"Installed App" onCondition:KMARecordOncePerInstall];
// Set user properties only if they have changed on the device
[[KISSmetricsAPI sharedAPI] setDistinct:@"7.1" forKey:"OS version"];

Optional Automated Tracking

  1. autoRecordInstalls()
    Calling this records two events:
    • Installed App
    • Updated App
  2. autoSetAppProperties()
    Calling this sets the following properties:
    • App Version : 1.0.0
    • App Build : 101
  3. autoSetHardwareProperties()
    Calling this sets the following properties:
    • Device Manufacturer: Apple
    • Device Platform: iPhone
    • Device Model: iPhone 5s
    • System Name: iOS
    • System Version: 7.0.4

Changes from v1

  • The identify method now works the same as it does in our JavaScript library. That is, recording identities no longer aliases known identities to one another.
    For example, if a user has been given a known identity such as “user@example.com” and a second call is made to identify: “another@example.com”, the SDK no longer assumes that this a second known identity for the same user and these identities will no longer be aliased to one another automatically.
  • Now, if there are 2 or more known identities of a user, a call to alias these two identities must be made.
  • Previously, a call to clearIdentity() was required before a new users’s identity was set. Though, it’s still a good idea to call clearIdentity() when a user signs out.
  • The KISSMetrics class is deprecated. Use KISSmetrics.

Performance

In the new SDK, all calls to record events, properties, identities and aliases are immediately passed onto a background thread for processing and delivery.

In our version 1, the processing of these calls was handled by the same thread that made the call to record before being delivered asynchronously. This wasn’t optimal as an application’s main thread is responsible for view updates and user interactions.

As a third party addition to our customers’ apps, we should use as little of the main thread as possible to ensure that our SDK doesn’t contribute to any lag in the users’ experience of their apps.

Send Queue Archiving

The SDK will archive all activity in a send queue and attempt to deliver from that queue as connectivity permits.

iOS v2 SDK specs

  • Size (compiled): 942kb
  • Memory Overhead
    • idle: 330.32kb (under iOS 7)
    • per avg. event with properties: estimated at < 500 bytes (< 200 for required params plus assumed < 300 bytes for average event with properties). During archiving of each event with properties, approximately 3kb of memory is allocated and quickly released.
  • iOS version support: iOS 5.1 (>99% of iOS devices)
 

We have built an official Android SDK that uses our API’s common methods, and some specific to the Android platform.

Releases

https://github.com/kissmetrics/KISSmetrics-Android-SDK/releases

Setup

Within your Application subclass or main activity’s onCreate method:

// Initialize the API
KISSmetricsAPI.sharedAPI(<api key="">, <application context="">);

Initializing with Development and Production keys

boolean isDebuggable =  ( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
if (isDebuggable) {
    KISSmetricsAPI.sharedAPI("yourDevelopmentProductKeyHere", getApplicationContext());
}else{
    KISSmetricsAPI.sharedAPI("yourProductionProductKeyHere", getApplicationContext());
}

Examples

// Track Event
KISSmetricsAPI.sharedAPI().record("Activated");
KISSmetricsAPI.sharedAPI().recordEvent("Activated");    // recordEvent() is being deprecated
// Track Event with Properties
HashMap<string, string=""> properties = new HashMap<string, string="">();
properties.put("Item", "Potion");
properties.put("Amount", "10");
KISSmetricsAPI.sharedAPI().record("Purchase", properties);
// Set Properties
HashMap<string, string=""> properties = new HashMap<string, string="">();
properties.put("Item", "Potion");
properties.put("Amount", "10");
KISSmetricsAPI.sharedAPI().set(properties);
KISSmetricsAPI.sharedAPI().setProperty(properties);    // setProperty() is being deprecated
/* New Functions as of v2 */
// Track an event only once per set identity. After an identity is cleared or a new 
// identity is set, the condition will pass once again.
KISSmetricsAPI.sharedAPI().record("Viewed feature", KISSmetricsAPI.RecordCondition.RECORD_ONCE_PER_IDENTITY); 
// Track an event only once per install
KISSmetricsAPI.sharedAPI().record("Installed App", KISSmetricsAPI.RecordCondition.RECORD_ONCE_PER_INSTALL); 
// Set user properties only if they have changed on the device
HashMap<string, string=""> properties = new HashMap<string, string="">();
properties.put("OS version", "7.1");
KISSmetricsAPI.sharedAPI().setDistinct(properties);

Optional Automated Tracking

autoRecordInstalls() - Calling this records two events:

  • Installed App
  • Updated App
    autoSetAppProperties() - Calling this sets the following properties:
  • App Version : 1.0.0 (aka: versionName)
  • App Build : 1 (aka: versionCode)
    autoSetHardwareProperties() - Calling this sets the following properties:
  • Device Manufacturer : asus
  • Device Model: Nexus 7 (but this may also just be a cryptic model #. This depends on the manufacturer.)
  • System Name : Android (hard-coded)
  • System Version : 4.2.2

Changes from v1

If you previously used 80steve’s Android library, there are some functional changes from his library:

  • Identities, Events and Properties are URL encoded for you. If you’re upgrading to this SDK, you should remove any URL encoding or URL encoded characters that may have been added as a workaround to the lack of encoding in the unoffical Android SDK.
  • The identify method now works the same as it does in our JavaScript library. That is, recording identities no longer aliases known identities to one another.
    • For example, if a user has been given a known identity such as “user@example.com” and a second call is made to identify: “another@example.com”, the SDK no longer assumes that this a second known identity for the same user and these identities will no longer be aliased to one another automatically.
  • Now, if there are 2 or more known identities of a user, a call to alias these two identities must be made.
  • Previously, a call to clearIdentity() was required before a new users’s identity was set. Though, it’s still a good idea to call clearIdentity() when a user signs out.

Performance

In the new SDK, all calls to record events, properties, identities and aliases are immediately passed onto a background thread for processing and delivery.

In our unofficial SDK (v1), the processing of these calls was handled by the same thread that made the call to record before being delivered asynchronously. This wasn’t optimal as an application’s main thread is responsible for view updates and user interactions.

As a third party addition to our customers’ apps, we should use as little of the main thread as possible to ensure that our SDK doesn’t contribute to any lag in the users’ experience of their apps.

Android v2 SDK specs

  • Size (compiled): 41kb
  • Memory Overhead
    • idle: 21kb
    • per avg. event with properties: estimated at < 500 bytes (< 200 for required params plus assumed < 300 bytes for average event with properties). During archiving of each event with properties, approximately 6kb of memory is allocated.
  • Android version support: 2.2 (Froyo) (>99% of Android devices)

Our Ruby Library has the basic functionality laid out in our API specifications. It allows you to record events and set properties. However, you might miss these features from our JavaScript library:

  • No built-in mechanisms for generating and saving identities for your users
  • No built-in mechanisms for automatically tying together anonymous and named identities.
  • No built-in mechanisms for running A/B tests
  • No automatic triggering of Events (such as detecting Search Engine traffic)
    For these reasons we recommend our JavaScript Library to our users, even if you are running Ruby on Rails, and to use the Ruby Library for recording events that occur server-side (account Upgrades may be an example of one such event). You might also consider looking at other APIs our customers have created.

Setup

The best way to install the gem is using gem install kmts or by adding it to your Gemfile:

gem 'kmts', '~> 2.0.0'

The source of the gem is on Github.
Note: our previous km Ruby gem is not thread safe, so we recommend now using kmts.
You will need your API key which you can find in your site settings.

Usage

Before calling any of the common methods you must call KM.init with a valid API key:

KMTS.init('KM_KEY' [, options])

The available options are:

  • log_dir – sets the logging directory. Default is '/tmp'. Please make sure that the directory exists, and that whatever web process is writing to the log has permission to write to that directory. The log file will contain a list of the URLs that would be requested (trk.kissmetrics.com URLs – please refer to API Specifications).
  • use_cron – toggles whether to send data directly to Kissmetrics, or log to a file and send in the background via cron (see Sending Data with Cron for more information). Default is false, which means data is sent directly to Kissmetrics. Using cron is optional, but recommended.
  • to_stderr – allows toggling of printing output to stderr. Default is true.
  • dryrun – New option as of November 25, 2012. Toggles whether to send data to Kissmetrics, or just log it to a file to review for debugging. Default is false, which means data is sent to Kissmetrics, regardless of whether you’re working in a production or development environment.
  • env – Updated option as of November 25, 2012. The environment variable now just helps us name the log files that store the history of event requests. This uses the Rails and Rack variables to determine if your Ruby environment is in development. If the Rails and Rack variables are not available, we default to production.
    Example:

KMTS.init("this is your key", :log_dir => '/var/logs/kissmetrics/')

Example Calls

KMTS.record('bob@bob.com', 'Viewed Homepage')
KMTS.record('bob@bob.com', 'Signed Up', {'Plan' => 'Pro', 'Amount' => 99.95})
KMTS.record('bob@bob.com', 'Signed Up', {'_d' => 1, '_t' => 1234567890})
KMTS.set('bob@bob.com', {:gender=>'male', 'Plan Name' => 'Pro'})
KMTS.alias('bob', 'bob@bob.com')

Troubleshooting

If you were watching for the events in Kissmetrics Live and did not see them, it helps to review what our library logged. In the log directory, you may see these files:

  • kissmetrics_production_sent.log
  • kissmetrics_production_query.log
  • kissmetrics_production_error.log
  • kissmetrics_development_sent.log
  • kissmetrics_development_query.log
  • kissmetrics_development_error.log

If you contact support to troubleshoot, please refer to the contents of these files, if possible.

Suggest Edits

Ruby on Rails

 

I. Using the Analytical Gem

If you are installing Kissmetrics in your Rails app, the JavaScript Library may actually be more appropriate for your frontend tracking. Consider using the Analytical RubyGem to help ease integration of multiple analytics products into your Rails app.

RubyGem link: http://rubygems.org/gems/analytical
GitHub link: https://github.com/jkrall/analytical

II. Another Rails Integration

This is an example on how to integrate Kissmetrics into a Ruby on Rails application using the basic Ruby Library.

  1. Begin by placing the Ruby Library script in your lib/ directoy.
  2. In the controller you wish to use, create a before_filter
  3. Identify the user you want to track
  4. Record your events and properties
    That’s it! Let’s look at each individual step in more detail.

Step 1 - Install the library

Simply place the km.rb file you downloaded in to your lib/ directory. Copying the script in your lib/ directory allows Rails to source the Kissmetrics code. There is no need to require anything.

Step 2 - Create the before_filter

Initialize Kissmetrics before making any requests by calling KM.init. You can include this in a Rails initializer, or create a before_filter:

before_filter:km_init

def km_init
	KM.init(
  	'YOUR_API_KEY',
    :log_dir = File.join(RAILS_ROOT, 'log', 'km')
  )
end

Step 3 - Identify the user you want to track

There are two types of users to track here. You can track identified users, or unidentified users. The Ruby Library, unlike the JavaScript Library does not automatically provide a way to track anonymous/unidentified users. However, we often would like to track anonymous users, and then later after they have logged in, or better yet, signed up, continue keeping track of them. The following is an example how you can, through cookies, and KM.alias continue keeping track of such users.

before_filter:km_init

def generate_identifier
  now = Time.now.to_i
  Digest::MD5.hexidigest(
    (request.referrer || '') + rand(now).to_s +
     now.to_s + 
    (request.user_agent || '')
  )
end

def km_init
  KM.init(
  	'YOUR_API_KEY',
    :log_dir = File.join(RAILS_ROOT, 'log', 'km')
  )
  
  if not identity = cookies[:km_identity]
    identity = generate_identifier
    cookies[:km_identity] = {
      :value => identity,
      :expires => 5.years.from_now
    }
	end
  
  # This example assumes you have a current_user, with a property "email". Use whatever makes sense for you app.
  
  if current_user
    if not cookies[:km_aliased]
      KM.alias(identity, current_user.email)
      cookies[:km_identity] = {
        :value => true,
      	:expires => 5.years.from_now
    	}
    end
    KM.identify(identity)
  end
end
   

Step 4 - Record your events and properties

You can refer back to the Ruby Library for example API calls.

While it is possible to record data exclusively with the PHP library it is generally used as a supplement to the Javascript library to track purchase data or anything else you might want to track server side. Our PHP Library has the basic functionality laid out in our API specifications. It allows you to record events and set properties. However, the PHP library will be missing these core automatic features from our JavaScript library:

  • Built-in mechanisms for generating and saving identities for your users
  • Built-in mechanisms for automatically tying together anonymous and named identities.
  • Built-in mechanisms for running A/B tests
  • Automatic triggering of Events (such as detecting Search Engine traffic)
  • Using the Event Library and Click to Track
    For these reasons we recommend our JavaScript Library to our users, and to use the PHP Library for recording events that occur server-side (account Upgrades may be an example of one such event). You might also consider looking at other APIs our customers have created. If you plan to track exclusively with PHP please contact support for more information on fully utilizing your implementation.

Setup

You can download a copy of the API from:

https://github.com/kissmetrics/kissmetrics-php

You will need your API key which you can find in your site settings.

Usage

Before calling any of the common methods, you must initialize with a valid API key:

<?
  require( 'KISSMetrics/KM.php' );
  $km = new KISSmetrics\Client($KM_KEY, KISSmetrics\Transport\Sockets::initDefault()); // Initialize
?>

Example Calls

<?php
  $km->identify('bob@example.com')   // Identify user (always)
    ->set(array('gender' => 'male')) // Set a property
    ->record('Viewed Homepage')      // Record an event
    ->record('Signed Up', array('Plan' => 'Pro', 'Amount' => 99.95));     // Record an event with properties
  if (isset( $_COOKIE['km_ai'] )) {
    $km->alias( $_COOKIE['km_ai'] );  // Alias to previously anonymous user, if applicable
  }
  $km->submit();  // Submit all queued items in one method
?>

Our Python Library has the basic functionality laid out in our API specifications. It allows you to record events and set properties. However, you might miss these features:

  • No built-in mechanisms for generating and saving identities for your users
  • No built-in mechanisms for automatically tying together anonymous and named identities.
  • No built-in mechanisms for running A/B tests
  • No automatic triggering of Events (such as detecting Search Engine traffic)
  • Does not support scheduling sending data via cron

For these reasons we recommend our JavaScript Library to our users, and to use the Python Library for recording events that occur server-side (account Upgrades may be an example of one such event). You might also consider looking at other APIs our customers have created.

Setup

You can download a copy of the API from: https://github.com/kissmetrics/py-KISSmetrics. You will need your API key which you can find in your site settings.

Example Calls

import KISSmetrics  # Load the Kissmetrics library
KM = KISSmetrics.Client('mySuperSecretApiKey')  # Initialize a client with a valid API key
KM.record('bob@bob.com', 'Viewed Homepage')
KM.record('bob@bob.com', 'Signed Up', {'Plan' : 'Pro', 'Amount' : 99.95})
KM.record('bob@bob.com', 'Signed Up', 1234567890)  # Pass in an optional timestamp
KM.set('bob@bob.com', {'gender': 'male'})

Additional Reading

There are more detailed examples and documentation at http://py-kissmetrics.readthedocs.org/.

Troubleshooting

If you were watching the events in Kissmetrics Live and did not see them, it helps to review what our library logged. The Python library keeps a log file at:

  • /tmp/kissmetrics_error.log
    If you contact support to troubleshoot, please refer to the contents of these files, if possible.
Suggest Edits

3rd-Party Libraries

The following libraries are 3rd-party libraries written by the Kissmetrics community.

 

Kissmetrics does not provide official support for these libraries, may not have reviewed them to see if they work, and makes no warranty about them. They are still worth investigating at your own risk if your application can’t use our official libraries.

If you have a library you would like to add to this list, email us at support@kissmetrics.com

Appcelerator

Braintree (Ruby)

C#

Django

Drupal

ExtJS

Grails

Java

Meteor

Node.js

PHP

Python

Ruby

WordPress (JavaScript/PHP)

Go

  • KissmetricsGo – The simple way to send kiss events from your go project.

Writing your Own

If Kissmetrics doesn’t support your chosen language or you are not satisfied with the official and 3rd-party offerings you can always write your own library. The Kissmetrics Tracking API is very simple. Please see API Specifications for more information. And if you do write your own, please tell us about it!

Suggest Edits

Introduction

REST API

 

Our REST API is the main channel for getting data out of Kissmetrics. We have endpoints for account-level data (like the names of your events, properties, report names, etc), but more importantly, we offer a slew of ad-hoc query options for you to query your product data that is stored in your Kissmetrics instance.

The REST API is in beta!

Please take note that the REST API is still under heavy development and subject to regular changes. These changes could result in breaking changes for developers. We will try our best to notify the community of any breaking changes and help ensure a smooth transition if necessary.

Suggest Edits

Authorization

 

Kissmetrics Query API validates all requests to ensure they are authorized to take the actions requested.

Requests issued against the Kissmetrics Query API must include an authorization header. Kissmetrics Query API validates all requests to ensure that requests are authorized to take the actions requested. If you omit an authorization header or supply an invalid authorization header, Kissmetrics Query API will respond with the following:

{
  "status": 401,
  "messages": [
    "Unauthenticated."
  ]
}

If you are supplying appropriate credentials, but you are not authorized to take the requested actions,
Kissmetrics Query API will respond with the following:

{
  "status": 403,
  "messages": [
    "You are not authorized to access this page."
  ]
}

To successfully authorize each request, you'll need to provide a BASE64-encoded string of your credentials in the form username:password to the Authorization header.

$ echo -n "support@kissmetrics.com:MyPassword123" | base64
> c3VwcG9ydEBraXNzbWV0cmljcy5jb206d3V0YW5nMTIz

To the resulting string, add the "Basic " prefix:

Authorization: Basic c3VwcG9ydEBraXNzbWV0cmljcy5jb206d3V0YW5nMTIz

For curl requests, the header may be included as such:

curl --header 'Authorization: Basic c3VwcG9ydEBraXNzbWV0cmljcy5jb206d3V0YW5nMTIz'
Suggest Edits

Query Types

 

Supported Query Types:

  • people_search
  • cohort
  • ab_test
  • funnel
  • metric
  • metric_over_time
  • people_count
  • activity
Suggest Edits

Products

Get a list of products for the authorized user.

 

Basic Auth

 Authentication is required for this endpoint.
gethttps://query.kissmetrics.com/v3/products
curl --request GET \
  --url https://query.kissmetrics.com/v3/products
var request = require("request");

var options = { method: 'GET',
  url: 'https://query.kissmetrics.com/v3/products' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/products")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://query.kissmetrics.com/v3/products");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/products"

response = requests.request("GET", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "data": [
    {
      "id": 123,
      "account_id": 456,
      "name": "Your Product",
      "timezone": "US/Pacific"
    }
  ]
}

Query Params

offset
int32

An offset into the list of returned items. The API will return the requested number of items start at that offset.

limit
int32

A limit on the number of objects to be returned.

 
Suggest Edits

Reports

Get a list of reports for a given product for the authorized user.

 

Basic Auth

 Authentication is required for this endpoint.
gethttps://query.kissmetrics.com/v3/products/product_id/reports
curl --request GET \
  --url https://query.kissmetrics.com/v3/products/product_id/reports
var request = require("request");

var options = { method: 'GET',
  url: 'https://query.kissmetrics.com/v3/products/product_id/reports' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/products/product_id/reports")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://query.kissmetrics.com/v3/products/product_id/reports");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/products/product_id/reports"

response = requests.request("GET", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "reports": [
    {
      "id": 6789,
      "product_id": 12345,
      "name": "My Funnel Report",
      "report_type": "funnel"
    }
  ]
}

Path Params

product_id
string
required

Query Params

offset
int32

An offset into the list of returned items. The API will return the requested number of items start at that offset.

limit
int32

A limit on the number of objects to be returned.

 
Suggest Edits

Events

Get a list of events for a given product for the authorized user.

 

Basic Auth

 Authentication is required for this endpoint.
gethttps://query.kissmetrics.com/v3/products/product_id/events
curl --request GET \
  --url https://query.kissmetrics.com/v3/products/product_id/events
var request = require("request");

var options = { method: 'GET',
  url: 'https://query.kissmetrics.com/v3/products/product_id/events' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/products/product_id/events")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://query.kissmetrics.com/v3/products/product_id/events");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/products/product_id/events"

response = requests.request("GET", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "events": [
    {
      "id": 6789,
      "product_id": 12345,
      "name": "Visited site",
      "visible": true,
      "total": 29,
      "first_used_at": "1970-01-01T00:00:00Z",
      "last_used_at": "1970-01-01T00:00:00Z",
      "first_sent_at": "1970-01-01T00:00:00Z",
      "last_sent_at": "1970-01-01T00:00:00Z"
    }
  ]
}

Path Params

product_id
string
required

Query Params

offset
int32

An offset into the list of returned items. The API will return the requested number of items start at that offset.

limit
int32

A limit on the number of objects to be returned.

 

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

$.get('http://yoursite.com/test/' + id, function(data) {
    console.log(data);
});
Suggest Edits

Properties

Get a list of properties for a given product for the authorized user.

 

Basic Auth

 Authentication is required for this endpoint.
gethttps://query.kissmetrics.com/v3/products/product_id/properties
curl --request GET \
  --url https://query.kissmetrics.com/v3/products/product_id/properties
var request = require("request");

var options = { method: 'GET',
  url: 'https://query.kissmetrics.com/v3/products/product_id/properties' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/products/product_id/properties")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://query.kissmetrics.com/v3/products/product_id/properties");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/products/product_id/properties"

response = requests.request("GET", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "events": [
    {
      "id": 6789,
      "product_id": 12345,
      "name": "Visited site",
      "visible": true,
      "total": 29,
      "first_used_at": "1970-01-01T00:00:00Z",
      "last_used_at": "1970-01-01T00:00:00Z",
      "first_sent_at": "1970-01-01T00:00:00Z",
      "last_sent_at": "1970-01-01T00:00:00Z"
    }
  ]
}

Path Params

product_id
string
required

Query Params

offset
int32

An offset into the list of returned items. The API will return the requested number of items start at that offset.

limit
int32

A limit on the number of objects to be returned.

 
Suggest Edits

Saved Report Query

Run the query associated with a saved report.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries/report
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries/report
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries/report' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries/report")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries/report");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries/report"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "id": "279139f5-f0e7-4f1c-bae5-ff509489981d"
}

Body Params

report_id
int32
required

The unique identifier of the report to run

date_range
json

The timeframe for which to run the query. *The date_range parameter is required for all query types except people_search. See Date Range Object for examples.

query_type
string

The type of query to run. *Default query type is pulled from the given report. See Query Types) for a list.

sort
string

The field to sort the results by.

order
string

The sort order if sort parameter is provided. One of asc or desc.

 
Suggest Edits

Saved Metric Query

Run the query associated with a saved metric.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries/metric
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries/metric
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries/metric' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries/metric")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries/metric");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries/metric"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "id": "279139f5-f0e7-4f1c-bae5-ff509489981d"
}

Body Params

metric_id
int32
required

The unique identifier of the metric to run

date_range
json

The timeframe for which to run the query. *The date_range parameter is required for all query types except people_search. See Date Range Object for examples.

query_type
string

The type of query to run. *Default query type is pulled from the given report. See Query Types) for a list.

sort
string

The field to sort the results by.

order
string

The sort order if sort parameter is provided. One of asc or desc.

 
Suggest Edits

Ad-hoc Query

Run a custom query that is not associated with a report or metric.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "id": "279139f5-f0e7-4f1c-bae5-ff509489981d"
}

Body Params

product_id
int32
required

The unique identifier of the product to query.

query_type
string

The type of query to run. *Default query type is pulled from the given report. See Query Types) for a list.

query_params
json
required

The object that outlines all of the ad-hoc query's conditions. See Query Params for details and examples.

sort
string

The field to sort the results by.

order
string

The sort order if sort parameter is provided. One of asc or desc.

 

Kissmetrics Query API supports the running of queries that are not tied to saved reports. An example of an ad-hoc query is when users run queries from inside Kissmetrics prior to saving their report or run queries after making un-saved changes to a saved report.

To run an ad-hoc query, Kissmetrics Query API requires the product ID, the type of query to be run, and the parameters for the query.

Suggest Edits

Query Results

Get the status and results of a query that has been started.

 

Basic Auth

 Authentication is required for this endpoint.
gethttps://query.kissmetrics.com/v3/queries/id
curl --request GET \
  --url https://query.kissmetrics.com/v3/queries/id
var request = require("request");

var options = { method: 'GET',
  url: 'https://query.kissmetrics.com/v3/queries/id' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries/id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://query.kissmetrics.com/v3/queries/id");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries/id"

response = requests.request("GET", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Path Params

id
string
required

Query ID

Query Params

sort
string
order
string
offset
int32
limit
int32
 
Suggest Edits

Cancel a Query

Cancel a query that has been previously started.

 

Basic Auth

 Authentication is required for this endpoint.
deletehttps://query.kissmetrics.com/v3/queries/id
curl --request DELETE \
  --url https://query.kissmetrics.com/v3/queries/id
var request = require("request");

var options = { method: 'DELETE',
  url: 'https://query.kissmetrics.com/v3/queries/id' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries/id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Delete.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("DELETE", "https://query.kissmetrics.com/v3/queries/id");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries/id"

response = requests.request("DELETE", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

{
  "id": "279139f5-f0e7-4f1c-bae5-ff509489981d",
  "cancelled": true
}

Path Params

id
string
required

Query ID

 
Suggest Edits

Export to CSV

The results of both ad-hoc queries and saved report queries can be exported to CSV. The Kissmetrics Query API requires the type of export (CSV), the ID of the query to export (obtained from running an ad-hoc report or running a saved report), and a name parameter to assign to the CSV file.

The response for this endpoint provides an export ID, which can be used to check the status of the export (unstarted/started/completed/error/cancelled) or to obtain the link to the CSV.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/exports
curl --request POST \
  --url https://query.kissmetrics.com/v3/exports
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/exports' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/exports")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/exports");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/exports"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

export_type
string
required

csv

query_id
string
required
options
object
 
options.name
string
required
options.email
string
 
Suggest Edits

Export to S3

The results of both ad-hoc queries and saved report queries can be exported to a S3 bucket in CSV format. The Kissmetrics Query API requires S3 bucket details and the ID of the query to export (obtained from running an ad-hoc report or running a saved report).

The S3 bucket must be writable or else the export will fail. See the documentation for instructions on how to give write access to Kissmetrics.

The response for this endpoint provides an export ID, which can be used to check the status of the export (unstarted/started/completed/error/cancelled).

If an error occurs and an email is provided, an error notification will be sent to the specified address.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/exports
curl --request POST \
  --url https://query.kissmetrics.com/v3/exports
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/exports' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/exports")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/exports");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/exports"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

export_type
string
required

s3

query_id
string
required
options
object
 
options.bucket
string
required
options.path
string
required
options.email
string
 
Suggest Edits

Export Status

This endpoint provides information on the status of active exports and also provides the link to the CSV for completed CSV exports. This endpoint requires the export ID obtained from exporting a query.

 

Basic Auth

 Authentication is required for this endpoint.
gethttps://query.kissmetrics.com/v3/exports/export_id
curl --request GET \
  --url https://query.kissmetrics.com/v3/exports/export_id
var request = require("request");

var options = { method: 'GET',
  url: 'https://query.kissmetrics.com/v3/exports/export_id' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/exports/export_id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "https://query.kissmetrics.com/v3/exports/export_id");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/exports/export_id"

response = requests.request("GET", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Path Params

export_id
string
required
 
Suggest Edits

A/B Test Queries

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

product_id
string
required

The unique identifier of the product to query.

query_type
string
required

The type of query to run, in this case it's ab_test.

sort
string

The field to sort the results by.

order
string

The sort order if sort parameter is provided. One of asc or desc.

query_params
object
 
query_params.variation_property_index
int32
required

The index of the property that contains the variation of the experiment.

query_params.conversion_event_index
int32
required

The index of the conversion event that should be compared between variations.

query_params.start_date
date-time
required

The start date of the experiment.

query_params.end_date
date-time

The end date of the experiment.

query_params.bucket_size
int32
required

The time range for each bucket - measured in seconds. (E.G.; 604800)

 
{
  "product_id": "1234",
  "query_type": "ab_test",
  "query_params": {
    "variation_property_index": 125, // Welcome email variation
    "conversion_event_index": 957, // Premium Subscription
    "start_date": "1498494140", // Mon Jun 26 2017 09:22:20 GMT-0700 (PDT)
    "end_date": "1505889399", // Tue Sep 19 2017 23:36:39 GMT-0700 (PDT)
    "bucket_size": 604800 // 7 days
  }
}
Suggest Edits

People Search (Group) Query

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

product_id
int32
required

The unique identifier of the product to query.

query_type
string
required

The type of query to run, in this case it's people_search.

sort
string

The field to sort the results by

order
string

string The sort order if sort parameter is provided. One of asc or desc.

query_params
object
 
query_params.type
string
required

The type of people search to run, in this case group.

query_params.filter
json
required

The Filter to apply to the query.

query_params.calculations
json
 

People Search queries help find people who share something in common - an event they all do or a trait they all share. People Searches can return both groups of people and individual people. This section of the reference docs outlines the former (see People Search (Identity) Query for the latter).

To run an ad-hoc People Search query against the Kissmetrics REST API, you will need to generate query params that contain the query filter and result columns. In general, a People Search (Group Search) query is of the form:

  • Find people who did "all of these":
    • Has done event "Visited site" at least 1 time today
  • Add columns to results:
    • Latest "KM Browser" property value today

Here's how we break the above query down into filter and calculation properties:

"query_params": {
  // Find people who
  "type": "group",
  
  // did "all of these"
  "filter": {
    "type": "and",
    "operands": [{
      "negate": false,
      "type": "event",
      "event": 2,
      "frequency_occurence": "at_least",
      "frequency_value": 1,
      "date_range": {
        "type": "preset",
        "preset": "today"
      }
    }]
  },
  
  // and add these columns to the result
  "calculations": [{
    "type": "last_value_in_range",
    "subject": {
      "negate": false,
      "type": "property",
      "comparison_mode": "any_value",
      "property": 755,
      "date_range": {
        "type": "preset",
        "preset": "today"
      }
    }
  }]
}
Suggest Edits

People Search (Identity) Query

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries
{
  "type": "identity",
  "identity": "support@kissmetrics.com",
  "calculations": [
    {
      "type": "first_date_in_range",
      "subject": {
        "type": "property",
        "property": 123,
        "negate": false,
        "comparison_mode": "contains",
        "comparison_string": "support@kissmetrics.com",
        "date_range": {
          "date_range_id": "last_7_days"
        }
      }
    }
  ]
}
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

product_id
int32
required

The unique identifier of the product to query.

query_type
string
required

The type of query to run, in this case it's people_search.

sort
string

The field to sort the results by.

order
string

The sort order if sort parameter is provided. One of asc or desc.

query_params
object
 
query_params.type
string
required

The type of people search to run, in this case identity.

query_params.identity
string
required

The ID of the person to query.

query_params.calculations
json
required

Array of Calculation Objects

 

For running an ad-hoc People Search query against Kissmetrics Query API, you will need to generate query params that contain the query's conditions and result columns. In general, a People Search (Identity Search) query is of the form:

  • Find a person: "support@kissmetrics.com"
  • Add columns to results:
    • property "Amount"'s average value today

Here's how we break the above queries down into filter attributes:

English Filter
Find a person type: "identity"
"support@kissmetrics.com" identity: "support@kissmetrics.com"
Add columns to results calculations: [<Result Column/Calculation>, ...]
Suggest Edits

People Count Query

Returns the total number of people that fall into a people search.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

product_id
int32
required
query_type
string
required
sort
string
order
string
query_params
object
 
query_params.conditions
json
query_params.segments
json

Array of Segments.

 

Basic Auth

 Authentication is required for this endpoint.
posthttps://query.kissmetrics.com/v3/queries
curl --request POST \
  --url https://query.kissmetrics.com/v3/queries
var request = require("request");

var options = { method: 'POST',
  url: 'https://query.kissmetrics.com/v3/queries' };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
require 'uri'
require 'net/http'

url = URI("https://query.kissmetrics.com/v3/queries")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
var data = JSON.stringify(false);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://query.kissmetrics.com/v3/queries");

xhr.send(data);
import requests

url = "https://query.kissmetrics.com/v3/queries"

response = requests.request("POST", url)

print(response.text)
A binary file was returned

You couldn't be authenticated

Try the API to see results

Body Params

product_id
int32
required
query_type
string
required

default: sql

query_params
object
required
 
query_params.statement
string
required

The SQL query to execute.

 

Schema

All tracking requests are available in a single denormalized table: records.

The records table has the following schema:

  • timestamp_ms: Timestamp of the record in milliseconds.
  • event: The internal event ID, or NULL if the tracking request was to /s (set properties) or /a (alias a pair of identifiers).
  • person: The internal person ID, e.g. 45678.
  • person_id: The person's identifier string, e.g. 1ebd5dad-d185-4834-8ca5-c60b5f07bee6 or foo@bar.com.
  • email: A copy of the person's identifier, but only if it's an email address. If not, this is NULL.
  • remote_ip: The IP address that sent the tracking request.
  • orig_person: When aliasing (/a) this is the original person ID.
  • dest_person: When aliasing (/a) this is the person ID after the alias operation.
  • year: The year extracted from the record timestamp.
  • month: The month extracted from the record timestamp.
  • channel: The KM Channel property, or NULL if this is not applicable.
  • channel_source: The KM Channel Source property, or NULL if this is not applicable.
  • channel_with_source: The KM Channel with Source property, or NULL if this is not applicable.
  • previous_page: The KM Previous Page property, or NULL if this is not applicable.
  • referrer: The KM Referrer property, or NULL if this is not applicable.
  • new_vs_returning: The KM New Vs Returning property, or NULL if this is not applicable.

Example: count the number of tracking requests in 2017:

SELECT COUNT(*) FROM records WHERE year = 2017

Filtering By Tracking Call

To select aliasing rows (anything sent with the /a tracking request), use:

SELECT ... FROM records WHERE is_alias()

To select rows that were sent with a /s request, use:

SELECT ... FROM records WHERE is_set()

Filtering By Event

Use the is_event(nameOrID) function:

  • SELECT COUNT(*) FROM records WHERE is_event("visited site"), or
  • SELECT COUNT(*) FROM records WHERE is_event(23)

Selecting Property Values

Use the property_value(nameOrID) function:

  • SELECT property_value("referrer") FROM records, or
  • SELECT property_value(45) FROM records

To select a numeric property value, use the numeric_property_value(nameOrID) function instead.

To check if a property exists, use the has_property(nameOrID) function.

Suggest Edits

Calculations

 

Calculations are used in People Search queries to add columns to the results. For example, you can add a column that calculates the "last KM Browser over the last 7 days".

Calculation object properties

Property Name
Required?
Description

label

No

A string that labels this calculated column, used in exports.

date_range_label

No

A string that labels the date range of the calculated column, used in exports.

type

Yes

If the subject is an event filter:

  • "first_date_in_range"
  • "last_date_in_range"
  • "total_times_in_range"


    If the subject is a property filter:
  • "first_date_in_range"
  • "last_date_in_range"
  • "first_value_in_range"
  • "last_value_in_range"
  • "total_value_in_range"
  • "min_value_in_range"
  • "max_value_in_range"
  • "avg_value_in_range"
  • "median_value_in_range"

subject

Yes

A Filter object.
Note: *The REST API does not currently support the passing of Logical Operator Filters as a subject.

Calculation object examples

"calculations": [{
  "type": "last_value_in_range",
  "label": "KM Browser",
  "date_range_label": "Last 7 days"
  "subject": {
    "negate": false,
    "type": "property",
    "comparison_mode": "any_value",
    "property": 755,
    "date_range": {
      "type": "preset",
      "preset": "today"
    }
  }
}]
"calculations": [{
  "type": "total_times_in_range",
  "label": "Site Visits",
  "date_range_label": "Today"
  "subject": {
    "negate": false,
    "type": "event",
    "event": 1,
    "date_range": {
      "type": "preset",
      "preset": "today"
    }
  }
}]
 

Filters let you isolate and analyze subsets of your data. You can use filters on any query type, including the Funnel Report, Cohort Report, People Search, and A/B Test Report.

They can be used to isolate data by specific events or properties. Furthermore, you can use filters to isolate data where a person did a certain event with a specific property or trait. Filters become even more powerful when you nest or chain them together using logical operators.

Filter object properties

Property Name
Required?
Description

type

Yes

Filter type:

  • event
  • property

negate

No

Set to true when you want to filter data by "has not done event" or "does not have trait", instead of the default positive "has done" or "has trait".

with_property

No

Set to true when you want to filter by events which have a specific property attached to it.

while_property

No

Set to true when you want to filter by events where the person has a specific trait.

event

Yes, if the type is event.

The index of the event.

property

Yes, if one of the following:

  • type is event
  • with_property is true
  • while_property is true

The index of the property.

first_ever

Optional if the filter type is event

Set to true when you want to filter data where the person did the the event for the first time.

frequency_occurrence

Optional if the filter type is event.

Set to at_least, at_most, or exactly if you want to filter by the number of times an event was done.

frequency_value

Yes, if both of following are true:

  • type is event
  • frequency_occurrence is set

The value used to calculate the frequency occurrence. For example, at least 1 or exactly 6.

comparison_mode

Optional if one of the following:

  • type is property
  • with_property is true
  • while_property is true

The comparison mode to use when filtering by traits, events with properties, or events with traits. Use comparison modes to filter for specific property or trait values. See Property Filter Comparison Modes.

comparison_string

Required if comparison_mode is not in_range, any_value, or empty.

The string to use when comparing property or trait values.

comparison_range_min

Required when the comparison_mode is in_range.

The minimum value when comparing a property or trait that should be within a range.

comparison_range_max

Required when the comparison_mode is in_range.

The maximum value when comparing a property or trait that should be within a range.

date_range

No

A Date Range object.

Property Filter Comparison Modes

Property Value Type Acceptable Comparison Modes
Text values "contains", "begins_with", "ends_with"
Numeric values "greater_than", "less_than", "greater_than_or_equal_to", "less_than_or_equal_to", "in_range"
Any values "any_value", "empty", "equals"

Logical Operators

More complex queries will require that multiple filters be chained together as is the case for constructing:

  • All who did/have any of these events/properties (type: "or")
  • All who did/have all of these events/properties (type: "and")
  • All who did/have all of these events/properties in order (type: "then")

Here's how the filter object looks when using logical operators:

Property Name
Required?
Description

type

Yes

One of the following:

  • or
  • and
  • then

operands

Yes

An array of Filters. This can also include nested logical operators, see Nesting Filters .

date_range

No

A Date Range object.

Nesting Logical Operators

Filters can be nested to abitrary depths by placing Logical Operators within Logical Operators. To visualize, imagine a tree, where Logical Operators can be either AND/OR/THEN and filters can be Event, Property, or EventWithProperty:

            AND
          /     \
        OR       OR
       /  \      /  \
  Event  Event  AND  Property
               /  \
        Property   EventWithProperty
Suggest Edits

Date Ranges

This page describes the options for date ranges that can be used in REST API calls.

 

Date range periods operate over the range 1970-01-01 to 2050-01-01. They are start
date and end date inclusive.

UI Label
Rolling
Type
Date/Preset
Start
End
Rounded

On

no

on

2017-01-15

2017-01-15T00:00:00Z

2017-01-15T23:59:59Z

no

Exactly

yes

on

-P3D

4.days.ago

3.days.ago

no

Before

no

before

2017-01-15

1970-01-01T00:00:00Z

2017-01-14T23:59:59Z

no

More than

yes

before

-P3D

1970-01-01T00:00:00Z

3.days.ago - 1.second

no

After

no

after

2017-01-15

2017-01-16T00:00:00Z

0.days.ago

yes*

Less than

yes

after

-P3D

3.days.ago

0.days.ago

no

Ever

no

preset

ever

1970-01-01T00:00:00Z

2050-01-01T00:00:00Z

no

Today

yes

preset

today

0.days.ago.beginning_of_day

1.day.from_now.beginning_of_day

yes

Yesterday

yes

preset

yesterday

1.day.ago.beginning_of_day

0.days.ago.beginning_of_day

yes

Last x days

yes

preset

last_x_days

x.days.ago.beginning_of_day

0.days.ago.beginning_of_day

yes

This month to date

yes

preset

this_month_to_date

0.days.ago.beginning_of_month

0.days.ago.beginning_of_day

yes

Between

no/no

between

2017-01-15/2017-01-16

2017-01-15T00:00:00Z

2017-01-16T23:59:59Z

yes*

N/A

no/yes

between

2017-01-15/-P2D

2017-01-15T00:00:00Z

2.days.ago

yes*

N/A

yes/no

between

-P2D/2017-01-16

2.days.ago

2017-01-16T23:59:59Z

yes*

Serializations

// "on"
{
  "type": "on",
  "date": "2017-01-15"
}

// "exactly"
{
  "type": "on",
  "date": "-P2D"
}

// "before"
{
  "type": "before",
  "date": "2017-01-15"
}

// "more than"
{
  "type": "before",
  "date": "-P2D"
}

// "after"
{
  "type": "after",
  "date": "2017-01-15"
}

// "less than"
{
  "type": "after",
  "date": "-P2D"
}

// "between" with two static dates
{
  "type": "between",
  "date": "2017-01-15/2017-01-16"
}

// "between" with two rolling dates
{
  "type": "between",
  "date": "-P5D/-PT48H"
}

// "between" with a static date and a rolling date
{
  "type": "between",
  "date": "2017-01-15/-P2D"
}

// "between" with a rolling date and a static date
{
  "type": "between",
  "date": "-P2D/2017-01-16"
}

// "between" with a future period
{
  "type": "between",
  "date": "-P5D/P4D"
}

// "last x days"
{
  "type": "preset",
  "preset": "last_7_days"
}

// "ever"
{
  "type": "preset",
  "preset": "ever"
}

// "today"
{
  "type": "preset",
  "preset": "today"
}

// "yesterday"
{
  "type": "preset",
  "preset": "yesterday"
}

// "this month to date"
{
  "type": "preset",
  "preset": "this_month_to_date"
}

Rolling Periods

Rolling date ranges change depending on the time they are run. A report run
at 2016-03-15T12:00:00Z with a range from 4 hours ago to now would effectively
be retrieving the range 2016-03-15T08:00:00Z to 2016-03-15T12:00:00Z, while the
same report run exactly 48 hours later would be querying over
2016-03-17T08:00:00Z to 2016-03-17T12:00:00Z. A static report's date range will
not change like this.

UI Label Type Date/Preset Start End Rounded
Exactly on -P3D 4.days.ago 3.days.ago no
More than before -P3D 1970-01-01T00:00:00Z 3.days.ago - 1.second no
Less than after -P3D 3.days.ago 0.days.ago no
Range between -P5D/-PT48H 5.days.ago 48.hours.ago no
Today preset today 0.days.ago.beginning_of_day 1.day.from_now.beginning_of_day yes
Yesterday preset yesterday 1.day.ago.beginning_of_day 0.days.ago.beginning_of_day yes
Last x days preset last_x_days x.days.ago.beginning_of_day 0.days.ago.beginning_of_day yes
This month to date preset this_month_to_date 0.days.ago.beginning_of_month 0.days.ago.beginning_of_day yes

Exactly

The exactly option allows users to specify a rolling 24-hour period ending
some number of seconds ago. In the UI, users select an integer number of days
ago, but internally, we use seconds (days ago 24 hours/day 60 min/hour 60
sec/min = seconds ago or simply days ago
86400).

As an example, a user selecting "exactly 2 days ago" creates a rolling 24 hour
window from 72 hours ago to 48 hours ago.

This is serialized as:

{
  "date_range": {
    "type": "on",
    "date": "-P2D"
  }
}

More Than

The more than option allows users to specify an "unbounded", rolling period which
terminates a specified number of seconds ago. In the UI, users select an integer number of days
ago, but internally, we use seconds (days ago 24 hours/day 60 min/hour 60 sec/min = seconds ago or simply days ago 86400).

As an example, a user selecting "more than 2 days ago" creates a rolling window of time from 1970-01-01T00:00:00Z to 172,801 seconds ago.

This is serialized as:

{
  "date_range": {
    "type": "before",
    "date": "-P2D"
  }
}

Less Than

The less than option allows users to specify a rolling period bounded by a
specified number of seconds ago and the current time. In the UI, users select an integer number of days
ago, but internally, we use seconds (days ago 24 hours/day 60 min/hour 60 sec/min = seconds ago or simply days ago 86400).

As an example, a user selecting "less than 2 days ago" creates a rolling window of time from 172,799 seconds ago to 0 seconds ago.

This is serialized as:

{
  "date_range": {
    "type": "after",
    "date": "-P2D"
  }
}

Range

The between option allows users to specify two durations (or any combination of
durations and ISO8601 timestamps).

As an example, a user selecting "between 30-90 days ago"
creates a rolling period of time (-P90D/-P30D).

This is serialized as:

{
  "date_range": {
    "type": "between",
    "date": "-P90D/-P30D"
  }
}

Last X Days

The last X days preset allows users to specify a rolling period calendar-bounded by a
specified number of days ago and the end of yesterday.

As an example, a user selecting "last 7 days" creates a rolling window
of time from 7 days ago at the beginning of the day to 1 day ago at the end of the day.

We do not convert to seconds internally as this period is calendar bounded.

This is serialized as:

{
  "date_range": {
    "type": "preset",
    "preset": "last_7_days"
  }
}

Today

The today preset allows users to specify a rolling period calendar-bounded by the current day.

As an example, a user selecting "today" creates a rolling window
of time from 0 days ago at the beginning of the day to 0 days ago at the end of the day.

We do not convert to seconds internally as this period is calendar bounded.

This is serialized as:

{
  "date_range": {
    "type": "preset",
    "preset": "today"
  }
}

Yesterday

The yesterday preset allows users to specify a rolling period calendar-bounded by the previous day.

As an example, a user selecting "yesterday" creates a rolling window
of time from 1 day ago at the beginning of the day to 1 day ago at the end of the day.

We do not convert to seconds internally as this period is calendar bounded.

This is serialized as:

{
  "date_range": {
    "type": "preset",
    "preset": "yesterday"
  }
}

This month to date

The this_month_to_date preset allows users to specify a rolling period calendar-bounded
by the beginning of the month and the current day.

As an example, a user selecting "this_month_to_date" creates a rolling window
of time from 0 days ago at the beginning of the month to 1 day ago at the end of the day.

We do not convert to seconds internally as this period is calendar bounded.

This is serialized as:

{
  "date_range": {
    "type": "preset",
    "preset": "this_month_to_date"
  }
}

Static Periods

Unlike rolling date ranges, static date ranges are not dependent on the time
they are run, and they do not change. The same report run two hours apart will
always query over the same timestamps.

UI Label Type Date/Preset Start End Rounded
On on 2017-01-15 2017-01-15T00:00:00Z 2017-01-15T23:59:59Z no
Before before 2017-01-15 1970-01-01T00:00:00Z 2017-01-14T23:59:59Z no
After after 2017-01-15 2017-01-16T00:00:00Z 0.days.ago yes*
Ever preset ever 1970-01-01T00:00:00Z 2050-01-01T00:00:00Z no
Between between 2017-01-15/2017-01-16 2017-01-15T00:00:00Z 2017-01-16T00:00:00Z no

On

The on option allows users to specify a date, which becomes a bounded, static
period between 00:00:00 and 23:59:59 for that calendar date. In the UI, users
select a calendar date, but internally, we use full ISO8601 timestamps for the
range.

As an example, a user selecting "on 2016-12-24" creates a static period of time
from 2016-12-24T00:00:00Z to 2016-12-24T23:59:59Z.

This is serialized as:

{
  "date_range": {
    "type": "on",
    "date": "2016-12-24"
  }
}

Before

The before option allows users to specify a date, which becomes a
bounded, static period starting on 01-01-1970 and terminating at 23:59:59 on the
day before the specified calendar date. In the UI, users select a calendar date,
but internally, we use full ISO8601 timestamps for the range.

As an example, a user selecting "before 2016-12-24" creates a static period of
time from 1970-01-01T00:00:00Z to 2016-12-23T23:59:59Z.

This is serialized as (note that end date is exclusive):

{
  "date_range": {
    "type": "before",
    "date": "2016-12-24"
  }
}

After

The after option allows users to specify a date, which becomes a bounded period
beginning at midnight on the day after the specified calendar date and terminating
at the current time.

If a date of the form YYYY-MM-DD is passed, the start date will be at 00:00:00 on
the following day. However, if a time is provided, the start date will be at that exact time.

As an example, a user selecting "after 2016-12-24" on 2017-12-30 creates a static period of
time from 2016-12-25T00:00:00 to 2017-12-30T00:00:00.

This is serialized as:

{
  "date_range": {
    "type": "after",
    "date": "2016-12-24"
  }
}

Between

The between option allows users to specify two dates, which become
the bounds of a static period starting at midnight on the start date and ending
at 23:59:59 on the end date (assuming no time is provided). In the UI, users select two calendar dates.

As an example, a user selecting "between 2016-12-24 and 2017-01-02"
creates a static period of time from 2016-12-24T00:00:00Z to
2017-01-02T23:59:59Z.

This is serialized as:

{
  "date_range": {
    "type": "between",
    "date": "2016-12-24/2017-01-02"
  }
}

Ever

The ever option is basically all time. It is represented internally as
1970-01-01T00:00:00Z to 2050-01-01T00:00:00Z.

This is serialized as:

{
  "date_range": {
    "type": "preset",
    "preset": "ever"
  }
}