Getting Started with Keen

Keen is the embedded analytics API that makes shipping custom customer-facing analytics easy and seamless.

Track any event: visits, clicks, signups, swipes, upgrades, purchases, powerups, errors, shares, etc. with the Stream API. Then build on top of your data with the Keen Compute API, Access API, and Visualization tools.

What are we doing in this guide?

We believe that getting started with Keen APIs should be quick and easy. For the technical wizards among you, this guide will take you 5 minutes. For the sorcerer’s apprentices, this shouldn’t take much more than 12 minutes. If it does, we’ve failed. Let us know!

There are three basic steps to using Keen:

This guide will walk you through steps 1 and 2 using one of our SDKs or cURL from your terminal.

1 - Stream Your Data

Let’s get to the heart of it - event streaming. Events are the actions that are happening that you want to track. Events of a similar type are stored in event collections. Collections are created on-the-fly when you send your first event. In this example, we’ll create a new Event Collection and call it purchases, but you can pick any name you want!

Let’s post a new purchase event into the purchases event collection. The event should be in JSON format and look like this example.

{
  "username": "John Smith",
  "item": "avocado",
  "price": 4.50
}

From Your Codebase

You will send this event to Keen using one of our SDKs or cURL.

Copy and modify the following code for the SDK you’re working with. Replace PROJECT_ID, READ_KEY, and WRITE_KEY with your project’s actual values.

// npm install keen-tracking
// Put this in a web page, or run it using `node file_name.js`

const KeenTracking = require('keen-tracking');

// Configure a client instance for your project
const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY'
});

// Create a data object with the properties you want to send
const purchase = {
  username: 'John Smith',
  item: 'avocado',
  price: 4.50
};

// Send it to the 'purchases' collection
client
  .recordEvent('purchases', purchase)
  .then(result => {
    console.log('Success', result);
  })
  .catch(err => {
    console.log('Error', err);
  });
# gem install keen
# Put this in a file, and run `ruby file_name.rb`

require "keen"

# Configure a client instance for your project
Keen.project_id = "PROJECT_ID"
Keen.write_key = "WRITE_KEY"
Keen.read_key = "READ_KEY"

# Create a data object with the properties and send it to the "purchases" collection
Keen.publish(:purchases, {
  username: "John Smith",
  item: "avocado"
  price: 4.50
})
# pip install keen
# Save this in a file, and run `python file_name.py`

import keen

# Configure a client instance for your project
keen.project_id = "PROJECT_ID"
keen.write_key = "WRITE_KEY"
keen.read_key = "READ_KEY"

keen.add_event("purchases", {
  "username": "John Smith",
  "item": "avocado",
  "price": 4.50
})
<?
// php composer.phar require keen-io/keen-io:~2.5
// Save this in a file on your web server, then open that page

  use KeenIO\Client\KeenIOClient;

  // Configure a client instance for your project
  $client = KeenIOClient::factory([
      'projectId' => 'PROJECT_ID',
      'writeKey'  => 'WRITE_KEY',
      'readKey'   => 'READ_KEY'
  ]);

  // Create a data object with the properties you want to send
  $event = [
    "username" => "John Smith",
    "item" => "avocado",
    "price" => 4.50
  ]

  // Send it to the "purchases" collection
  $client->addEvent('purchases', $event);
?>
// compile 'io.keen:keen-client-api-java:5.6.0'
/* Save this in a file, then run:
javac file_name.java
java file_name
*/

// Configure a client instance for your project
KeenClient client = new JavaKeenClientBuilder().build();
KeenClient.initialize(client);

KeenProject project = new KeenProject(PROJECT_ID, WRITE_KEY, READ_KEY);
KeenClient.client().setDefaultProject(project);

// Create a data object with the properties you want to send
Map<String, Object> event = new HashMap<>();
event.put("username", "John Smith");
event.put("item", "avocado");
event.put("price", 4.50);

// Send it to the "purchases" event collection
KeenClient.client().addEvent("purchases", event);
// PM> Install-Package KeenClient
// Put this in a new file in Visual Studio, and run it!

using Keen.Core; // Replace this with Keen.NET_35 for projects targeting .NET 3.5

// Configure a client instance for your project
var projectSettings = new ProjectSettingsProvider("PROJECT_ID", writeKey: "WRITE_KEY", readKey: "READ_KEY");
var keenClient = new KeenClient(projectSettings);

// Create a data object with the properties you want to send
var purchase = new
{
  username = "John Smith",
  item = "avocado",
  price = 4.50
};

// Send it to the "purchases" collection
keenClient.AddEvent("purchases", purchase);
echo '{
  "username": "John Smith",
  "item": "avocado",
  "price": 4.50
}' > purchase1.json

curl "https://api.keen.io/3.0/projects/PROJECT_ID/events/purchases?api_key=WRITE_KEY" -H "Content-Type: application/json" -d @purchase1.json
// Add KeenClient as a dependency to your CocoaPods or Carthage project
// Then import the framework header in your project
#import <KeenClient/KeenClient.h>

// Configure a client instance to be used with your project where appropriate
// A single client instance per project/key combination is ideal
KeenClient *client =
    [[KeenClient alloc] initWithProjectID:@"PROJECT_ID" andWriteKey:@"WRITE_KEY" andReadKey:@"READ_KEY"];

// Create an event
NSDictionary *purchase = @{
    @"username": @"John Smith",
    @"item": @"avocado",
    @"price": @(4.50)
};

// Cache the event for upload
NSError *error;
[client addEvent:purchase toEventCollection:@"purchases" error:&error];
if (nil != error) {
    NSLog(@"Oops!");
}

// Upload all cached events
[client uploadWithFinishedBlock:^{
    NSLog(@"Done uploading cached events!");
}];
// Add the KeenClient SDK framework to your project using CocoaPods or Carthage
import KeenClient

// Create an instance of the client, ideally using a single client per project/key combination
let client = KeenClient(projectID: "PROJECT_ID", andWriteKey: "WRITE_KEY", andReadKey: "READ_KEY")!

// Create a dictionary containing the event details
let purchase : [String : Any] = [
    "username" : "John Smith",
    "item" : "avocado",
    "price" : 4.50
]

// Cache the event for upload
do {
    try client.addEvent(purchase, toEventCollection: "purchases")
} catch _ {
    NSLog("Oops!")
}

// Upload the event
client.upload {
    NSLog("Finished uploading cached events!")
}

There are a few things going on here:

  • We initialized the SDK Keen Client (unless you’re using cURL)
  • We send the request to Keen’s API with both the Project ID and the name of the event collection, where we store the event. In this example, the name of your EVENT_COLLECTION is purchases.
  • We’ve automatically created the purchases collection from the event you just sent. Now you have a place to stream all your future purchase events!
  • Third, we included the event JSON in the API Request.

The API response should look like:

{
  "created": true
}

If you are using one of the SDKs, you might not see the response.

Ta-da!

Once the API acknowledges that your event has been stored, it may take up to 10 seconds before it will appear in your query results.

2.1 - Check for Data

Through our Compute API, you’ll have access to a number of different tools. For the moment, let’s just worry about one: counts. It does exactly what it sounds like it does; it counts the number of times an event has occurred.

To begin with we will try a very simple version of a count. The only parameter we need for a simple count is “event_collection” - this is the set of events we will be counting. Use the same client setup code from above, but replace the add event lines with this:

Simple Count Request

// npm install keen-analysis

const KeenAnalysis = require('keen-analysis');

// Configure a client instance for your project
const client = new KeenAnalysis({
  projectId: 'PROJECT_ID',
  readKey: 'READ_KEY'
});

client
  .query({
    analysis_type: 'count',
    event_collection: 'purchases',
    timeframe: 'this_14_days'
  })
  .then(result => {
    console.log('Response', result);
  })
  .catch(err => {
    console.log('Error', err);
  });
Keen.count("purchases", :timeframe => "this_14_days")
keen.count("purchases", timeframe="this_14_days")
<?
  $totalPurchases = $client->count("purchases", ["timeframe" => "this_14_days"]);
?>
KeenQueryClient queryClient = new KeenQueryClient.Builder(project).build();

long count = queryClient.count("purchases", new RelativeTimeframe("this_14_days"));
var itemCount = keenClient.client().Query(QueryType.Count(), "purchases", null, QueryRelativeTimeframe.ThisNDays(14));
curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/count?api_key=MASTER_KEY&event_collection=purchases&timeframe=this_14_days"
// Create a query object
KIOQuery *query = [[KIOQuery alloc] initWithQuery:@"count"
                          andPropertiesDictionary:@{ @"event_collection": @"purchases",
                                                     @"timeframe": @"this_14_days"}];

// Run the query request
[client runAsyncQuery:query
    completionHandler:^(NSData *queryResponseData, NSURLResponse *response, NSError *error) {
        if (nil == error &&
            nil != queryResponseData) {
            NSDictionary *responseDictionary =
              [NSJSONSerialization JSONObjectWithData:queryResponseData options:kNilOptions error:&error];

            if (nil != error) {
                NSLog(@"Oops!");
            } else {
                NSLog(@"response: %@", responseDictionary);
            }
        }
   }];
// Create a query object
let query = KIOQuery(query: "count", andPropertiesDictionary: [ "event_collection": "purchases",
                                                                "timeframe": "this_14_days" ])

// Run the query using the client
client.runAsyncQuery(query, completionHandler: { (responseData : Data?, urlResponse : URLResponse?, error : Error?) in
   if (responseData != nil)
   {
       do {
           let responseDictionary = try JSONSerialization.jsonObject(with: responseData!, options: [])
               as! [String: AnyObject]
           NSLog("Response dictionary %@", responseDictionary)
       } catch _ {
           NSLog("Oops!")
       }
   }
})

If you print out the API response, it will be:

{
    "result": 1
}

1, Victory! We only inserted one event, so we counted one event. You just got started!!

2.2 - Analyze Your Data

Here are three more simple examples to show off other compute analyses you can do:

Average Request

Change count to average and add a target_property parameter to let the API know which numeric property you want to average. Here’s how you can run an average on the “price” property.

client
  .query({
    event_collection: 'purchases',
    analysis_type: 'average',
    target_property: 'price',
    timeframe: 'this_14_days'
  })
  .then(res => {
    console.log('Response', res);
  })
  .catch(err => {
    console.log('Error', err);
  });
Keen.average("purchases", :target_property => "price", :timeframe => "this_14_days")
keen.average("purchases", target_property="price", timeframe="this_14_days")
<?
  $totalPurchases = $client->average("purchases", [
    "target_property" => "price",
    "timeframe" => "this_14_days"
  ]);
?>
KeenQueryClient queryClient = new KeenQueryClient.Builder(project).build();

long count = queryClient.average("purchases", "price", new RelativeTimeframe("this_14_days"));
var itemCount = keenClient.client().Query(QueryType.Average("price"), "purchases", "price", QueryRelativeTimeframe.ThisNDays(14));
curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/average?api_key=MASTER_KEY&event_collection=EVENT_COLLECTION&target_property=price&timeframe=this_14_days"
// Create a query object
KIOQuery *query = [[KIOQuery alloc] initWithQuery:@"average"
                          andPropertiesDictionary:@{
                              @"event_collection": @"purchases",
                              @"target_property": @"price",
                              @"timeframe": @"this_14_days"
                          }];

// Run the query request
[client runAsyncQuery:query
    completionHandler:^(NSData *queryResponseData, NSURLResponse *response, NSError *error) {
        if (nil == error &&
            nil != queryResponseData) {
            NSDictionary *responseDictionary =
              [NSJSONSerialization JSONObjectWithData:queryResponseData options:kNilOptions error:&error];

            if (nil != error) {
                NSLog(@"Oops!");
            } else {
                NSLog(@"response: %@", responseDictionary);
            }
        }
    }];
let query = KIOQuery(query: "average", andPropertiesDictionary: [ "event_collection": "purchases",
                                                                  "target_property": "price"
                                                                  "timeframe": "this_14_days" ])

// Run the query using the client
client.runAsyncQuery(query, completionHandler: { (responseData : Data?, urlResponse : URLResponse?, error : Error?) in
    if (responseData != nil)
    {
        do {
            let responseDictionary = try JSONSerialization.jsonObject(with: responseData!, options: [])
                as! [String: AnyObject]
            NSLog("Response dictionary %@", responseDictionary)
        } catch _ {
            NSLog("Oops!")
        }
    }
})

If you print out the API response, it will be:

{
    "result": 4.5
}

Request with Interval

You can also add an interval parameter in order to break the results into buckets. Here’s an example with an average of the previous_5_minutes and a “minutely” Interval.

  client
    .query({
      event_collection: 'purchases',
      analysis_type: 'count',
      timeframe: 'previous_5_minutes',
      interval: 'minutely'
    })
    .then(res => {
      console.log('Response', res);
    })
    .catch(err => {
      console.log('Error', err);
    });
Keen.count("purchases", :interval => 'minutely', :timeframe => "previous_5_minutes")
keen.count("purchases", interval='minutely', timeframe="previous_5_minutes")
<?
  $totalPurchases = $client->count("purchases", [
      "timeframe" => "previous_5_minutes",
      "interval" => "minutely"
    ]);
?>
Query query = new Query.Builder(QueryType.AVERAGE)
  .withEventCollection("purchases")
  .withInterval("minutely")
  .withTimeframe(new RelativeTimeframe("previous_5_minutes"))
  .withTargetProperty("price")
  .build();

KeenQueryClient queryClient = new KeenQueryClient.Builder(project)
  .build()

QueryResult result = queryClient.execute(query);
var timeframe = QueryRelativeTimeframe.PreviousNMinutes(5);
var interval = QueryInterval.Minutely();

var itemCount = keenClient.client().QueryInterval(QueryType.Count(), "purchases", null, timeframe, interval);
curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/count?api_key=MASTER_KEY&event_collection=purchases&timeframe=previous_5_minutes&interval=minutely"
// Create a query object
KIOQuery *query = [[KIOQuery alloc] initWithQuery:@"count"
                          andPropertiesDictionary:@{
                              @"event_collection": @"purchases",
                              @"timeframe": @"previous_5_minutes",
                              @"interval": @"minutely"
                          }];

// Run the query request
[client runAsyncQuery:query
    completionHandler:^(NSData *queryResponseData, NSURLResponse *response, NSError *error) {
        if (nil == error &&
            nil != queryResponseData) {
            NSDictionary *responseDictionary =
              [NSJSONSerialization JSONObjectWithData:queryResponseData options:kNilOptions error:&error];

            if (nil != error) {
                NSLog(@"Oops!");
            } else {
                NSLog(@"response: %@", responseDictionary);
            }
        }
    }];

// Create a query object
let query = KIOQuery(query: "count", andPropertiesDictionary: [ "event_collection": "purchases",
                                                                "timeframe": "previous_5_minutes"
                                                                "interval": "minutely" ])

// Run the query using the client
client.runAsyncQuery(query, completionHandler: { (responseData : Data?, urlResponse : URLResponse?, error : Error?) in
    if (responseData != nil)
    {
        do {
            let responseDictionary = try JSONSerialization.jsonObject(with: responseData!, options: [])
                as! [String: AnyObject]
            NSLog("Response dictionary %@", responseDictionary)
        } catch _ {
            NSLog("Oops!")
        }
    }
})

If you print out the API response, it will be something like:

{
  "result": [
    {
      "value": 0,
      "timeframe": {
        "start": "2020-03-29T22:44:00.000Z",
        "end": "2020-03-29T22:45:00.000Z"
      }
    },
    {
      "value": 1,
      "timeframe": {
        "start": "2020-03-29T22:45:00.000Z",
        "end": "2020-03-29T22:46:00.000Z"
      }
    },
    {
      "value": 0,
      "timeframe": {
        "start": "2020-03-29T22:46:00.000Z",
        "end": "2020-03-29T22:47:00.000Z"
      }
    },
    {
      "value": 0,
      "timeframe": {
        "start": "2020-03-29T22:47:00.000Z",
        "end": "2020-03-29T22:48:00.000Z"
      }
    },
    {
      "value": 0,
      "timeframe": {
        "start": "2020-03-29T22:48:00.000Z",
        "end": "2020-03-29T22:49:00.000Z"
      }
    }
  ]
}

That’s just the beginning. You’ll find a lot more analysis tools on our website and our API Reference.

3 - Visualize your data

Now you can use our fancy keen.io/dataviz and create some charts for your data. It’s really simple.

At first you need to install Dataviz via npm i @keen.io/dataviz or by embedding

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@keen.io/dataviz@latest/dist/dataviz.min.js"></script>

on your website and you’re ready to go!

We need a container for our chart, so add one in your HTML.

<div id="container"></div>

Then we need to create a chart instance. Take a look at our new Data Visualization library to learn the interface and supported options.

Basically it’s just

const areaChart = new KeenDataviz({
  type: 'area',
  container: '#container',
});

to create a chart. But we can extend it and add some options

const areaChart = new KeenDataviz({
  type: 'area',
  container: '#container',
  widget: {
    title: {
      content: 'Purchases',
    },
    subtitle: {
      content: 'Previous 5 minutes',
    },
    legend: {
      enabled: false,
    },
  },
  settings: {
    curve: 'spline',
    strokeWidth: 2,
    margins: { top: 30, left: 45, right: 30, bottom: 60 },
    xScaleSettings: { type: 'band', formatLabel: label => new Date(label).toLocaleTimeString() },
    theme: {
      axisX: {
        enabled: true,
      },
    },
  },
});

and finally get a chart

We can send more events and get a chart like this

4 - Next Steps

Congratulations! You’ve graduated from the Keen Getting Started guide. Admittedly, we’ve just scratched the surface, but hopefully you’ve got some context on what you can build.

Now you can move onto the good stuff, build analytics into your own app, and build beautiful visualizations! Here’s the same three steps you’ve completed with ideas on how to harness the full potential of the Keen Embedded Analytics API Platform:

1. Send Your Data

You can stream data into Keen in a variety of ways. The most common is what we did above, sending events via the API, but there’s plenty of other ways:

2. Analyze Your Data

  • Run counts, sums, averages, medians, build funnels, and more using our Analysis APIs!
  • Now that you’ve sent at least one sample event, you might want to try checking out our query builder, Explorer, in Keen. Notice that all of the custom properties you send are immediately available for analysis, and you can do a lot more than counts! The Explorer has some query types, but there are even more available by API. Check out filters, funnels, and more in our analysis API docs.

3. Visualize Your Data

  • Ready to see some charts and graphs of your data? Head over to the visualization documentation to learn how to embed charts into dashboards, web pages, and apps!
Streams Compute Visualize Explore
JavaScript

iOS
Swift
Android
Java
Ruby
Python
PHP
.NET
Xamarin
Unity

Last but not least, make sure to configure your Access Keys. Access Keys can do all sorts of things, like:

  • Separate reading and writing data to specific keys
  • Only allow your customers to see their specific data
  • Automatically add data to your events

Questions? Reach out to us anytime at team@keen.io or chat with us on Keen Community Slack!