Authentication
# Set these environment variables
$ export PROJECT_ID=PROJECT_ID
$ export MASTER_KEY=MASTER_KEY
$ export READ_KEY=READ_KEY
$ export WRITE_KEY=WRITE_KEY
$ export EVENT_NAME=EVENT_NAME
Keen supports two mechanisms for authenticating HTTP API requests and one for Kafka API. All are requiring API Keys for the project you want to use.
HTTP Header
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/events/purchases \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"key": 123
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
writeKey: 'WRITE_KEY'
});
One way you can authenticate requests is with an HTTP header called “Authorization”. This method works for every API call.
HTTP Query String Parameter
Request
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/events/purchases?api_key=MASTER_KEY&key=123"
You can also authenticate requests with a query string parameter called api_key
.
Kafka Producer
To authenticate a Kafka Producer to the Keen Kafka Inbound Cluster use the SASL_SSL
security protocol and the PLAIN
SASL mechanism.
Use your PROJECT_ID as the username and the project WRITE_KEY as password.
Request
$ kafka-console-producer.sh \
--bootstrap-server b1.kafka-in.keen.io:9092,b2.kafka-in.keen.io:9092,b3.kafka-in.keen.io:9092 \
--topic "Target-collection-name" \
--producer-property security.protocol=SASL_SSL \
--producer-property sasl.mechanism=PLAIN \
--producer-property sasl.jaas.config='org.apache.kafka.common.security.plain.PlainLoginModule required username="PROJECT_ID" password="WRITE_KEY";'
from kafka import KafkaProducer
KafkaProducer(
bootstrap_servers=["b1.kafka-in.keen.io:9092", "b2.kafka-in.keen.io:9092", "b3.kafka-in.keen.io:9092"],
security_protocol="SASL_SSL",
sasl_mechanism="PLAIN",
value_serializer=lambda v: v.encode("utf-8"),
sasl_plain_username="PROJECT_ID",
sasl_plain_password="WRITE_KEY"
)
import org.apache.kafka.clients.producer.KafkaProducer;
...
Properties props = new Properties();
props.put(BOOTSTRAP_SERVERS_CONFIG, "b1.kafka-in.keen.io:9092,b2.kafka-in.keen.io:9092,b3.kafka-in.keen.io:9092");
props.put(SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
props.put(SASL_MECHANISM, "PLAIN");
props.put(SASL_JAAS_CONFIG, "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"PROJECT_ID\" password=\"WRITE_KEY\";");
props.put(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
new KafkaProducer<>(props);
Kafka Consumer
To authenticate a Kafka Consumer to the Keen Kafka Outbound Cluster use the SASL_SSL
security protocol and the PLAIN
SASL mechanism.
Use your PROJECT_ID as the username and the project READ_KEY as password.
Request
$ kafka-console-consumer.sh \
--bootstrap-server b1.kafka-out.keen.io:9092,b2.kafka-out.keen.io:9092,b3.kafka-out.keen.io:9092 \
--topic "Target-collection-name" \
--consumer-property security.protocol=SASL_SSL \
--consumer-property sasl.mechanism=PLAIN \
--consumer-property sasl.jaas.config='org.apache.kafka.common.security.plain.PlainLoginModule required username="PROJECT_ID" password="READ_KEY";'
from kafka import KafkaConsumer
KafkaConsumer(
"Target-collection-name",
bootstrap_servers=["b1.kafka-out.keen.io:9092", "b2.kafka-out.keen.io:9092", "b3.kafka-out.keen.io:9092"],
security_protocol="SASL_SSL",
sasl_mechanism="PLAIN",
sasl_plain_username="PROJECT_ID",
sasl_plain_password="READ_KEY",
group_id="YOUR_GROUP_ID"
)
import org.apache.kafka.clients.consumer.KafkaConsumer;
...
Properties props = new Properties();
props.put(BOOTSTRAP_SERVERS_CONFIG, "b1.kafka-out.keen.io:9092,b2.kafka-out.keen.io:9092,b3.kafka-out.keen.io:9092");
props.put(SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
props.put(SASL_MECHANISM, "PLAIN");
props.put(SASL_JAAS_CONFIG, "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"PROJECT_ID\" password=\"READ_KEY\";");
props.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(GROUP_ID_CONFIG, "YOUR_GROUP_ID");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
API Keys
Example
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
writeKey: 'WRITE_KEY'
});
keen_project = Keen::Client.new(
:project_id => "your_project_id",
:write_key => "your_project_write_key",
:read_key => "your_project_read_key",
)
from keen.client import KeenClient
client = KeenClient(
project_id="your_project_id",
write_key="your_project_write_key",
read_key="your_project_read_key"
)
<?php
use KeenIO\Client\KeenIOClient;
$client = KeenIOClient::factory([
'projectId' => $projectId,
'writeKey' => $writeKey,
'readKey' => $readKey
]);
?>
#import <KeenClient/KeenClient.h> // This import works for a framework version of KeenClient (CocoaPods with use_frameworks! or Carthage).
// or
#import "KeenClient.h" // If linking against a static library version (CocoaPods without use_frameworks!)
// ...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[KeenClient sharedClientWithProjectID:@"your_project_id" andWriteKey:@"your_write_key" andReadKey:@"your_read_key"];
return YES;
}
KeenClient client = new JavaKeenClientBuilder().build();
KeenProject project = new KeenProject(PROJECT_ID, WRITE_KEY, READ_KEY);
client.setDefaultProject(project);
// for Android
KeenClient client = new AndroidKeenClientBuilder(this).build();
KeenProject project = new KeenProject(PROJECT_ID, WRITE_KEY, READ_KEY);
client.setDefaultProject(project);
var prjSettings = new ProjectSettingsProvider("YourProjectID", writeKey: "YourWriteKey");
var keenClient = new KeenClient(prjSettings);
Each of your projects will have its own set of API Keys, which you can retrieve from the overview page of your project.
- Go to https://keen.io/project/
PROJECT_ID
- Click the “Access” tab
- Select and copy the appropriate key
Master Key
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
The Master Key is the most powerful API Key of all. It can be used to authenticate any API call, and is required to perform various administrative functions, such as:
Write Key
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
The Write Key is an API Key specifically for writing data. It can authenticate any API request that writes data to Keen. The Write Key is automatically created for each project, and can be regenerated. Typically, you would use the Write Key for requests described in the Stream section of the docs.
Read Key
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
The Read Key is an API Key for querying and extracting data. It can authenticate any API request to query or analyze data from Keen. Like the Write Key, the Read Key is automatically created for each project, and can be regenerated. Typically, you would use the Read Key for requests described in the Compute section of the docs.
Organization Key
The Organization Key is an API Key for programmatically managing your Keen account information. When an organization is created, it will be assigned an Organization Key automatically.
Use the Organization Key for requests described in the Access section of the docs.
Access Key
An Access Key is an API Key generated by the API to identify the source or user making a request to the Keen.
You can programmatically generate, revoke, or modify Access Keys. For example, if you wanted to have customer-facing analytics in your app, Access Keys would allow individual customers to see their own data without exposing anyone else’s data. Access Keys can also restrict where a user can send data or automatically include other data properties.
Each key has a defined scope of permitted operations. You can read more on how to create, revoke, or modify Access Keys in the full Access Keys section.
Limits
Our #1 job here at Keen is to maintain the reliability and stability of our API. As you might expect, we’ve put a number of protections in place to safeguard the service.
Rate Limiting
Request Type | Request Limits |
---|---|
Recording Events | No Limit! |
Ad-Hoc Queries | 200/minute |
Extractions | 200/minute |
Updates | 10/minute |
Deletes | 10/minute |
Delete a Collection | 100/minute |
How Rate Limits Work
- If too many requests are made in a minute, rate limiting kicks in and all requests of that type will be blocked for the next minute.
- If the number of requests goes back down under the limit, the block is lifted.
- Blocked requests return a 429 with a message about the limit.
- Being blocked in one rate limit category does not affect others.
- Limits are enforced at the project level.
- Cached Queries and Cached Datasets lookups do not contribute to ad-hoc query rate limiting. This combined with subsecond response times makes them ideal for building customer-facing embedded analytics!
Concurrency Limiting
Concurrency limits restrict the number of queries you may have executing simultaneously, with a separate limit for the number you may have awaiting execution.
Keen employs per Organization fuzzy concurrency limits for queries based on the workload we are facing. These limits are sometimes adjusted if the platform is under duress or if there are other extenuating circumstances.
In these situations the API may will return a 429.
Response Size Limit
The response for the most of the analysis types is a small json object containing the key “result”, and the computed value. Some features can greatly explode the size of the result. Mostly, these fall into following categories:
- group_by’s on a property (or set of properties) that has many distinct values,
- queries using the select_unique analysis type that have a high cardinality target_property,
- queries using interval with many sub-timeframes,
- combination of the above.
Queries with the response size larger than 150 MB will error with a meaningful message.
Fast Failure
Keen strives to begin executing your query promptly. Unfortunately sometimes the number of queries may exceed our capacity and to prevent poor experiences we will “fail fast” your queries. Queries that are not promptly executed will return a 503 status code.
Event Sizes
Individual events are limited to 900,000 bytes. Bulk event payloads are limited to 10,000,000 bytes.
Extraction Limits
Request Type | Limit |
---|---|
Synchronous Extraction | 1,000,000 events scanned; 100,000 events extracted |
Asynchronous Extraction | 10,000,000 events |
The extraction request type allows you to pull your raw event data out of Keen. If you need to extract more than the limit, break up your request into chunks by timeframe. For example, if a month’s worth of data is over 10M events, you could run one extraction for each week.
Other Query Limits
Request Type | Limit |
---|---|
Count Unique | Approximation kicks in it at 1,000,000 distinct values |
Group By | 1,000,000 unique groups |
Max Intervals | 9,000 intervals |
Median | Approximation kicks in it at 1,000,000 distinct values |
Percentile | Approximation kicks in it at 1,000,000 distinct values |
Funnel | 1,000,000 unique actors |
Cached Query Limits
Cached Queries must have a refresh_rate
greater than 4 hours, and less than 24 hours.
Timeframe Limits
Timeframe limits applies only to the Free plan. You are not allowed to query events older than 3 months on the Free plan. Events older than 3 months won’t be included in the query results.
Timeouts
Request Type | Limit |
---|---|
Max Query Response Time | 5 minutes |
The API will automatically end any query that takes longer than 5 minutes to run. Queries will return a 504 response code with the response message seen on the right.
Response
{
"message": "Your query did not finish in 300 seconds. Most likely something is wrong on our side. Please let us know at team@keen.io.",
"error_code": "QueryIncompleteError"
}
The most common reason for a long-running query is simply that the query touches a lot of data points.
How to Reduce Timeouts and Improve Query Performance
Because Keen indexes on timestamps, the easiest way to improve query speed is to reduce the timeframe of the query, or split it into smaller component chunks. A query with the timeframe “last_week” will run several times faster than a query with the timeframe “last month”. Query time grows approximately linearly as the number of datapoints your timeframe spans increases.
Errors
200: Event accepted
Note: Check the response body for individual event statuses. Some or all may have failed.
201: Event created successfully
400: Bad Request
Missing or invalid parameters. Error messages will often contain more information to help you figure out what’s wrong.
401: Unauthorized
The API Key is invalid.
403: Forbidden
The API Key is not allowed to run this request.
404: Not Found
The requested resource was not found.
429: Too many requests, see Rate Limiting
500: Internal Server Error
Something went wrong on our end. If the issue persists, give us a shout so we can investigate.
503: Service Unavailable
Our service is temporarily offline. If the issue persists, let us know.
504 - Timeout
Your query did not complete in the time allowed, see Timeouts.
Versions
Request
$ curl "https://api.keen.io/?api_key=MASTER_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.get(client.url('base'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.get({
url: client.url('base'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle errors
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
[
{
"is_public": true,
"url": "/3.0",
"version": "3.0"
},
{
"is_public": false,
"url": "/2.0",
"version": "2.0"
},
{
"is_public": false,
"url": "/1.0",
"version": "1.0"
}
]
Returns all available API versions.
HTTP Methods
Method | Authentication | Response |
---|---|---|
GET | Master Key | All available API versions |
HEAD | Master Key | Response header |
Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
A note about versioning
This API reference guide is based solely on the current version of our API (3.0), so all subsequent resources will stem from “/3.0”, like so:
https://api.keen.io/3.0/
Streams
Events
Resource
https://api.keen.io/3.0/projects/PROJECT_ID/events
Events are the individual data points collected by the Keen API. These events are stored in collections, which you can access through the Events resource. This flexible resource allows you to operate on multiple event collections for a given project with a single request. The specific actions for GET and POST requests are described in detail below.
HTTP Record a single event
Request
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"key": 123
}'
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME?api_key=WRITE_KEY&data=ENCODED_DATA"
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
client
.recordEvent('purchases', {
item: 'Avocado',
number_of_items: 10,
user: {
name: 'John Smith'
}
})
.then((response) => {
// handle successful responses
})
.catch(error => {
// handle errors
});
Keen.publish(:sign_ups, { :username => "lloyd", :referred_by => "harry" })
keen.add_event("sign_ups", {
"username": "lloyd",
"referred_by": "harry"
})
<?php
$event = ['username' => "lloyd" , "referred_by" => "harry"];
$client->addEvent('sign_ups', $event);
?>
// create an event
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSDictionary *event = [NSDictionary dictionaryWithObjectsAndKeys:@"first view", @"view_name", @"going to", @"action", nil];
[[KeenClient sharedClient] addEvent:event toEventCollection:@"tab_views" error:nil];
}
// upload event
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIBackgroundTaskIdentifier taskId = [application beginBackgroundTaskWithExpirationHandler:^(void) {
NSLog(@"Background task is being expired.");
}];
[[KeenClient sharedClient] uploadWithFinishedBlock:^(void) {
[application endBackgroundTask:taskId];
}];
}
protected void track() {
// Create an event to upload to Keen.
Map<String, Object> event = new HashMap<String, Object>();
event.put("item", "golden widget");
// Add it to the "purchases" collection in your Keen Project.
KeenClient.client().addEvent("purchases", event);
}
var purchase = new
{
category = "magical animals",
username = "hagrid",
price = 7.13,
payment_type = "information",
animal_type = "norwegian ridgeback dragon"
};
keenClient.AddEvent("purchases", purchase);
Response
{
"created": true
}
A POST or GET request authenticated with a Write Key records a single event to a given event collection.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Write Key | Record a single event |
POST | Write Key | Record a single event |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
data | URL-encoded AND base-64 encoded event body |
redirect | Optional: A URL to redirect the request toward after the event is recorded. If the protocol (http:// or https://) is omitted, we will automatically include one. |
Image Beacons
Example Image Beacon
<img src="https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME?api_key=WRITE_KEY&data=ENCODED_DATA"/>
Set a GET request URL (with a data
parameter containing a URL/base-64 encoded event body payload) as the src
attribute of an HTML image tag. When the image “loads” the event will be recorded.
URL Redirects
Example Redirect
<a href="https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME?api_key=WRITE_KEY&data=ENCODED_DATA&redirect=http://my-site.com/actual-href">Click me!</a>
Set a GET request URL as described above, with a redirect
parameter included, as the href
attribute of an anchor tag. When the anchor tag is clicked the event will be recorded and the API will forward the request to the URL supplied in the redirect
parameter.
HTTP Record multiple events
Request
# This example records 2 events to the "signups" collection and 2 events to the "purchases" collection. Notice each event created shares several common properties, including `keen.timestamp`:
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/events \
-H 'Authorization: WRITE_KEY' \
-H 'Content-Type: application/json' \
-d '{
"signups": [
{ "name" : "bob" },
{ "name" : "mary" }
],
"purchases": [
{ "price": 10 },
{ "price": 20 }
]
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
const multipleEvents = {
signups: [
{ username: 'rob' },
{ username: 'mary' }
],
purchases: [
{ item: 'avocado', price: 10 },
{ item: 'orange', price: 20 }
],
};
client
.recordEvents(multipleEvents)
.then((response) => {
// handle successful responses
})
.catch(error => {
// handle errors
});
Keen.publish_batch(
:signups => [
{ :name => "bob" },
{ :name => "mary" }
],
:purchases => [
{ :price => 10 },
{ :price => 20 }
]
)
keen.add_events({
"signups": [
{ "username": "bob" },
{ "username": "mary" }
],
"purchases": [
{ "price": 10 },
{ "price": 20 }
]
})
<?php
$signUps = [
['username' => 'bob']
['username' => 'mary']
];
$purchases = [
['purchase' => ['price' => 10]],
['purchase' => ['price' => 20]]
];
$client->addEvents(['purchases' => $purchases, 'signups' => $signUps]);
?>
// create an event, you can create multiple events before calling upload
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSDictionary *event = [NSDictionary dictionaryWithObjectsAndKeys:@"first view", @"view_name", @"going to", @"action", nil];
[[KeenClient sharedClient] addEvent:event toEventCollection:@"tab_views" error:nil];
}
// upload event
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIBackgroundTaskIdentifier taskId = [application beginBackgroundTaskWithExpirationHandler:^(void) {
NSLog(@"Background task is being expired.");
}];
[[KeenClient sharedClient] uploadWithFinishedBlock:^(void) {
[application endBackgroundTask:taskId];
}];
}
KeenClient client = KeenClient.client();
String sampleCollection = "sampleCollection";
Map<String, Object> sampleEvent = new HashMap<String, Object>();
sampleEvent.put("item", "golden widget");
for(int i = 0; i < 1000; i++) {
client.queueEvent(sampleCollection, sampleEvent);
}
client.sendQueuedEvents(); //See also: sendQueuedEventsAsync()
var client = new KeenClient(new ProjectSettingsProviderEnv(), new EventCacheMemory());
// Events are added as usual, and at any time you may transmit the cached events to the server:
client.SendCachedEvents();
Response
{
"purchases": [
{
"success": true
},
{
"success": true
}
],
"signups": [
{
"success": true
},
{
"success": true
}
]
}
Record multiple events to one or more event collections with a single request.
Note that the API expects a JSON object whose keys are the names of each event collection you want to insert into. Each key should point to a list of events to insert for that event collection. Once the API acknowledges that your event has been stored, it may take up to 10 seconds before it will appear in query results. For bulk loading events, see the bulk loading guide.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Write Key | Record multiple events across one or more event collections |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
A note about bulk-loading
When loading events in bulk, we recommend batches of 5,000 events or less. For historical events, be sure to overwrite the keen.timestamp
property, or the API will assume each event happened at the time it was received.
See the bulk loading guide for more information.
Kafka Event Streaming
Keen now offers streaming using Apache Kafka as one of the interfaces to stream and backup events making integration with external systems easy.
Producing events
Request
$ kafka-console-producer.sh \
--bootstrap-server b1.kafka-in.keen.io:9092,b2.kafka-in.keen.io:9092,b3.kafka-in.keen.io:9092 \
--topic "collection-name" \
--producer-property security.protocol=SASL_SSL \
--producer-property sasl.mechanism=PLAIN \
--producer-property sasl.jaas.config='org.apache.kafka.common.security.plain.PlainLoginModule required username="PROJECT_ID" password="WRITE_KEY";'
>{"key": 123}
>
from kafka import KafkaProducer
kafka_producer = KafkaProducer(
bootstrap_servers=["b1.kafka-in.keen.io:9092", "b2.kafka-in.keen.io:9092", "b3.kafka-in.keen.io:9092"],
security_protocol="SASL_SSL",
sasl_mechanism="PLAIN",
value_serializer=lambda v: v.encode("utf-8"),
sasl_plain_username="PROJECT_ID",
sasl_plain_password="WRITE_KEY"
)
kafka_producer.send(topic="Target-collection-name", value="{\"key\":123}")
import org.apache.kafka.clients.producer.KafkaProducer;
...
Properties props = new Properties();
props.put(BOOTSTRAP_SERVERS_CONFIG, "b1.kafka-in.keen.io:9092,b2.kafka-in.keen.io:9092,b3.kafka-in.keen.io:9092");
props.put(SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
props.put(SASL_MECHANISM, "PLAIN");
props.put(SASL_JAAS_CONFIG, "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"PROJECT_ID\" password=\"WRITE_KEY\";");
props.put(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer kafkaProducer = new KafkaProducer<>(props);
kafkaProducer.send(new ProducerRecord<>("Target-collection-name", "{\"key\":123}"));
A Kafka producer authenticated with a Write Key may stream events to a topic.
There is no need to create a topic in advance, the auto.create.topics.enable=true
.
The topic name must be unique within a project. The topic name is used as the collection name for the events streamed.
Events streamed to the Keen Kafka Inbound Cluster are processed the same way as in the HTTP API.
Consuming events
Request
kafka-console-consumer.sh \
--bootstrap-server b1.kafka-out.keen.io:9092,b2.kafka-out.keen.io:9092,b3.kafka-out.keen.io:9092 \
--topic "Target-collection-name" \
--consumer-property security.protocol=SASL_SSL \
--consumer-property sasl.mechanism=PLAIN \
--consumer-property sasl.jaas.config='org.apache.kafka.common.security.plain.PlainLoginModule required username="PROJECT_ID" password="READ_KEY";'
--from-beginning
from kafka import KafkaConsumer
KafkaConsumer(
"Target-collection-name",
bootstrap_servers=["b1.kafka-out.keen.io:9092", "b2.kafka-out.keen.io:9092", "b3.kafka-out.keen.io:9092"],
security_protocol="SASL_SSL",
sasl_mechanism="PLAIN",
sasl_plain_username="PROJECT_ID",
sasl_plain_password="READ_KEY",
group_id="YOUR_GROUP_ID"
)
polled_messages = kafka_consumer.poll()
for partition in polled_messages.values():
for consumer_record in partition:
print(consumer_record.value.decode("utf-8"))
import org.apache.kafka.clients.consumer.KafkaConsumer;
...
Properties props = new Properties();
props.put(BOOTSTRAP_SERVERS_CONFIG, "b1.kafka-out.keen.io:9092,b2.kafka-out.keen.io:9092,b3.kafka-out.keen.io:9092");
props.put(SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
props.put(SASL_MECHANISM, "PLAIN");
props.put(SASL_JAAS_CONFIG, "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"PROJECT_ID\" password=\"READ_KEY\";");
props.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(GROUP_ID_CONFIG, "YOUR_GROUP_ID");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(List.of("Target-collection-name"));
consumer.poll(Duration.of(30, ChronoUnit.SECONDS))
.forEach(record -> System.out.println(record.key() + " " +record.value()));
You can consume event streams using a Kafka Consumer authenticated to a topic with a Read Key.
There is no need to create a topic in advance, the auto.create.topics.enable=true
.
To consume from some specific event collection use the collection name as the Kafka Consumer’s topic
(if you are also using Kafka for streaming events this will be the same topic name as used by the Kafka Producer).
Events sent using HTTP API and a Kafka Producer will both be streamed to the Outbound Kafka Cluster. You can send some of your events
using HTTP API and some using a Kafka Producer and then consume all of your events using a Kafka Consumer.
All events available for consumption in the Outbound Kafka Cluster are enriched as described in Data Enrichment.
Event Collections
Resource
https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME
The Event Collection resource provides a flexible interface for operating on a single event collection. This resource has different actions for GET, POST and DELETE requests, all of which are described in detail below.
Creating a new event collection
An event collection is created the first time an event is recorded.
Inspect a single collection
Request
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME?api_key=READ_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: "PROJECT_ID",
readKey: 'READ_KEY'
});
client
.get(client.url('events', 'COLLECTION_NAME'))
.auth(client.readKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: "PROJECT_ID",
readKey: 'READ_KEY',
});
client
.get({
url: client.url('events', 'COLLECTION_NAME'),
api_key: client.readKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
<?php
$client = KeenIOClient::factory([
'projectId' => $project_id,
'masterKey' => $master_key
]);
$results = $client->getCollection(['event_collection' => 'collection_name']);
?>
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
{
"properties": {
"item.id": "num",
"item.on_sale": "bool",
"item.price": "num",
"quantity": "num",
"screen.name": "string",
"user.id": "num",
"user.level": "num",
"user.referring_source": "string"
}
}
A GET request authenticated with a Read Key returns schema information for a single event collection, along with properties (and their types), and links to sub-resources.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Return schema information for a single event collection, along with properties (and their types), and links to sub-resources. |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
Inspect all collections
Request
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/events?api_key=READ_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: "PROJECT_ID",
readKey: 'READ_KEY'
});
client
.get(client.url('events'))
.auth(client.readKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: "PROJECT_ID",
readKey: 'READ_KEY',
});
client
.get({
url: client.url('events'),
api_key: client.readKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
<?php
$client = KeenIOClient::factory([
'projectId' => $project_id,
'masterKey' => $master_key
]);
$results = $client->getCollections();
?>
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
[
{
"name": "purchases",
"properties": {
"item.id": "num",
"item.on_sale": "bool",
"item.price": "num",
"quantity": "num",
"screen.name": "string",
"user.id": "num",
"user.level": "num",
"user.referring_source": "string"
},
"url": "/3.0/projects/PROJECT_ID/events/purchases"
},
{
"name": "level_ups",
"properties": {
"from_level": "num",
"level": "num",
"screen.name": "string",
"to_level": "num",
"user.id": "num",
"user.level": "num",
"user.prior_balance": "num",
"user.referring_source": "string"
},
"url": "/3.0/projects/PROJECT_ID/events/level_ups"
},
{
"name": "logins",
"properties": {
"user.email": "string",
"user.id": "string",
"user_agent.browser": "string",
"user_agent.browser_version": "string",
"user_agent.platform": "string"
},
"url": "/3.0/projects/PROJECT_ID/events/logins"
}
]
Return schema information for the event collections in a given project, along with properties (and their types), and links to sub-resources. Up to 5000 event collections is returned.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Return schema information for all the event collections in a given project, along with properties (and their types), and links to sub-resources. |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
include_schema | Optional boolean flag indicating if properties should be returned in the response. When not provided it defaults to true . Allowed values:true , false . |
Properties
The Property resource is a simple interface for inspecting or deleting specified properties for a given event collection.
Properties are pieces of information that describe an event and relevant information about things related to that event.
While we generally believe that it can’t hurt to have too much information, we have put some practical limits in place: there cannot be more than 1,000 properties per event collection. Exceeding this number is usually caused by the dynamic naming of properties.
Inferred Data types
When an event is recorded, each property’s data type is automatically inferred to be one of the following:
Type | Description |
---|---|
string | string of characters |
number | number or decimal |
boolean | either true or false |
array | a list of data points, of the same type |
Property Name Rules
- Must be less than 256 characters long
- There cannot be any periods (.) in the name
- They cannot be a null value
Property Value Rules
- String values must be less than 10,000 characters long
- Numeric values must be between -2^63 (-9223372036854775808) and 2^63 - 1 (9223372036854775807) (inclusive)
- Values in lists must themselves follow the above rules
- Values in dictionaries must themselves follow the above rules
Property hierarchies
Example data model
{
"customer": {
"id": "0808130sdfsf801",
"email": "email@address.com"
},
"store": {
"name": "Whole Foods Market",
"address": {
"street": "2001 Market St",
"city": "San Francisco",
"state": "California"
}
}
}
We highly recommend organizing your event properties into hierarchies to alleviate confusion and de-clutter in your data model.
Properties in this example data model can be accessed in a clean and intuitive way: customer.id
and store.address.city
.
Inspect a single property
Request
$ curl "https://api.keen.io/v3/projects/PROJECT_ID/events/COLLECTION_NAME/properties/PROPERTY_NAME?api_key=READ_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: "PROJECT_ID",
readKey: 'READ_KEY'
});
client
.get(client.url('events', 'COLLECTION_NAME', 'properties', 'keen.id'))
.auth(client.readKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: "PROJECT_ID",
readKey: 'READ_KEY',
});
client
.get({
url: client.url('events', 'COLLECTION_NAME', 'properties', 'keen.id'),
api_key: client.readKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
<?php
$client = KeenIOClient::factory([
'projectId' => $project_id,
'masterKey' => $master_key
]);
$results = $client->getProperty(['event_collection' => 'example_collection_name', 'property_name' => 'example_property_name']);
?>
Response
{
"property_name": "PROPERTY_NAME",
"url": "/3.0/projects/PROJECT_ID/events/COLLECTION_NAME/properties/PROPERTY_NAME",
"type": "string"
}
Return details for a single property in a given event collection.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Return details for a single property of an event collection. |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
Timestamps
The API uses the ISO-8601 format; an international standard for representing time data. All time data is stored in UTC as well.
The ISO-8601 format is as follows:
{YYYY}-{MM}-{DD}T{hh}:{mm}:{ss}.{SSS}{TZ}
Pattern | Description |
---|---|
YYYY | Four digit year. Example: “2014” |
MM | Two digit month. Example: January would be “01” |
DD | Two digit day. Example: The first of the month would be “01” |
hh | Two digit hour. Example: The hours for 12:01am would be “00” and the hours for 11:15pm would be “23” |
mm | Two digit minute |
ss | Two digit second |
SSS | Milliseconds to the third decimal place |
TZ | Time zone offset. Specify a positive or negative integer. To specify UTC, add “Z” to the end. Example: To specify Pacific time (UTC-8 hours), you should append “-0800” to the end of your date string |
Example ISO-8601 date strings:
- 2012-01-01T00:01:00-08:00
- 1996-02-29T15:30:00+12:00
- 2000-05-30T12:12:12Z
Dynamic placeholders
Example Usage
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"ip_address": "${keen.ip}",
"user_agent": "${keen.user_agent}"
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: "PROJECT_ID",
writeKey: 'WRITE_KEY'
});
const eventBody = {
ip_address: '${keen.ip}',
user_agent: '${keen.user_agent}'
};
client.recordEvent('pageviews', eventBody, (err, res) => {
if (err) {
// Handle error
}
else {
// Handle response
}
});
Result
{
"ip_address": "192.168.0.1",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36"
}
Some properties pertain to information about the client sending data. To make this collection simple and configurable, we’ve introduced template-like strings that, when used as event properties, will change dynamically based on the client sending data.
Placeholder | Description |
---|---|
${keen.ip} | Replaced with the IP address of the client. |
${keen.user_agent} | Replaced with the user agent string of the client. |
The “keen” object
Example
{
"keen": {
"created_at": "2012-12-14T20:24:01.123000+00:00",
"timestamp": "2012-12-14T20:24:01.123000+00:00",
"id": "asd9fadifjaqw9asdfasdf939"
},
"device": {
"id": "0980980231sdfsss",
"model": "H09 Beta"
}
}
A special keen
object is automatically attached to every event when recorded. It contains three standard properties and three optional properties.
Standard properties
Property | Overwrite? | Description |
---|---|---|
id | No | A unique ID, used internally to ensure once-only writes. |
created_at | No | An ISO-8601 timestamp, set at the time each event is recorded. |
timestamp | Yes | An ISO-8601 timestamp, set to the same time as keen.created_at unless a value is already present when received (useful when bulk-loading historical data). |
Optional Properties
Example
{
"keen": {
"location": {
"coordinates": [ -88.21337, 40.11041 ]
},
"addons": [ {} ],
"uniqueness_token": "7ae05b64-eab5-4dda-a0e9-ea897fcc9c4a"
},
"user": {
"name": "Smacko",
"age": 21
}
}
Property | Description |
---|---|
keen.uniqueness_token | Your unique event ID, used for duplicate prevention. You can read more about event IDs in our data modelling guide. |
keen.location | Enable geo filtering by including a pair of coordinates within keen.location of each event. These coordinates should be specified as an array of the longitude and latitude values in decimal format (up to 6 decimal places). Please note that the coordinates are in the format longitude followed by latitude. This is a GeoJSON Standard. |
keen.addons | Enable various data enrichment add-ons by passing an array of configuration objects. This is described in detail below. |
Duplicate prevention
When sending events, it is possible to receive an error even though your event was actually saved successfully. For example, if you send an event, and the request times out, you can’t be sure whether Keen saved your event, or not. If you simply retry the request, you might end up with a duplicate event.
Uniqueness Token
Uniqueness Token example usage
{
"keen": {
"uniqueness_token": "7ae05b64-eab5-4dda-a0e9-ea897fcc9c4a"
},
"customer": {
"id": "0808130sdfsf801",
"email": "email@address.com"
}
}
In order to prevent duplicates, Keen allows you to supply a keen.uniqueness_token
, an optional property in the Keen object, in your event data.
This property can be set to any UTF-8 encoded String and must not be longer than 64 characters.
If a subsequent event with the same keen.uniqueness_token
is sent to the same collection within 31 days, it will be dropped silently.
We recommend token values to be cryptographic hashes of a list of properties uniquely identifying the event.
For example, in the case of tracking video views, an event representing the “video has been played” could have the following uniqueness token:
uniqueness_token = sha256(video_identifier + timestamp + user_session_identifier)
.
Use keen.uniqueness_token
in conjunction with a retry mechanism to achieve ‘Exactly Once’ event delivery semantics. Read more about the uniqueness token in our Data Modeling Guide.
Data Enrichment
Keen can enrich event data by parsing or joining it with other data sets. This is done through the concept of “add-ons”.
Example
# Taken from Sending A Single Event. The only change is the content of the json
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"keen": {
"addons": [
{
"name": "addon:name",
"input": {
"parameter_name" : "where.to.find.parameter"
},
"output": "where.to.put.output"
}
]
},
"user": {
"name": "Smacko",
"age": 21
}
}'
{
"keen": {
"addons": [
{
"name": "addon:name",
"input": {
"parameter_name" : "where.to.find.parameter"
},
"output": "where.to.put.output"
}
]
},
"user": {
"name": "Smacko",
"age": 21
}
}
Configuration
Add-ons can be activated by passing an array of configuration objects to the keen.addons
property of each event.
Each configuration object contains the following parameters:
Property | Description |
---|---|
name | A string indicating the name of the add-on. |
input | An object containing required parameters based on the add-on. |
output | A property in which to store the enriched data. This property can be nested, and does not necessarily need to exist prior, but cannot be placed in the keen namespace. |
IP to Geo parser
Example Usage
# Taken from Sending A Single Event. The only change is the content of the json
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"ip_address": "${keen.ip}",
"keen": {
"addons": [
{
"name" : "keen:ip_to_geo",
"input" : {
"ip" : "ip_address"
},
"output" : "ip_geo_info"
}
]
}
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
const eventBody = {
ip_address: '${keen.ip}',
keen: {
addons: [
{
name: 'keen:ip_to_geo',
input: {
ip: 'ip_address'
},
output: 'ip_geo_info'
}
]
}
};
client.recordEvent('pageviews', eventBody, (err, res) => {
if (err) {
// Handle error
}
else {
// Handle response
}
});
Result
{
"ip_address": "192.168.0.1",
"ip_geo_info": {
"city" : "San Francisco",
"province" : "California",
"country" : "United States",
"country_code": "US",
"continent" : "North America",
"postal_code" : "94122",
"coordinates" : [-122.42005, 37.77479]
},
"keen": {
"created_at": "2012-12-14T20:24:01.123000+00:00",
"timestamp": "2012-12-14T20:24:01.123000+00:00",
"id": "asd9fadifjaqw9asdfasdf939"
}
}
This add-on uses a client’s IP address to add data about the geographical location of the client when the event was recorded.
Activate this add-on
The parameters for the IP to Geo add-on are as follows:
Parameter | Description |
---|---|
name | “keen:ip_to_geo” |
input | An object with properties: - A mandatory key of “ip” with a value of the name of the property containing the IP address to parse. - An optional key “remove_ip_property” with a value true, to remove the “ip” property from the event, after the IP to GEO enrichment. { "ip": "property.containing.ip_address" } or{ "ip": "property.containing.ip_address", "remove_ip_property": true }
|
output | A property name describing where the produced object should be stored. |
Output properties
Property | Description |
---|---|
city | City associated with the client’s IP address. |
province | State/province associated with the client’s IP address. |
country | Country associated with the client’s IP address. |
country_code | ISO country code associated with the client’s IP address. |
continent | Continent associated with the client’s IP address. |
postal_code | Postal code associated with the client’s IP address. |
coordinates | List of geo coordinates, longitude followed by latitude. |
User Agent parser
Example Usage
# Taken from Sending A Single Event. The only change is the content of the json
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"user_agent": "${keen.user_agent}",
"keen": {
"addons": [
{
"name" : "keen:ua_parser",
"input" : {
"ua_string" : "user_agent"
},
"output" : "parsed_user_agent"
}
]
}
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
const eventBody = {
user_agent: '${keen.user_agent}',
keen: {
addons: [
{
name: 'keen:ua_parser',
input: {
ua_string: 'user_agent'
},
output: 'parsed_user_agent'
}
]
}
};
client.recordEvent('pageviews', eventBody, (err, res) => {
if (err) {
// Handle error
}
else {
// Handle response
}
});
Result
{
"user_agent": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3",
"parsed_user_agent" : {
"device" : {
"family" : "iPhone",
"type" : "mobile phone"
},
"browser" : {
"family" : "Chrome Mobile iOS",
"major" : 19,
"minor" : 0,
"patch" : 1084
},
"os" : {
"family" : "iOS",
"major" : 5 ,
"minor" : 1,
"patch" : 1,
"patch_minor" : null
}
},
"keen": {
"created_at": "2012-12-14T20:24:01.123000+00:00",
"timestamp": "2012-12-14T20:24:01.123000+00:00",
"id": "asd9fadifjaqw9asdfasdf939"
}
}
This add-on will take a user agent string and parse it into the device, browser, browser version, operating system, and operating system version.
Activate this add-on
Parameter | Description |
---|---|
name | “keen:ua_parser” |
input | An object with a key of “ua_string” and a value of the name of the property containing the user agent string to parse.{ "ua_string": "property.containing.ua_string" }
|
output | A property name describing where the produced object should be stored. |
Output properties
Property | Description |
---|---|
device | An object containing info about the device. |
device.family | A string containing the device family. ie: “iPhone” |
device.type | A string containing the device type ie: “mobile phone”. When user agent is invalid that property doesn’t exists. |
browser | An object containing info about the browser. |
browser.family | A string containing the family of the browser. ie: “Firefox” |
browser.major | A number indicating the major version of the browser. |
browser.minor | A number indicating the minor version of the browser. |
browser.patch | A number indicating the patch of the browser. |
os | An object containing info about the os. |
os.family | A string containing the family of the operating system. ie: “Windows 10” |
os.major | A number indicating the major version of the operating system. |
os.minor | A number indicating the minor version of the operating system. |
os.patch | A number indicating the patch of the operating system. |
os.patch_minor | A number indicating the minor version of the patch of the operating system. |
URL parser
Example Usage
# Taken from Sending A Single Event. The only change is the content of the json
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"page_url" : "http://my-website.com/cool/link?source=twitter&foo=bar/#title",
"keen" : {
"addons" : [
{
"name" : "keen:url_parser",
"input" : {
"url" : "page_url"
},
"output" : "parsed_page_url"
}
]
}
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
const eventBody = {
page_url: document.location.href,
keen: {
addons: [
{
name: 'keen:url_parser',
input: {
url: 'page_url'
},
output: 'parsed_page_url'
}
]
}
};
client.recordEvent('pageviews', eventBody, (err, res) => {
if (err) {
// Handle error
}
else {
// Handle response
}
});
Result
{
"page_url": "http://my-website.com/cool/link?source=twitter&foo=bar/#title",
"parsed_page_url": {
"protocol" : "http",
"domain" : "my-website.com",
"path" : "/cool/link",
"anchor" : "title",
"query_string" : {
"source" : "twitter",
"foo" : "bar"
}
},
"keen": {
"created_at": "2012-12-14T20:24:01.123000+00:00",
"timestamp": "2012-12-14T20:24:01.123000+00:00",
"id": "asd9fadifjaqw9asdfasdf939"
}
}
This add-on will take a well-formed URL and parse it into its component pieces for rich multi-dimensional analysis.
Activate this add-on
Parameter | Description |
---|---|
name | “keen:url_parser” |
input | An object with a key of “url” and a value of the name of the property containing the URL to parse.{ "url": "property.containing.url" }
|
output | A property name describing where the produced object should be stored. |
Output properties
Property | Description |
---|---|
protocol | A string containing the protocol of the URL (http or https). |
domain | A string containing the domain of the URL. ie: “keen.io” |
path | A string containing the path of the URL. ie: “/super-awesome-content” |
anchor | A string containing the anchor tag of the URL. ie: “title” |
query_string | An object containing key:value pairs of each parameter in the query string. ie: “source” : “twitter” |
Referrer parser
Example Usage
# Taken from Sending A Single Event. The only change is the content of the json
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"referrer" : {
"url": "https://search-engine.com?search=analytics"
},
"page": {
"url": "http://mysite.com/landing-page"
},
"keen" : {
"addons" : [
{
"name" : "keen:referrer_parser",
"input" : {
"referrer_url" : "referrer.url",
"page_url" : "page.url"
},
"output" : "referrer.info"
}
]
}
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
const eventBody = {
page: {
url: document.location.href
},
referrer: {
info: { /* Enriched */ },
url: document.referrer
},
keen: {
addons: [
{
name: 'keen:referrer_parser',
input: {
page_url: 'page.url',
referrer_url: 'referrer.url'
},
output: 'referrer.info'
}
]
}
};
client.recordEvent('pageviews', eventBody, (err, res) => {
if (err) {
// Handle error
}
else {
// Handle response
}
});
Result
{
"referrer": {
"url": "https://search-engine.com?search=analytics",
"info": {
"medium" : "SEARCH",
"source" : "search-engine.com",
"term" : "analytics"
}
},
"page": {
"url": "http://mysite.com/landing-page"
},
"keen": {
"created_at": "2012-12-14T20:24:01.123000+00:00",
"timestamp": "2012-12-14T20:24:01.123000+00:00",
"id": "asd9fadifjaqw9asdfasdf939"
}
}
This add-on will take a well-formed referrer URL and parse it into its source.
Activate this add-on
Parameter | Description |
---|---|
name | “keen:referrer_parser” |
input | An object with two properties: A key of “referrer_url” with a value of the name of the property containing the referrer URL to parse. A key of “page_url” with a value of the name of the property containing the URL of the current page. |
output | A property name describing where the produced object should be stored. |
Output properties
Property | Description |
---|---|
medium | A string containing the general category of the source of the referrer. See the chart below for potential mediums. ie: “SEARCH” |
source | A string containing the origin or source of the referrer. ie: “Google” |
term | A string containing the search term of the referrer, if it contains one. |
Potential mediums
Medium | Description |
---|---|
UNKNOWN | If the parser can’t figure out the medium. |
INTERNAL | If the domain of the referring URL matches the domain of the page_url. |
SEARCH | If search was the referrer. |
If an email client was the referrer. |
Datetime parser
Example Usage
# Taken from Sending A Single Event. The only change is the content of the json
$ curl http://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: WRITE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"keen" : {
"timestamp": "2016-05-21T16:36:40.092Z",
"addons": [
{
"name": "keen:date_time_parser",
"input": {
"date_time": "keen.timestamp"
},
"output": "timestamp_info"
}
]
}
}'
import KeenTracking from 'keen-tracking';
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY'
});
const eventBody = {
keen: {
timestamp: new Date().toISOString(),
addons: [
{
name: 'keen:date_time_parser',
input: {
date_time: 'keen.timestamp'
},
output: 'timestamp_info'
}
]
}
};
client.recordEvent('pageviews', eventBody, (err, res) => {
if (err) {
// Handle error
}
else {
// Handle response
}
});
Result
{
"keen": {
"timestamp": "2016-05-21T16:36:40.092Z"
},
"timestamp_info": {
"millisecond": 92,
"day_of_week_string": "Saturday",
"hour": 16,
"timezone_offset": 0,
"day_of_month": 21,
"day_of_week": 6,
"month": 5,
"second": 40,
"week": 20,
"year": 2016,
"minute": 36
}
}
This add-on uses a specified keen.timestamp property, but you can reference the keen.timestamp
property with providing it yourself (it will use system time when we receive the event).
Activate this add-on
The parameters for the Datetime parser add-on are as follows:
Parameter | Description |
---|---|
name | “keen:date_time_parser” |
input | An object with a key of “date_time” and a value of the name of the property containing a datetime to parse.{ "date_time": "property.containing.datetime" }
|
output | A property name describing where the produced object should be stored. |
Output properties
Property | Description |
---|---|
millisecond | The millisecond of the datetime provided. |
day_of_week_string | The day of week in string form. ie: “Saturday” |
hour | The universal time (00:00 - 24:00) hour of the datetime provided. |
timezone_offset | The numerical timezone offset from UTC of the datetime provided. ie: 0 |
day_of_month | The day of month of the datetime provided. |
day_of_week | The day of week in numerical form: ie: 5 |
month | The month of the datetime provided |
second | The second of the datetime provided |
week | The week of the datetime provided |
year | The year of the datetime provided |
minute | The minute of the datetime provided |
Compute
Analyses
You can perform a wide range of analyses in Keen by running queries on your event data. The different types of analysis and the associated queries are described in detail in this section.
Note: Numeric Aggregations on Non-Numbers
The API supports aggregations that only apply to numeric values. Minimum, maximum, average, sum and standard deviation are really only useful when applied in the context of numbers.
For this reason, we automatically filter out events that have non-numeric data when these kinds of aggregations are requested.
As an example, if you have two events in your “purchases” collection, where the “item.price” property has values 24.50 and “hello”, an Average analysis on this collection and “item.price” will return 24.50, not 12.25. The second event is completely ignored.
For this reason, amongst others, we highly recommend only using data of a single type for any particular property.
Count
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/count?api_key=READ_KEY&event_collection=COLLECTION_NAME"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'purchases',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'purchases',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("clicks", :timeframe => "this_7_days") # => 100
keen.count("clicks", timeframe="this_7_days") # => 100
<?php
$totalPurchases = $client->count("clicks", ["timeframe" => "this_7_days"]); // => 100
?>
KIOQuery *countQuery = [[KIOQuery alloc] initWithQuery:@"count" andPropertiesDictionary:@{@"event_collection": @"collection", @"timeframe": @"this_14_days"}];
[[KeenClient sharedClient] runAsyncQuery:countQuery block:countQueryCompleted];
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
long count = queryClient.count("<event_collection>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var count = keenClient.Query(QueryType.Count(), "target_collection", relativeTimeframe, timezone: timezone);
Response
{
"result": 100
}
Return the number of events in the collection matching given criteria.
This type of analysis can help answer questions such as:
- How many purchases have been made by users from Iowa in the previous two weeks?
- How many times has a landing page been viewed?
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the number of events in the event collection matching the given criteria. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the number of events in the event collection matching the given criteria. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Count Unique
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/count_unique?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count_unique \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count_unique', {
event_collection: 'purchases',
target_property: 'username',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count_unique',
eventCollection: 'purchases',
targetProperty: 'username',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count_unique("purchases", :target_property => "username", :timeframe => "this_7_days") # => 78
keen.count_unique("purchases", target_property="username", timeframe="this_7_days") # => 78
<?php
$totalItems = $client->countUnique("purchases", ["target_property" => "username", "timeframe" => "this_7_days"] ); // => 78
?>
KIOQuery *countUniqueQuery = [[KIOQuery alloc] initWithQuery:@"count_unique" andPropertiesDictionary:@{@"event_collection": @"collection", @"target_property": @"key", @"timeframe": @"this_14_days"}];
[[KeenClient sharedClient] runAsyncQuery:countUniqueQuery block:countQueryCompleted];
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
long countUnique = queryClient.countUnique("<event_collection>", "<target_property>", new AbsoluteTimeframe("2015-05-15T19:00:00.000Z","2015-06-07T19:00:00.000Z"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var countUnique = keenClient.Query(QueryType.CountUnique(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 78
}
Return the number of events with unique values, for a target property in a collection matching given criteria. A common use for this is to count the number of unique users who performed an event.
This type of analysis can help answer questions such as:
- How many unique users have logged in to my application?
- How many unique people have viewed a landing page in the previous week?
- How many different companies are using our app?
- In how many different countries is our app being used?
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the number of events in the collection containing unique values for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the number of events in the collection containing unique values for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
force_exact | A boolean value that instructs the query to fail if the result is so big it requires us to approximate the answer. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Approximation
The API will switch from an exact count to an approximated count above 1,000,000 distinct values. The optional force_exact
parameter will cause the query to fail, rather than return an approximated count. If you want to geek out about statistics, give us a shout.
Limits
Queries are rate limited at 200/minute.
Minimum
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/minimum?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/minimum \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('minimum', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'minimum',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.minimum("purchases", :target_property => "price", :timeframe => "this_7_days") # => 20.78
keen.minimum("purchases", target_property="price", timeframe="this_7_days") # => 20.78
<?php
$minimum = $client->minimum("purchases", ["target_property" => "price", "timeframe" => "this_7_days"]); // => 20.78
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double minimum = queryClient.minimum("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var minimum = keenClient.Query(QueryType.Minimum(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 20.78
}
Return the minimum numeric value for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the minimum numeric value for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the minimum numeric value for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Maximum
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/maximum?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/maximum \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('maximum', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'maximum',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.maximum("purchases", :target_property => "price", :timeframe => "this_7_days") # => 1575.52
keen.maximum("purchases", target_property="price", timeframe="this_7_days") # => 1575.52
<?php
$maximum = $client->maximum("purchases", ["target_property" => "price", "timeframe" => "this_7_days"]); // => 1575.52
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double maximum = queryClient.maximum("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var Maximum = keenClient.Query(QueryType.Maximum(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 1575.52
}
Return the maximum numeric value for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the maximum numeric value for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the maximum numeric value for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Sum
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/sum?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/sum \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('sum', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'sum',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.sum("purchases", :target_property => "price", :timeframe => "this_7_days") # => 189423.67
keen.sum("purchases", target_property="price", timeframe="this_7_days") # => 189423.67
<?php
$sum = $client->sum("purchases", ["target_property" => "price", "timeframe" => "this_7_days"]); => 189423.67
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double sum = queryClient.sum("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var sum = keenClient.Query(QueryType.sum(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 189423.67
}
Calculate the sum of all numeric values for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the sum of all numeric values for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the sum of all numeric values for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Average
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/average?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/average \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('average', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'average',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.average("purchases", :target_property => "price", :timeframe => "this_7_days") # => 58.20
keen.average("purchases", target_property="price", timeframe="this_7_days") # => 58.20
<?php
$average = $client->average("purchases", ["target_property" => "price", "timeframe" => "this_7_days"]); // => 58.20
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double average = queryClient.average("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var average = keenClient.Query(QueryType.average(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 58.20
}
Calculate the average value for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the average value for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the average value for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Median
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/median?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/median \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('median', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'median',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.median("purchases", :target_property => "price", :timeframe => "this_7_days") # => 64.1
keen.median("purchases", target_property="price", timeframe="this_7_days") # => 58.20
<?php
$median = $client->median("purchases", ["target_property" => "price", "timeframe" => "this_7_days"]); // => 64.1
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double median = queryClient.median("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var median = keenClient.Query(QueryType.Median(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 64.1
}
Calculate the median value for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
Median can have up to approximately 3% error on high cardinality data sets with extreme domains. If you need higher accuracy and are willing to sacrifice performance, let us know!
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the median value for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the median value for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Approximation
The API will switch from an exact result to an approximated result above 1,000,000 values. If you want to geek out about statistics, give us a shout.
Limits
Queries are rate limited at 200/minute.
Percentile
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/percentile?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY&percentile=PERCENTILE"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/percentile \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"percentile\": PERCENTILE,
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('percentile', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days',
percentile: 90
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'percentile',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
percentile: 90,
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.percentile("purchases", :target_property => "price", :percentile => 90, :timeframe => "this_7_days") # => 2
// Currently not supported by this SDK.
<?php
$percentile = $client->percentile("purchases", ["target_property" => "price", "percentile" => 90, "timeframe" => "this_7_days"]); // => 2
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double percentile = queryClient.percentile("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var percentile = keenClient.Query(QueryType.percentile(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": 2
}
Calculate a specified percentile value for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
Percentile can have up to approximately 3% error on high cardinality data sets with extreme domains. If you need higher accuracy and are willing to sacrifice performance, let us know!
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns a specified percentile value for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns a requested percentile value for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
percentile | Specifies the percentile to calculate, supporting 0-100 with two decimal places of precision. Example: 99.99 |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Approximation
The API will switch from an exact result to an approximated result above 1,000,000 values. If you want to geek out about statistics, give us a shout.
Limits
Queries are rate limited at 200/minute.
Select Unique
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/select_unique?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/select_unique \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('select_unique', {
event_collection: 'purchases',
target_property: 'user.email',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'select_unique',
eventCollection: 'purchases',
targetProperty: 'user.email',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.select_unique("purchases", :target_property => "user.email", :timeframe => "this_7_days") # => ["bob@aol.com", "joe@yahoo.biz", "travis@gmail.com"]
keen.select_unique("purchases", target_property="user.email", timeframe="this_7_days" ) # => ["bob@aol.com", "joe@yahoo.biz", "travis@gmail.com"]
<?php
$items = $client->selectUnique("purchases", ["target_property" => "user.email", :"timeframe" => "this_7_days"]); // => ["bob@aol.com", "joe@yahoo.biz", "travis@gmail.com"]
?>
// Supported
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
Query query = new Query.Builder(QueryType.SELECT_UNIQUE)
.withEventCollection("<event_collection>")
.withTargetProperty("click-number")
.withTimeframe(new RelativeTimeframe("this_month"))
.build();
QueryResult result = queryClient.execute(query);
if (result.isListResult()) {
List<QueryResult> listResults = result.getListResults();
for (QueryResult item : listResults) {
if (item.isLong()) {
// do something with long value
}
}
}
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var selectUnique = keenClient.Query(QueryType.SelectUnique(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": [
"bob@aol.com",
"joe@yahoo.biz",
"travis@gmail.com"
]
}
Return a list of unique property values for a target property, among all events in a collection matching given criteria.
Some example uses for this analysis type:
- List all of the email addresses for people who used a certain feature
- List all of the countries where your app is being used
- List all of the devices or browsers on which your app is being used
- List all of the users who have purchased an upgrade for your app
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns a list of unique property values for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns a list of unique property values for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Absent Values
A “missing” value is still considered a unique value. Targeting a property that has not been defined will return a single result.
This makes it possible to differentiate between events that have a property defined and those that do not. To avoid this behavior, just include an “exists” filter for that property.
Limits
Queries are rate limited at 200/minute.
Standard Deviation
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/standard_deviation?api_key=READ_KEY&event_collection=COLLECTION_NAME&target_property=TARGET_PROPERTY"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/standard_deviation \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"target_property\": \"TARGET_PROPERTY\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('standard_deviation', {
event_collection: 'purchases',
target_property: 'price',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'standard_deviation',
eventCollection: 'purchases',
targetProperty: 'price',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
double standardDeviation = queryClient.standardDeviation("<event_collection>", "<target_property>", new RelativeTimeframe("this_week"));
// Currently not supported by this SDK.
Response
{
"result": 6.75
}
Calculate the standard deviation value for a target property, among all events in a collection matching given criteria.
Non-numeric values will be ignored. If none of the property values are numeric, the API returns an error.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the standard deviation value for a target property. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the standard deviation value for a target property. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
target_property | Specifies the name of the property to analyze. |
timeframe | Limits analysis to a specific period of time when the events occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Multi-Analysis
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/multi_analysis?api_key=READ_KEY&event_collection=COLLECTION_NAME&analyses=ENCODED_ANALYSIS_OBJECT"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/multi_analysis \
-H 'Authorization: READ_KEY' \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "COLLECTION_NAME",
"analyses": {
"unique users": {
"analysis_type": "count_unique",
"target_property": "user.id"
},
"total visits": {
"analysis_type": "count"
}
},
"timeframe": "this_7_days"
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('multi_analysis', {
event_collection: 'pageviews',
analyses: {
'unique users': {
analysis_type: 'count_unique',
target_property: 'user.id'
}
'total visits': {
analysis_type: 'count'
}
},
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'multi_analysis',
eventCollection: 'pageviews',
analyses: {
'unique users': {
analysisType: 'count_unique',
targetProperty: 'user.id',
},
'total visits': {
analysisType: 'count',
}
},
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.multi_analysis("purchases", analyses: {
:avg_price => {
:analysis_type => "average",
:target_property => "price"
},
:max_price => {
:analysis_type => "maximum",
:target_property => "price"
}
},
:timeframe => "this_7_days") # => {:"avg_price" => 52.79, :"max_price" => 736.41}
keen.multi_analysis("purchases",
analyses={
"avg_price": {
"analysis_type": "average",
"target_property": "price"
},
"max_price":{
"analysis_type": "maximum",
"target_property": "price"
}
},
"timeframe": "this_7_days") # => {"avg_price": 52.79, "max_price": 736.41}
<?php
$analyses = [
"avg_price" => [
"analysis_type" => "average",
"target_property" => "price"
],
"max_price" => [
"analysis_type" => "maximum",
"target_property" => "price"
]
];
$stats = $client->multiAnalysis("purchases", ["analyses" => $analyses], ["timeframe" => "this_7_days"]);
// => ["avg_price" => 52.79, "max_price" => 736.41]
?>
KIOQuery *countQuery = [[KIOQuery alloc] initWithQuery:@"count" andPropertiesDictionary:@{@"event_collection": @"collection", @"timeframe": @"this_14_days"}];
KIOQuery *countUniqueQuery = [[KIOQuery alloc] initWithQuery:@"count_unique" andPropertiesDictionary:@{@"event_collection": @"collection", @"target_property": @"key", @"timeframe": @"this_14_days"}];
// Optionally set a name for your queries, so it's easier to check the results
[countQuery setQueryName:@"count_query"];
[countUniqueQuery setQueryName:@"count_unique_query"];
[[KeenClient sharedClient] runAsyncMultiAnalysisWithQueries:@[countQuery, countUniqueQuery] block:countQueryCompleted];
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
final MultiAnalysis multiAnalysis = new MultiAnalysis.Builder()
.withEventCollection("the_collection")
.withTimeframe(new RelativeTimeframe("this_month"))
.withSubAnalysis(new SubAnalysis("label_for_count", QueryType.COUNT))
.withSubAnalysis(new SubAnalysis("sum_analysis_label", QueryType.SUM, "property_to_sum"))
.build();
QueryResult result = this.queryClient.execute(multiAnalysis);
if (result instanceof MultiAnalysisResult) {
MultiAnalysisResult multiAnalysisResult = (MultiAnalysisResult)result;
for (String subAnalysisLabel : multiAnalysisResult.getAllResults().keySet()) {
QueryResult resultForSubAnalysis = multiAnalysisResult.getResultFor(subAnalysisLabel);
// ... do something with the results of the various sub-analyses.
}
}
IEnumerable<MultiAnalysisParam> analyses = new List<MultiAnalysisParam>()
{
new MultiAnalysisParam("purchases", MultiAnalysisParam.Metric.Count()),
new MultiAnalysisParam("max_price", MultiAnalysisParam.Metric.Maximum("price")),
new MultiAnalysisParam("min_price", MultiAnalysisParam.Metric.Minimum("price"))
};
var result = keenClient.QueryMultiAnalysis("purchases", analyses);
var purchases = int.Parse(result["purchases"]);
var maxPrice = float.Parse(result["max_price"]);
Response
{
"result": {
"unique users" : 5291,
"total visits" : 21392
}
}
Multi-analysis lets you run multiple types of analyses over the same data. For example, you could count the number of purchases you’ve had in the last week, as well as sum the price of the goods sold, all in one API call!
Multi-analysis currently supports:
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the results for multiple analyses. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
analyses | A URL-encoded JSON object that defines the multiple types of analyses to perform. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timeframe | Refines the scope of events to be included in the analysis based on when the event occurred. |
timezone | Assigns a timezone offset to relative timeframes. |
group_by | Specifies the name of a property by which to group results. Using this parameter changes the response format. |
interval | Specifies the size of time interval by which to group results. Using this parameter changes the response format. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Funnels
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/funnel \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"steps": [
{
"event_collection": "signed up",
"actor_property": "visitor.guid",
"timeframe": "this_7_days"
},
{
"event_collection": "completed profile",
"actor_property": "user.guid",
"timeframe": "this_7_days"
},
{
"event_collection": "referred user",
"actor_property": "user.guid",
"timeframe": "this_7_days"
}
]
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('funnel', {
steps: [
{
event_collection: 'signed up',
actor_property: 'visitor.guid',
timeframe: 'this_7_days'
},
{
event_collection: 'completed profile',
actor_property: 'user.guid', // = visitor.guid from the step above
timeframe: 'this_7_days'
},
{
event_collection: 'referred user',
actor_property: 'user.guid',
timeframe: 'this_7_days'
}
]
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'funnel',
steps: [
{
eventCollection: 'signed up',
actorProperty: 'visitor.guid',
timeframe: 'this_7_days',
},
{
eventCollection: 'completed profile',
actorProperty: 'user.guid', // = visitor.guid from the step above
timeframe: 'this_7_days',
},
{
eventCollection: 'referred user',
actorProperty: 'user.guid',
timeframe: 'this_7_days',
},
]
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.funnel(:steps => [
{
:event_collection => "signed up",
:actor_property => "visitor.guid",
:timeframe => "this_7_days"
},
{
:event_collection => "completed profile",
:actor_property => "user.guid",
:timeframe => "this_7_days"
},
{
:event_collection => "referred user",
:actor_property => "user.guid",
:timeframe => "this_7_days"
}
])
step1 = {
"event_collection": "signed up",
"actor_property": "visitor.guid",
"timeframe": "this_7_days"
}
step2 = {
"event_collection": "completed profile",
"actor_property": "user.guid",
"timeframe": "this_7_days"
}
step3 = {
"event_collection": "referred user",
"actor_property": "user.guid",
"timeframe": "this_7_days"
}
keen.funnel([step1, step2, step3])
<?php
$step1 = [
"event_collection" => "signed up",
"actor_property" => "visitor.guid",
"timeframe" => "this_7_days"
];
$step2 = [
"event_collection" => "completed profile",
"actor_property" => "user.guid",
"timeframe" => "this_7_days"
];
$step3 = [
"event_collection" => "referred user",
"actor_property" => "user.guid",
"timeframe" => "this_7_days"
];
$steps = [
'steps' => array($step1, $step2, $step3)
];
$client->funnel($steps);
?>
IEnumerable<FunnelStep> funnelSteps = new List<FunnelStep>
{
new FunnelStep
{
EventCollection = "registered_users",
ActorProperty = "id"
},
new FunnelStep
{
EventCollection = "subscribed_users",
ActorProperty = "user_id"
},
};
var result = keenClient.QueryFunnel(funnelSteps);
var registeredUsers = result.ElementAt(0);
var registeredAndSubscribedUserCount = result.ElementAt(1);
KIOQuery *funnelQuery = [[KIOQuery alloc] initWithQuery:@"funnel" andPropertiesDictionary:@{@"timeframe": @"this_14_days", @"steps": @[@{@"event_collection": @"user_signed_up",
@"actor_property": @"user.id"},
@{@"event_collection": @"user_completed_profile",
@"actor_property": @"user.id"}]}];
[[KeenClient sharedClient] runAsyncQuery:funnelQuery block:countQueryCompleted];
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// funnel
final Funnel funnel = new Funnel.Builder()
.withStep(new FunnelStep("signed_up", "visitor.guid", new RelativeTimeframe("this_7_days")))
.withStep(new FunnelStep("completed_profile", "user.guid", new RelativeTimeframe("this_7_days")))
.withStep(new FunnelStep("referred_user", "user.guid", new RelativeTimeframe("this_7_days", "UTC")))
.build();
QueryResult result = this.queryClient.execute(funnel);
if (result instanceof FunnelResult) {
// The result was a FunnelResult as expected.
// Cast the result to the appropriate type.
FunnelResult funnelResult = (FunnelResult)result;
// Get the sub-result for the funnel analysis
ListResult funnelListResult = funnelResult.getFunnelResult();
// Unpack the list of QueryResults for each funnel step
List<QueryResult> funnelResultData = funnelListResult.getListResults();
// Iterate through each funnel step result
for (QueryResult stepResult : funnelResultData) {
if (stepResult instanceof LongResult) {
// Do something with each result of the funnel.
long stepData = stepResult.longValue();
}
}
// Get the actors result, which may be null.
// In the case of this example, no steps requested actor values
// so it indeed would be null, but FunnelStep has an optional parameter
// to request actor values which will populate this result.
ListResult actorsResult = funnelResult.getActorsResult();
if (null != result.getActorsResult()) {
// A list of actor values was provided in the response.
// Unpack the list of lists of actors
List<QueryResult> actorsResultLists = actorsResult.getListResults();
for (QueryResult stepActorsResult : actorsResultLists) {
// Get the list of actors for this step
List<QueryResult> stepActorsResultList = stepActorsResult.getListResults();
// Iterate through all actor values
for (QueryResult actorResult : stepActorsResultList) {
// Unpack the actor value
if (actorResult instanceof StringResult) {
String actorValue = actorResult.stringValue();
}
else if (actorResult instanceof LongResult) {
long actorValue = actorResult.longValue();
}
}
}
}
}
Response
{
"result": [
3,
1,
0
],
"steps": [
{
"actor_property": "visitor.guid",
"event_collection": "signed up",
"timeframe": "this_7_days"
},
{
"actor_property": "user.guid",
"event_collection": "completed profile",
"timeframe": "this_7_days"
},
{
"actor_property": "user.guid",
"event_collection": "referred user",
"timeframe": "this_7_days"
}
]
}
Returns the number of unique actors that successfully (or unsuccessfully) make it through a series of steps. “Actors” could mean users, devices, or any other identifiers that are meaningful to you.
Actors will fall off at each step, to varying degrees, so a funnel analysis reveals where a given flow loses the most users. This helps identify areas for improvement, as well as the overall health of your business.
For example, a funnel could include these steps:
- Successful completion of an app’s tutorial
- Creation of content in the app
- Sharing of content with another user
A funnel analysis with those steps would work like this:
- Count the number of unique users who completed the app’s tutorial.
- Of the users who were counted in step 1, count the number of them who created content.
- Of the users who were counted in step 2, count the number of them who shared content.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Returns the number of unique actors that successfully (or unsuccessfully) make it through a series of provided steps. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Returns the number of unique actors that successfully (or unsuccessfully) make it through a series of provided steps. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
steps | A URL encoded JSON Array defining the steps in the funnel. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
timeframe | Refines the scope of events to be included in the analysis based on when the event occurred. |
timezone | Assigns a timezone offset to relative timeframes. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Queries are rate limited at 200/minute.
Funnels are limited to 2 million unique actors per request.
Steps
A step is defined as an event or set of events that meet given criteria. The first step, along with any filters that you provide, determine the starting data set of the funnel.
Each step includes an actor_property
(typically a user ID) that specifies the important thing you want to count in that step. Continuing our example, the first step would count the number of unique user IDs in the event collection “Tutorial Completions”.
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
actor_property | Specifies the name of the property to use as a unique identifier. |
timeframe | Refines the scope of events to be included in this step, based on when the event occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in this step, based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
Special Parameters
Response when
with_actors
is true
{
"result": [
3,
1,
0
],
"actors": [
[ "f9332409s0", "b7732409s0", "k22315b211" ],
[ "f9332409s0" ],
null
],
"steps": [
{
"actor_property": "visitor.guid",
"event_collection": "signed up",
"timeframe": "this_7_days"
},
{
"actor_property": "user.guid",
"event_collection": "completed profile",
"timeframe": "this_7_days"
},
{
"actor_property": "user.guid",
"event_collection": "referred user",
"timeframe": "this_7_days"
}
]
}
Parameter | Default | Description |
---|---|---|
inverted | false | A boolean value that excludes events matching this step. |
optional | false | A boolean value that instructs the funnel to ignore the effects of this step on subsequent steps. |
with_actors | false | A boolean value that instructs the funnel to return a list of actor_property values for this step. |
Query Parameters
Filters
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_14_days\",
\"filters\": [
{
\"property_name\" : \"price\",
\"operator\" : \"gte\",
\"property_value\" : 0.99
},
{
\"property_name\" : \"on_sale\",
\"operator\" : \"eq\",
\"property_value\" : true
}
]
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('extraction', {
event_collection: 'purchases',
filters: [
{
property_name: 'item.price',
operator: 'gt',
property_value: 10
}
],
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'extraction',
eventCollection: 'purchases',
filters: [
{
propertyName: 'item.price',
operator: 'gt',
propertyValue: 10,
},
],
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :timeframe => "this_14_days", :"filters" => [
{
"property_name" => "item.price",
"operator" => "gt",
"property_value" => 10
}
])
keen.count("purchases",
filters=[{
"property_name": "item.price",
"operator": "gt",
"property_value": 10
}],
timeframe="this_14_days")
<?php
$filters = [
["property_name" => "item.price", "operator" => "gt", "property_value" => 10]
];
$client->count("purchases", ["filters" => $filters, "timeframe" => "this_14_days"]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
Query query = new Query.Builder(QueryType.COUNT)
.withEventCollection("<event_collection>")
.withFilter("click-count", FilterOperator.GREATER_THAN, 1)
.withFilter("click-count", FilterOperator.LESS_THAN, 5)
.withTimeframe(new RelativeTimeframe("this_month"))
.build();
QueryResult result = queryClient.execute(query);
if (result.isLong()) {
long queryResult = result.longValue();
}
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var filters = new List<QueryFilter>()
{
new QueryFilter("field1", QueryFilter.FilterOperator.GreaterThan(), "1")
};
var result = keenClient.Query(QueryType.Count(), "user_registrations", relativeTimeframe, filters: filters);
Filters refine the scope of events to be included in an analysis, based on event property values. For example, a filter could be used to limit analysis to events that came from Android users.
Filters are sent as an array of JSON objects. Each JSON object (except Or Filters) has three properties, all of which are required:
Property | Description |
---|---|
property_name | Specifies the name of the property to filter. |
operator | Specifies the filter operator to use. |
property_value | The value to compare to the property specified by the property_name . |
Operator Definitions
Operator | Description |
---|---|
or | “Or” - This is a special filter that matches an event if any of its component filters match. See Or Filters. |
eq | “Equal to” – Note that if your property’s value is an array, “eq” can be used to filter for values inside that array. For example, eq: 5 will match a value of [5, 6, 7]. |
ne | “Not equal to” |
lt | “Less than” |
lte | “Less than or equal to” |
gt | “Greater than” |
gte | “Greater than or equal to” |
exists | Whether or not a specific property exists on an event record. The value passed in must be either true or false . |
in | Whether or not the property value is in a given set of values. The value passed in must be a JSON array of values. Example: [1,2,4,5]. |
contains | Whether or not the string property value contains the given sequence of characters, or list property value contains given element. |
not_contains | Whether or not the string property value does not contain the given sequence of characters, or list property value does not contain given element. |
within | Used to select events within a certain radius of the provided geo coordinate (for geo analysis only). |
regex | Matching property name with regular expression in property value. Example: [A-Z][a-z]+ will match Test , Foo , Bar , but will not match: A , AAA , aaaa . |
Not all filter operators make sense for different property data types, so only certain operators are valid for each.
Note: For contains
and not_contains
operators, the property must exist in order for the event to pass the filter. If the property does not exist, then the event will not pass the filter and will not be returned as part of the result set.
Note: We are using google/re2 syntax for regex expressions. To get more information about re2 syntax go to documentation page.
Data Type | Valid Operators |
---|---|
string | eq, ne, lt, gt, exists, in, contains, not_contains, regex |
number | eq, ne, lt, lte, gt, gte, exists, in |
boolean | eq, exists, in |
geo coordinates | within |
Or Filters
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_14_days\",
\"filters\": [
{
\"operator\" : \"or\",
\"operands\" : [
{
\"property_name\" : \"price\",
\"operator\" : \"gte\",
\"property_value\" : 9.99
},
{
\"property_name\" : \"customer.tier\",
\"operator\" : \"eq\",
\"property_value\" : \"premium\"
}
]
}
]
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'purchases',
filters: [
{
operator: 'or',
operands: [
{
property_name: 'user.full_name', // uuids, aliases etc
operator: 'eq',
property_value: 'John Smith'
},
{
property_name: 'user.full_name',
operator: 'eq',
property_value: 'Eva Smith'
}
],
}
],
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'purchases',
filters: [
{
operator: 'or',
operands: [
{
propertyName: 'user.full_name', // uuids, aliases etc
operator: 'eq',
propertyValue: 'John Smith',
},
{
propertyName: 'user.full_name',
operator: 'eq',
propertyValue: 'Eva Smith',
}
],
},
],
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Or filters allow you to filter on events that match one or more of an array of filters, but not necessarily all of them. For example you could query the count of purchase events that either were over some price threshold or were made by a premium subscriber. Simply pass the component filters in the operands
parameter. You may use any of the filter operators inside an or
filter.
Geo Filtering
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_14_days\",
\"filters\": [
{
\"property_name\" : \"keen.location.coordinates\",
\"operator\" : \"within\",
\"property_value\" : {
\"coordinates\":[ -122.42005, 37.77479 ],
\"max_distance_miles\": 30
}
}
]
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'purchases',
filters: [
{
property_name: 'keen.location.coordinates',
operator: 'within',
property_value: {
coordinates: [ -122.42005, 37.77479 ],
max_distance_miles: 30
}
}
],
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'purchases',
filters: [
{
propertyName: 'keen.location.coordinates',
operator: 'within',
propertyValue: {
coordinates: [ -122.42005, 37.77479 ],
maxDistanceMiles: 30,
},
},
],
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
If your events have geo coordinates, you can filter for results within a given radius of a given location. For example, filter events within a ten mile radius of San Francisco [-122.42005, 37.77479].
Please note that the filter takes the format longitude followed by latitude. This is a GeoJSON Standard. It’s the reverse of the order, latitude and longitude, which are displayed in many other contexts, such as Google Maps.
You can combine geo filters with other filters. The example to the right shows how you find events related to developers within 30 miles of San Francisco.
In order for geo-filtering to work, your coordinates must be recorded in the property keen.location
or in your specified output property from the IP to Geo enrichment addon.
There is one limitation to geo filtering, which is that it can’t be used in combination with a Group By request.
Group By
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "user_logins",
"timeframe": "this_14_days",
"group_by": "user.email"
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
group_by: 'user.email',
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
groupBy: 'user.email',
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :timeframe => "this_14_days", :"group_by" => "user.email")
keen.count("purchases", timeframe="this_14_days", group_by="user.email")
<?php
$client->count("clicks", ["group_by" => "user.email", "timeframe" => "this_14_days"]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
Query query = new Query.Builder(QueryType.COUNT)
.withEventCollection("<event_collection>")
.withGroupBy("click-number")
.withTimeframe(new RelativeTimeframe("this_month"))
.build();
QueryResult result = queryClient.execute(query);
if (result.isGroupResult()) {
for (Map.Entry<Group, QueryResult> groupResult : result.getGroupResults().entrySet()) {
Map<String, Object> groupProperies = groupResult.getKey().getProperties();
long groupCount = groupResult.getValue().longValue();
// ... do something with the group properties and the count result
}
}
// Supported by this SDK.
Response
{
"result": [
{
"user.email": "ryan@keen.io",
"result": 39
},
{
"user.email": "dan@keen.io",
"result": 27
},
{
"user.email": "kirk@keen.io",
"result": 32
}
]
}
The group_by
parameter groups results categorically. There are two types of group_by
arguments:
- string property_name (co-occurrence of a specified property),
- object (ranges of a specified property).
This type of analysis can help answer questions such as:
- Of our signups, how many came from iOS, Android, or Web clients?
- How much revenue has been made for each of our user cohorts?
- How many users are coming from different countries around the world?
- How many items were purchased within the given price ranges?
This parameter can be added to any of the following types of analysis:
Adding the group_by
parameter changes the structure of the response from a single value to an array of objects containing:
- The unique value for the specified
group_by
argument - The result of the analysis
Group By Buckets
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "temperature_measurements",
"timeframe": "this_14_days",
"group_by": [{
"buckets": {
"size": 10,
"offset": 5
},
"property_name": "temperature.celsius",
"alias": "temperature"
}]
}'
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
{
"result": [
{
"temperature": "[-5, 5)",
"result": 2
},
{
"temperature": "[5, 15)",
"result": 12
},
{
"temperature": "[15, 25)",
"result": 5
},
{
"temperature": "[35, 45)",
"result": 1
}
]
}
The feature is currently only available via the Keen API. It’s not yet available in the Explorer or the Saved Queries view.
Group by buckets allow you to divide a range into a series of intervals, or buckets
(e.g. count of transactions grouped in $10 increments). You can categorize numeric properties into buckets by providing the size
and the offset
from the start. A count query with group_by buckets can be visualized as a Histogram.
The formula for calculating a bucket is: X = Math.floor((property_value - offset) / size) * size + offset
, then the bucket is defined as: [X, X + size)
. The left edge is inclusive, the right edge is exclusive.
Required Parameters
Parameter | Description |
---|---|
buckets.size | The bucket width. Must be a positive number. |
property_name | The property name which values are assigned to buckets. |
Optional Parameters
Parameter | Description |
---|---|
buckets.offset | The point where buckets calculation starts. If not provided 0 is used. |
alias | The string that is returned in the query response to identify bucket values. If not provided buckets_ + N is used, where N is based on the clause position in the group_by array. |
Group By Ranges
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "purchases",
"timeframe": "this_14_days",
"group_by": [{
"ranges": [
{"to": 1, "to_closed": true},
{"from": 2, "to": 20, "from_closed": false},
{"from": 100, "key": "very_expensive"}
],
"property_name": "item.price",
"alias": "price_ranges"
}]
}'
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
{
"result": [
{
"price_ranges": "(-Infinity, 1]",
"result": 12
},
{
"price_ranges": "(2, 20)",
"result": 5
},
{
"price_ranges": "very_expensive",
"result": 1
}
]
}
The feature is currently only available via the Keen API. It’s not yet available in the Explorer or the Saved Queries view.
Group by ranges allow you to categorize numeric and string properties into explicit ranges provided in the request. All ranges present in the request will be returned in the response regardless to the chosen analysis result.
Required Parameters
Parameter | Description |
---|---|
property_name | The property name which values are assigned to buckets. |
Optional Parameters
Parameter | Description |
---|---|
ranges.from | The start of a range. Must be lower or equal than to . If not provided defaults to -Infinity . |
ranges.to | The end of a range. Must be greater or equal to from . If not provided defaults to Infinity . |
ranges.from_closed | If start of a range is inclusive. If not provided defaults to true . |
ranges.to_closed | If end of a range is inclusive. If not provided defaults to false . |
ranges.key | The string to represent the range in the response. If not provided defaults to [from, to) . |
alias | The string that is returned in the query response to identify bucket values. If not provided defaults to ranges_ + N , where N is the position in the group_by array. |
allow_range_overlap | If any two ranges are overlapping you need to confirm that you are aware of that situation by setting the property to true . If not provided defaults to false , which means that a query with any overlapping ranges will error. |
Multiple Group By Clauses
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "purchases",
"timeframe": "this_14_days",
"group_by": [ "item.name", "item.type" ]
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'purchases',
group_by: [ 'item.name', 'item.type' ],
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'purchases',
groupBy: [ 'item.name', 'item.type' ],
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :timeframe => "this_14_days", :group_by => ["item.name", "item.type"])
keen.count("purchases", timeframe="this_14_days", group_by=["item.name", "item.type"])
<?php
$client->count("clicks", ["group_by" => ["item.name", "item.type"]]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
Query query = new Query.Builder(QueryType.COUNT)
.withEventCollection("<event_collection>")
.withGroupBy("click-number")
.withGroupBy("company-id")
.withTimeframe(new RelativeTimeframe("this_month"))
.build();
QueryResult result = queryClient.execute(query);
if (result.isGroupResult()) {
for (Map.Entry<Group, QueryResult> groupResult : result.getGroupResults().entrySet()) {
Map<String, Object> groupProperies = groupResult.getKey().getProperties();
long groupCount = groupResult.getValue().longValue();
// ... do something with the group properties and the count result
}
}
// Supported by this SDK.
Response
{
"result": [
{
"item.name": "Golden Widget",
"item.type": "Widget",
"result": 39
},
{
"item.name": "Silver Widget",
"item.type": "Widget",
"result": 59
},
{
"item.name": "Milk Shake",
"item.type": "Food",
"result": 86
}
]
}
Specify more than one group by clause, by providing an array of either string property names or objects to define ranges, or the mix of both.
The response structure for this request simply contains an additional property: the unique value for the second specified group_by
argument.
Grouping By at Intervals
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "user logins",
"group_by": "user.email",
"timeframe": "previous_3_days",
"interval": "daily"
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
group_by: 'user.email',
interval: 'daily',
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
groupBy: 'user.email',
interval: 'daily',
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :group_by => "user.email", :interval => "daily", :timeframe => "previous_3_days")
keen.count("purchases", group_by="user.email", interval="daily", timeframe="previous_3_days")
<?php
$client->count("clicks", ["group_by" => "user.email", "interval" => "daily", "timeframe" => "previous_3_days"]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
Query query = new Query.Builder(QueryType.COUNT)
.withEventCollection("<event_collection>")
.withInterval("weekly")
.withGroupBy("click-number")
.withTimeframe(new RelativeTimeframe("this_month"))
.build();
QueryResult result = queryClient.execute(query);
if (result.isIntervalResult()) {
for (IntervalResultValue intervalResult : result.getIntervalResults()) {
AbsoluteTimeframe timeframe = intervalResult.getTimeframe();
for (Map.Entry<Group, QueryResult> groupResult : intervalResult.getResult().getGroupResults().entrySet()) {
Map<String, Object> groupProperies = groupResult.getKey().getProperties();
long groupCount = groupResult.getValue().longValue();
// ... do something with the group properties and the count result
}
}
}
// Supported by this SDK.
Response
{
"result": [
{
"timeframe": {
"start": "2014-08-22T00:00:00.000Z",
"end": "2014-08-23T00:00:00.000Z"
},
"value": [
{
"user.email": "ryan@keen.io",
"result": 3
},
{
"user.email": "dan@keen.io",
"result": 2
},
{
"user.email": "kirk@keen.io",
"result": 1
}
]
},
{
"timeframe": {
"start": "2014-08-23T00:00:00.000Z",
"end": "2014-08-24T00:00:00.000Z"
},
"value": [
{
"user.email": "ryan@keen.io",
"result": 0
},
{
"user.email": "dan@keen.io",
"result": 1
},
{
"user.email": "kirk@keen.io",
"result": 1
}
]
},
{
"timeframe": {
"start": "2014-08-25T00:00:00.000Z",
"end": "2014-08-26T00:00:00.000Z"
},
"value": [
{
"user.email": "ryan@keen.io",
"result": 5
},
{
"user.email": "dan@keen.io",
"result": 4
},
{
"user.email": "kirk@keen.io",
"result": 0
}
]
}
]
}
Groups results by provided criteria and at specified intervals by using group_by
in conjunction with the interval
parameter (detailed below).
Using these parameters together alters the response structure. Each sub-timeframe (which is determined by the interval attribute) has a response similar to that of a basic group_by
, containing the unique value of the property as well as the result of the analysis.
By default, every interval has the same set of group_by keys. To get only the non-zero values, set the zero_fill
parameter to false
(default: true
). This might be useful especially when you want to reduce the size of a very large response.
Order By
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "user_logins",
"timeframe": "this_14_days",
"group_by": "user.email",
"order_by": "result"
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
group_by: 'user.email',
order_by: 'result',
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
groupBy: 'user.email',
orderBy: 'result',
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
# not yet supported, let us know you're waiting!
keen.count("logins", group_by="user.email", timeframe="this_14_days", order_by={"property_name": "result"})
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
Response
{
"result": [
{
"user.email": "dan@keen.io",
"result": 27
},
{
"user.email": "kirk@keen.io",
"result": 32
},
{
"user.email": "ryan@keen.io",
"result": 39
}
]
}
The order_by
parameter orders results returned by a group_by
query. Ordering can be performed on any property that is part of the group_by
, or by the result value of the specified analysis. Additionally, ordering can be ASC for ascending (default) or DESC for descending, and can be limited to a subset of groups.
This type of analysis can help answer questions such as:
- What are the top 10 most popular article titles from last week?
- What is the rank of all states based on sum purchases during Black Friday?
- Who are the top 100 influencing users based on invites sent to friends?
This parameter can be added to any of the following types of analysis:
Adding the order_by
parameter allows you to specify the order and number of results returned in your request but does not change the data structure of your response.
Specifying descending or ascending direction for ordering
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "user_logins",
"timeframe": "this_14_days",
"group_by": "user.email",
"order_by": {"property_name": "result", "direction": "DESC"}
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
group_by: 'user.email',
order_by: {'property_name': 'result', 'direction': 'DESC'},
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
groupBy: 'user.email',
orderBy: {'property_name': 'result', 'direction': 'DESC'},
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
# not yet supported, let us know you're waiting!
keen.count("logins", group_by="user.email", timeframe="this_14_days",
order_by={"property_name": "result", "direction": keen.direction.ASCENDING})
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
Response
{
"result": [
{
"user.email": "ryan@keen.io",
"result": 39
},
{
"user.email": "kirk@keen.io",
"result": 32
},
{
"user.email": "dan@keen.io",
"result": 27
}
]
}
You can specify a direction
parameter to return results in for order_by
. If omitted, the default ordering is ascending (“ASC”).
Ordering By Multiple Properties
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "purchases",
"timeframe": "this_14_days",
"group_by": [ "item.name", "item.type" ],
"order_by": [{"property_name": "result", "direction": "DESC"}, {"property_name": "item.type", "direction": "ASC"}]
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'purchases',
group_by: [ 'item.name', 'item.type' ],
order_by: [{'property_name': 'result', 'direction': 'DESC'}, {'property_name': 'item.name', 'direction': 'ASC'}]
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'purchases',
groupBy: [ 'item.name', 'item.type' ],
orderBy: [{'property_name': 'result', 'direction': 'DESC'}, {'property_name': 'item.name', 'direction': 'ASC'}]
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
# not yet supported, let us know you're waiting!
keen.count("logins", group_by="user.email", timeframe="this_14_days", order_by=[{"property_name": "result", "direction": keen.direction.DESCENDING}, {"property_name": "item.name", "direction": keen.direction.ASCENDING}])
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
Response
{
"result": [
{
"item.name": "Milk Shake",
"item.type": "Food",
"result": 86
},
{
"item.name": "Golden Widget",
"item.type": "Widget",
"result": 39
},
{
"item.name": "Silver Widget",
"item.type": "Widget",
"result": 39
}
]
}
You can specify more than one property to order_by
, with a URL-encoded JSON array of objects. One of the multiple property names specified can be result
.
The response structure for this request orders the results by the first property name and direction specified, and in the case of a tie sorts by the second name and direction specified, then so on and so forth.
Limiting the number of groups returned
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "user_logins",
"timeframe": "this_14_days",
"group_by": "user.email",
"order_by": "result",
"limit": 1
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
group_by: 'user.email',
order_by: 'result',
limit: '1',
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
groupBy: 'user.email',
orderBy: 'result',
limit: '1',
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
# not yet supported, let us know you're waiting!
keen.count("logins", group_by="user.email", timeframe="this_14_days", limit=10, order_by={"property_name": "result"})
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
// not yet supported, let us know you're waiting!
Response
{
"result": [
{
"user.email": "dan@keen.io",
"result": 27
}
]
}
You can set a limit parameter, which will limit the number of results you get based on the direction you set. The limit parameter will apply to each interval. We will still compute the full results, but only return a certain amount to you.
For example, if you set the limit
parameter to 10 you will get the top 10 results in your timeframe. If you set a limit
parameter and have a daily interval for your timeframe, you will receive the top 10 results for each day in your timeframe.
Interval
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d '{
"event_collection": "user logins",
"timeframe": "previous_3_days",
"interval": "daily"
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
interval: 'daily',
timeframe: 'this_14_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
interval: 'daily',
timeframe: 'this_14_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :interval => "daily", :timeframe => "previous_3_days")
keen.count("purchases", interval="daily", timeframe="previous_3_days")
<?php
$client->count("clicks", ["interval" => "daily", "timeframe" => "previous_3_days"]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
Query query = new Query.Builder(QueryType.COUNT)
.withEventCollection("<event_collection>")
.withInterval("weekly")
.withTimeframe(new RelativeTimeframe("this_month"))
.build();
QueryResult result = queryClient.execute(query);
if (result.isIntervalResult()) {
for (IntervalResultValue intervalResult : result.getIntervalResults()) {
AbsoluteTimeframe timeframe = intervalResult.getTimeframe();
long intervalCount = intervalResult.getResult().longValue();
// ... do something with the absolute timeframe and count result.
}
}
// Supported by this SDK.
Response
{
"result": [
{
"timeframe": {
"start": "2014-08-22T00:00:00.000Z",
"end": "2014-08-23T00:00:00.000Z"
},
"value": 6
},
{
"timeframe": {
"start": "2014-08-23T00:00:00.000Z",
"end": "2014-08-24T00:00:00.000Z"
},
"value": 2
},
{
"timeframe": {
"start": "2014-08-25T00:00:00.000Z",
"end": "2014-08-26T00:00:00.000Z"
},
"value": 9
}
]
}
The interval
parameter groups results into sub-timeframes spanning a specified length of time.
This type of analysis can help answer questions such as:
- How many signups have occurred daily, over the past 21 days?
- How much has revenue grown per week since launching a new product?
Supported Intervals
- minutely
- hourly
- daily
- weekly
- monthly
- yearly
Custom Intervals
In addition to the above intervals, the following pattern can be used to create highly specific custom intervals: every_{n}_{units}
, where {n}
can be any whole integer greater than 0 (zero), and {units}
can be minutes, hours, days, weeks, months, or years.
Here are a few examples:
- every_30_minutes
- every_8_hours
- every_3_days
- every_2_weeks
- every_6_months
- every_3_years
Timeframe
The timeframe
parameter specifies a period of time over which to run an analysis. This refines the scope of events that are included in the analysis, based on when each event occurred.
There are two types of timeframes:
- Absolute timeframes: a fixed timeframe with an explicit start and end
- Relative timeframes: a rolling timeframe that is relative to “now”
Absolute Timeframes
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": {
\"start\": \"2012-08-13T19:00:00.000Z\",
\"end\": \"2013-09-20T19:00:00.000Z\"
}
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
timeframe: {
start: '2012-08-15T19:00:00.000Z',
end: '2017-08-15T19:00:00.000Z'
}
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
timeframe: {
start: '2012-08-15T19:00:00.000Z',
end: '2017-08-15T19:00:00.000Z',
}
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :timeframe => {
:start => "2012-08-13T19:00:00.000Z",
:end => "2013-09-20T19:00:00.000Z"
})
keen.count("purchases", timeframe={
"start": "2012-08-13T19:00:00.000Z",
"end": "2013-09-20T19:00:00.000Z"
})
<?php
$client->count('clicks', ["timeframe" => [
"start" => "2012-08-13T19:00:00.000Z",
"end" => "2013-09-20T19:00:00.000Z"
]]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
long countUnique = queryClient.countUnique("<event_collection>", "<target_property>", new AbsoluteTimeframe("2015-05-15T19:00:00.000Z","2015-06-07T19:00:00.000Z"));
var absoluteTimeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddMonths(-1), DateTime.Now));
var countUnique = keenClient.Query(QueryType.CountUnique(), "target_collection", "target_property", absoluteTimeframe);
Absolute timeframes are passed in with a URL-encoded JSON object containing “start” and “end” properties with ISO-8601 formatted date strings.
A query will be inclusive of events starting at the exact same time as the start time and exclusive of events starting with the exact same time as the end time. In other words, to run a query on an exact 24 hour window, you can use a timeframe that starts at midnight one day and ends at midnight the next day.
Relative Timeframes
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :timeframe => "this_7_days")
keen.count("purchases", timeframe="this_7_days")
<?php
$client->count("clicks", ["timeframe" => "this_7_days"]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// query
long count = queryClient.count("<event_collection>", new RelativeTimeframe("this_week"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var countUnique = keenClient.Query(QueryType.CountUnique(), "target_collection", "target_property", relativeTimeframe);
Relative timeframes are passed in with a patterned string sequence:
{rel}_{n}_{units}
Pattern | Description |
---|---|
{rel} | “this” or “previous” – Use “this” when you want to include events happening right up until now. Use “previous” when you only want to get results for complete chunks of time (e.g. the full hour, day, or week). |
{n} | Any whole number greater than 0 (zero). |
{units} | “minutes”, “hours”, “days”, “weeks”, “months”, or “years”. |
This chart illustrates the difference between “this” and “previous”:
Notice that this_2_days
will include all of the current day and all of the previous day, whereas previous_2_days
includes the previous two fully completed days and none of the current day.
Below are the supported relative timeframes for “this”:
Timeframe | Description |
---|---|
this_minute | Creates a timeframe starting from the beginning of the current minute until now. |
this_hour | Creates a timeframe starting from the beginning of the current hour until now. |
this_day | Creates a timeframe starting from the beginning of the current day until now. |
this_week | Creates a timeframe starting from the beginning of the current week until now. |
this_month | Creates a timeframe starting from the beginning of the current month until now. |
this_year | Creates a timeframe starting from the beginning of the current year until now. |
this_n_minutes | All of the current minute and the previous completed n-1 minutes. |
this_n_hours | All of the current hour and the previous completed n-1 hours. |
this_n_days | All of the current day and the previous completed n-1 days. |
this_n_weeks | All of the current week and the previous completed n-1 weeks. |
this_n_months | All the current month and previous completed n-1 months. |
this_n_years | All the current year and previous completed n-1 years. |
Below are the supported relative timeframes for “previous”:
Timeframe | Description |
---|---|
previous_n_minutes | Gives a start of n-minutes before the most recent complete minute and an end at the most recent complete minute. (For example: If right now it is 7:15:30pm and I specify “previous_3_minutes”, the timeframe would stretch from 7:12pm until 7:15pm.) |
previous_n_hours | Gives a start of n-hours before the most recent complete hour and an end at the most recent complete hour. (For example: If right now it is 7:15pm and I specify “previous_7_hours”, the timeframe would stretch from noon until 7:00pm.) |
previous_n_days | Gives a starting point of n-days before the most recent complete day and an end at the most recent complete day. (For example: If right now it is Friday at 9:00am and I specify a timeframe of “previous_3_days”, the timeframe would stretch from Tuesday morning at 12:00am until Thursday night at midnight.) |
previous_n_weeks | Gives a start of n-weeks before the most recent complete week and an end at the most recent complete week. (For example: If right now it is Monday, and I specify a timeframe of “previous_2_weeks”, the timeframe would stretch from three Sunday mornings ago at 12:00am until the most recent Sunday at 12:00am (yesterday morning).) |
previous_n_months | Gives a start of n-months before the most recent completed month and an end at the most recent completed month. (For example: If right now is the 5th of the month, and I specify a timeframe of “previous_2_months”, the timeframe would stretch from the start of two months ago until the end of last month.) |
previous_n_years | Gives a start of n-years before the most recent completed year and an end at the most recent completed year. (For example: If right now is the June 5th, and I specify a timeframe of “previous_2_years”, the timeframe would stretch from the start of two years ago until the end of last year.) |
Below are several short-hand convenience names:
Timeframe | Description |
---|---|
today | convenience for “this_day” |
previous_minute | convenience for “previous_1_minutes” |
previous_hour | convenience for “previous_1_hours” |
yesterday | convenience for “previous_1_days” |
previous_day | convenience for “previous_1_days” |
previous_week | convenience for “previous_1_weeks” |
previous_month | convenience for “previous_1_months” |
previous_year | convenience for “previous_1_years” |
Timezone
The timezone
parameter is available when running an analysis with a relative timeframes.
Timezones ensure you’re getting the data from your local definition of “today”, rather than UTC. This helps make dashboards and visualizations relevant to whomever is looking at them, regardless of where they are in the world.
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_7_days\",
\"timezone\": -28800
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
timeframe: 'this_7_days',
timezone: -28800
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
timeframe: 'this_7_days',
timezone: -28800,
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.count("purchases", :timeframe => "this_7_days", :timezone => -28800)
keen.count("purchases", timeframe="this_7_days", timezone=-28800)
<?php
$client->count("clicks", ["timeframe" => "this_7_days", "timezone" => -28800]);
?>
// Supported by this SDK.
// Add the Keen Query package to your build and then build it
KeenProject queryProject = new KeenProject("<project id>", "<write key>", "<read key>");
KeenQueryClient queryClient = new KeenQueryClient.Builder(queryProject).build();
// Timezone parameter is built into RelativeTimeframe
long count = queryClient.count("<event_collection>", new RelativeTimeframe("this_week", "US/Pacific"));
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var countUnique = keenClient.Query(QueryType.CountUnique(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
To specify a timezone, just include a timezone
parameter with a value equal to the number of seconds to offset the time.
For example, to adjust an analysis to US/Pacific time (UTC-08:00), set the timezone
parameter to -28800 (-8 hours * 60 minutes * 60 seconds).
Named Timezones
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_7_days\",
\"timezone\": \"US/Pacific\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('count', {
event_collection: 'logins',
timeframe: 'this_7_days',
timezone: 'US/Pacific'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'count',
eventCollection: 'logins',
timeframe: 'this_7_days',
timezone: 'US/Pacific',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen’s API supports all timezones part of the tz database.
It’s best to use these when you can because you no longer have to worry about Daylight Savings Time (DST).
To use a specific timezone, simply set the timezone
parameter to a string containing the named timezone. Here are some examples of named timezones:
Timezone | Description |
---|---|
US/Eastern | The Eastern timezone in the U.S., either standard or daylight. |
US/Central | The Central timezone in the U.S., either standard or daylight. |
US/Mountain | The Mountain timezone in the U.S., either standard or daylight. |
US/Pacific | The Pacific timezone in the U.S., either standard or daylight. |
US/Alaska | The Alaskan timezone in the U.S., either standard or daylight. |
US/Hawaii | The Hawaiian timezone in the U.S. Hawaii doesn’t observe DST, pretty easy for us! |
Europe/Amsterdam | The timezone for Amsterdam in Europe, either standard or daylight. |
Europe/London | The timezone for London in Europe, either standard or daylight. |
Europe/Paris | The timezone for Paris in Europe, either standard or daylight. |
Europe/Prague | The timezone for Prague in Europe, either standard or daylight. |
Europe/Stockholm | The timezone for Stockholm in Europe, either standard or daylight. |
Europe/Copenhagen | The timezone for Copenhagen in Europe, either standard or daylight. |
Africa/Casablanca | The timezone for Casablanca in Africa, either standard or daylight. |
Africa/Nairobi | The timezone for Nairobi in Africa. No daylight savings observance. |
Asia/Singapore | The timezone for Singapore in Asia, either standard or daylight. |
Australia/Sydney | The timezone for Sydney in Australia, either standard or daylight. |
Asia/Dubai | The timezone for Dubai in Asia, either standard or daylight. |
Asia/Istanbul | The timezone for Istanbul in Asia, either standard or daylight. |
Asia/Jakarta | The timezone for Jakarta in Asia, either standard or daylight. |
Asia/Tokyo | The timezone for Tokyo in Asia, either standard or daylight. |
America/Sao_Paulo | The timezone for Sao Paulo in South America, either standard or daylight. |
Australia/Perth | The timezone for Perth in Australia, either standard or daylight. |
Europe/Istanbul | The timezone for Istanbul in Europe, either standard or daylight. |
Pacific/Auckland | The timezone for Auckland in New Zealand, either standard or daylight. |
UTC | The UTC timezone. |
Execution Metadata
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/count?include_metadata=true \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": {
\"start\": \"2018-10-01T00:00:00.000Z\",
\"end\": \"2018-11-01T00:00:00.000Z\"
}
}"
Response
{
"result": 5,
"execution_metadata": {
"total_processing_time": 0.09057211875915527,
"events_scanned": 1000,
"properties_per_event": 4,
"total_properties_scanned": 4000
}
}
Keen’s pricing is mostly based on two factors: how much new data you collect, and how much you crunch. You can easily
calculate additional information concerning costs generated by a specific query, when you pass include_metadata=true
as an additional query parameter. The query result will be enriched with execution_metadata
object, composed of numbers
that indicate the count of scanned events, properties, and total time required to process your request.
Metadata properties
Property | Description |
---|---|
total_processing_time | Gives info on how long it takes to process the query in seconds. |
events_scanned | The number of events that exist within the timeframe you provided. |
properties_per_event | The number of properties per event required to calculate the query. |
total_properties_scanned | The total number of properties scanned during the query execution. |
Saved Queries
Saved queries allow you to easily access your favourite metrics. Rather than entering the same query parameters over and over again, queries can be easily saved, edited, and shared with your teammates.
Creating a Saved Query
Request
# PUT
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json' \
-X PUT \
-d '{
"refresh_rate": 0,
"query": {
"analysis_type": "sum",
"target_property": "price",
"event_collection": "purchases",
"filters": [
{
"property_name": "price",
"operator": "gte",
"property_value": 1.00
}
],
"group_by": [
"platform"
],
"order_by": [
{
"property_name": "result",
"direction": "DESC"
}
],
"timeframe": "this_2_weeks",
"limit": 20
}
}'
// Supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
const savedQueryName = 'my-saved-query';
client
.put(client.url('queries', 'saved', savedQueryName))
.auth(client.masterKey())
.send({
query: {
analysis_type: 'sum',
target_property: 'price',
event_collection: 'purchases',
timeframe: 'this_2_weeks',
filters: [
{
property_name: 'price',
operator: 'gte',
property_value: 1.00
}
],
group_by: [
'platform'
],
order_by: [
{
property_name: 'result',
direction: 'DESC'
}
],
limit: 20
},
metadata: {
display_name: 'Purchases (past 2 weeks)',
},
refresh_rate: 0
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
const savedQueryName = 'my-saved-query';
client
.put({
url: client.url('queries', 'saved', savedQueryName),
api_key: client.masterKey(),
params: {
query: {
analysisType: 'sum',
targetProperty: 'price',
eventCollection: 'purchases',
timeframe: 'this_2_weeks',
filters: [
{
propertyName: 'price',
operator: 'gte',
propertyValue: 1.00,
},
],
groupBy: [
'platform',
],
orderBy: [
{
propertyName: 'result',
direction: 'DESC',
},
],
limit: 20,
},
metadata: {
displayName: 'Purchases (past 2 weeks)',
},
refreshRate: 0,
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
saved_query_attributes = {
refresh_rate: 0,
query: {
analysis_type: "sum",
target_property: "price",
event_collection: "purchases",
timeframe: "this_2_weeks",
filters: [
{
property_name: "price",
operator: "gte",
property_value: 1.00
}
],
group_by: [
"platform"
],
order_by: [
{
property_name: "result",
direction: "DESC"
}
],
limit: 20
},
metadata: {
display_name: "Purchases (past 2 weeks)",
}
}
Keen.saved_queries.create("QUERY_NAME", saved_query_attributes)
saved_query_attributes = {
"refresh_rate": 0,
"query": {
"analysis_type": "sum",
"target_property": "price",
"event_collection": "purchases",
"timeframe": "this_2_weeks",
"filters": [
{
"property_name": "price",
"operator": "gte",
"property_value": 1.00
}
],
"group_by": [
"platform"
],
"order_by": [
{
property_name: "result",
direction: "DESC"
}
],
"limit": 20
},
"metadata": {
"display_name": "Purchases (past 2 weeks)",
}
}
keen.saved_queries.create("QUERY_NAME", saved_query_attributes)
<?php
$client = KeenIOClient::factory([
'projectID' => $project_id,
'masterKey' => $master_key
]);
$query = [
"analysis_type" => "sum",
"target_property" => "price",
"event_collection" => "purchases",
"filters" =>
[
[
"property_name" => "price",
"operator" => "gte",
"property_value" => 1.00
]
],
"group_by" =>
[
"platform"
],
"order_by" =>
[
[
"property_name" => "result",
"direction" => "DESC"
]
],
"limit" => 20,
"timeframe" => "this_2_weeks"
];
$client->createSavedQuery(['query_name' => 'QUERY_NAME', 'query' => $query]);
?>
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// query
SingleAnalysis count = new SingleAnalysis.Builder(QueryType.SUM)
.withEventCollection("purchases")
.withTargetPropertyName("price")
.withFilter("price", FilterOperator.GREATER_THAN_EQUAL, 1.00)
.withTimeframe(new RelativeTimeframe("this_2_weeks"))
.build();
// orderBy() and limit() are not supported yet
// Currently not supported by this SDK.
Response
{
"updated": false,
"refresh_rate": 0,
"created": true,
"user_last_modified_date": "2018-12-13T14:44:56.034766+00:00",
"last_modified_date": "2018-12-13T14:44:56.034766+00:00",
"query_name": "QUERY_NAME",
"urls": {
"cached_query_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME",
"cached_query_results_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result"
},
"created_date": "2018-12-13T14:44:56.034766+00:00",
"query": {
"filters": [
{
"operator": "gte",
"property_name": "price",
"property_value": 1
}
],
"analysis_type": "sum",
"timezone": null,
"order_by": [
{
"direction": "DESC",
"property_name": "result"
}
],
"group_by": [
"platform"
],
"timeframe": "this_2_weeks",
"target_property": "price",
"interval": null,
"limit": 20,
"event_collection": "purchases"
},
"metadata": null,
"run_information": null
}
# Note: run_information is null because the query is currently being scheduled and executed. Once the initial scheduling and execution has been completed, run_information will be populated.
HTTP Methods
Method | Authentication | Description |
---|---|---|
PUT | Master Key | Create a saved query |
Required Parameters
Parameter | Description |
---|---|
query | Parameters for the query you want to save |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
Getting Saved Query Results
Request
# GET
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json'
// Supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('saved', 'my-saved-query')
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Keen.saved_queries.get("QUERY_NAME", results: true)
keen.saved_queries.results("QUERY_NAME")
<?php
$client = KeenIOClient::factory([
'projectId' => $project_id,
'masterKey' => $master_key
]);
$results = $client->getSavedQueryResults(['query_name' => 'QUERY_NAME']);
?>
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// query
QueryResult sumResultSaved = savedQueryApi.getResult("QUERY_NAME");
// Currently not supported by this SDK.
Response
{
"refresh_rate": 0,
"user_last_modified_date": "2018-12-13T15:00:48.073000+00:00",
"last_modified_date": "2018-12-13T15:00:48.073000+00:00",
"query_name": "QUERY_NAME",
"result": [
{
"platform": "Mobile",
"result": 1283.0400000000004
},
{
"platform": "Web",
"result": 323.7299999999999
}
],
"urls": {
"cached_query_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME",
"cached_query_results_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result"
},
"created_date": "2018-12-13T15:00:48.073000+00:00",
"query": {
"filters": [
{
"operator": "gte",
"property_name": "price",
"property_value": 1.00
}
],
"analysis_type": "sum",
"timezone": null,
"order_by": [
{
"direction": "DESC",
"property_name": "result"
}
],
"group_by": [
"platform"
],
"timeframe": "this_2_weeks",
"target_property": "price",
"interval": null,
"limit": 20,
"event_collection": "purchases"
},
"metadata": null,
"run_information": null
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Get saved query results |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
Updating Saved Queries
Request
# PUT
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json' \
-X PUT
-d '{
"refresh_rate": 0,
"query": {
"analysis_type": "count",
"event_collection": "purchases",
"filters": [
{
"property_name": "price",
"operator": "gte",
"property_value": 1.00
}
],
"timeframe": "this_1_weeks"
}
}'
// Supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.put(client.url('queries', 'saved', 'QUERY_NAME'))
.auth(client.masterKey())
.send({
refresh_rate: 0,
query: {
analysis_type: 'count',
event_collection: 'purchases',
filters: [
{
property_name: 'price',
operator: 'gte',
property_value: 1.00
}
],
timeframe: 'this_1_weeks'
},
metadata: {
display_name: 'Purchases (past week)',
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.put({
url: client.url('queries', 'saved', 'QUERY_NAME'),
api_key: client.masterKey(),
params: {
refreshRate: 0,
query: {
analysisType: 'count',
eventCollection: 'purchases',
filters: [
{
propertyName: 'price',
operator: 'gte',
propertyValue: 1.00,
},
],
timeframe: 'this_1_weeks',
},
metadata: {
displayName: 'Purchases (past week)',
},
},
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
saved_query_attributes = {
refresh_rate: 0,
metadata: {
display_name: "New Display Name",
}
}
Keen.saved_queries.update("QUERY_NAME", saved_query_attributes)
"saved_query_attributes" = {
"refresh_rate": 0,
"metadata": {
"display_name": "New Display Name",
}
}
keen.saved_queries.update("QUERY_NAME", saved_query_attributes)
<?php
$client = KeenIOClient::factory([
'projectID' => $project_id,
'masterKey' => $master_key
]);
// please note that refresh_rate is currently not supported by this SDK
$query = [
"analysis_type" => "count",
"event_collection" => "purchases",
"filters" =>
[
[
"property_name" => "price",
"operator" => "gte",
"property_value" => 2.50
]
],
"timeframe" => "this_1_weeks"
];
$client->updateSavedQuery(['query_name' => 'QUERY_NAME', 'query' => $query]);
?>
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// query
Map<String, Object> updateResponse = null;
// Update a saved query to now be a cached query with the minimum refresh rate of 4 hrs...
// ...using partial update:
Map<String, Object> partialUpdates = new HashMap<String, Object>();
int refreshRate = 4 * 3600; // 4 hrs
partialUpdates.put("refresh_rate", refreshRate);
updateResponse = savedQueryApi.updateQuery("QUERY_NAME", partialUpdates);
// ...using full update, if we've already fetched the query definition and removed unacceptable
// properties. Some properties, like "run_information" returned by getting a query definition cannot
// be `PUT` back or an error is returned.:
Map<String, Object> fullUpdates = mySanitizeHelper(countQueryDef);
fullUpdates.put("refresh_rate", 14400);
updateResponse = savedQueryApi.updateQueryFull("QUERY_NAME", fullUpdates);
// ...or using the helpers:
updateResponse = savedQueryApi.setRefreshRate("QUERY_NAME", RefreshRate.fromHours(4));
// Currently not supported by this SDK.
Response
{
"updated": true,
"refresh_rate": 14400,
"created": false,
"user_last_modified_date": "2018-12-13T15:17:08.479000+00:00",
"last_modified_date": "2018-12-13T15:17:08.479000+00:00",
"query_name": "QUERY_NAME",
"urls": {
"cached_query_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME",
"cached_query_results_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result"
},
"created_date": "2018-12-13T15:09:33.724000+00:00",
"query": {
"filters": [
{
"operator": "gte",
"property_name": "price",
"property_value": 1
}
],
"analysis_type": "count",
"timezone": null,
"order_by": null,
"group_by": null,
"timeframe": "this_1_weeks",
"interval": null,
"limit": null,
"event_collection": "purchases"
},
"metadata": null,
"run_information": null
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
PUT | Master Key | Update a saved query |
Optional Parameters
Parameter | Description |
---|---|
query | Parameters for the query you want to save |
Getting All Saved Query Definitions
Request
# GET
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json'
// Currently not supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.get(client.url('queries', 'saved'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.get({
url: client.url('queries', 'saved'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Keen.saved_queries.all
keen.saved_queries.all()
<?php
$client = KeenIOClient::factory([
'projectID' => $project_id,
'masterKey' => $master_key
]);
$results = $client->getSavedQueries();
?>
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// query
List<Map<String, Object>> allQueryDefs = savedQueryApi.getAllDefinitions();
// Currently not supported by this SDK.
Response
[
{
"refresh_rate": 14400,
"user_last_modified_date": "2018-12-13T15:09:11.293000+00:00",
"last_modified_date": "2018-12-13T15:10:10.461000+00:00",
"query_name": "QUERY_NAME",
"urls": {
"cached_query_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME",
"cached_query_results_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result"
},
"created_date": "2018-12-13T15:00:48.073000+00:00",
"query": {
"filters": [
{
"operator": "gte",
"property_name": "price",
"property_value": 1
}
],
"analysis_type": "count",
"timezone": null,
"order_by": null,
"group_by": null,
"timeframe": "this_1_weeks",
"interval": null,
"limit": null,
"event_collection": "purchases"
},
"run_information": {
"last_run_date": "2018-12-13T15:10:05.005667",
"last_run_message": "success",
"last_run_status": 200,
"next_run_date": "2018-12-13T19:10:05.005667"
},
"metadata": null
}
]
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Master Key | Get a saved query definition |
Getting a Saved Query Definition
Request
# GET
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json'
# or alternatively:
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME?api_key=access_key_with_query_definition_permitted \
-H 'Content-Type: application/json'
// Currently not supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.get(client.url('queries', 'saved', 'my-saved-query'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.get({
url: client.url('queries', 'saved', 'my-saved-query'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Keen.saved_queries.get("QUERY_NAME")
keen.saved_queries.get("QUERY_NAME")
<?php
$client = KeenIOClient::factory([
'projectId' => $project_id,
'masterKey' => $master_key
]);
$results = $client->getSavedQuery(['query_name' => 'QUERY_NAME']);
?>
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// query
Map<String, Object> countQueryDef = savedQueryApi.getDefinition("QUERY_NAME");
// Currently not supported by this SDK.
Response
{
"refresh_rate": 0,
"user_last_modified_date": "2018-12-13T15:00:48.073000+00:00",
"last_modified_date": "2018-12-13T15:00:48.073000+00:00",
"query_name": "QUERY_NAME",
"urls": {
"cached_query_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME",
"cached_query_results_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result"
},
"created_date": "2018-12-13T15:00:48.073000+00:00",
"query": {
"filters": [
{
"operator": "gte",
"property_name": "price",
"property_value": 1.00
}
],
"analysis_type": "sum",
"timezone": null,
"order_by": [
{
"direction": "DESC",
"property_name": "result"
}
],
"group_by": [
"platform"
],
"timeframe": "this_2_weeks",
"target_property": "price",
"interval": null,
"limit": 20,
"event_collection": "purchases"
},
"run_information": null,
"metadata": null
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Master Key | Get a saved query definition |
Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header. Must contain query_definition in the permitted property array. |
Delete a Saved Query
Request
# DELETE
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME \
-H "Authorization: MASTER_KEY" \
-X DELETE
// Currently not supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.del(client.url('queries', 'saved', 'my-saved-query'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.del({
url: client.url('queries', 'saved', 'my-saved-query'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Keen.saved_queries.delete("QUERY_NAME")
keen.saved_queries.delete("QUERY_NAME")
<?php
$client = KeenIOClient::factory([
'projectId' => $project_id,
'masterKey' => $master_key
]);
$client->deleteSavedQuery(['query_name' => 'QUERY_NAME']);
?>
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// delete
savedQueryApi.deleteQuery("QUERY_NAME");
// Currently not supported by this SDK.
Response
// Successful result
204 - No Content
HTTP Methods
Method | Authentication | Description |
---|---|---|
DELETE | Master Key | Delete a saved query |
Cached Queries
Cached Queries are a way for you to build applications with charts and tables that load instantly, even as your data volume grows. Cached Queries reduce your average query response time down to milliseconds, which is key for building customer-facing reports where the demand for responsiveness is highest.
A Cached Query is simply a Keen Query that you specify to be recomputed on a regular interval. Once you identify the queries you want to build on, add caching and we will take care of running them in the background. Then when you want the answers, we will have them ready to go instead of calculating them from scratch.
As an example: you can cache a query like “Median response time over the last 90 days, updated hourly” and we’ll continuously refresh the cached result for you based on the hourly “refresh_rate”. Everytime a user loads your report, it will only take milliseconds to retrieve the median over three months of data.
Try caching a Multi-Analysis Query to get several results in a single rapid response! If you want to cache results, but retrieve them based on an index like Customer ID or allow a user to request a custom set of intervals then check out Cached Datasets
Query Caching can be enabled on any Saved Query
Creating a Cached Query
Request
# PUT
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json' \
-X PUT
-d '{
"refresh_rate": 43200,
"query": {
"analysis_type": "count",
"event_collection": "purchases",
"filters": [
{
"property_name": "price",
"operator": "gte",
"property_value": 1.00
}
],
"timeframe": "this_1_weeks"
}
}'
// Supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.put(client.url('queries', 'saved', 'my-saved-query'))
.auth(client.masterKey())
.send({
refresh_rate: 43200
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.put({
url: client.url('queries', 'saved', 'my-saved-query'),
api_key: client.masterKey(),
params: {
refreshRate: 43200,
},
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# update refresh_rate in saved_query_attributes
saved_query_attributes = {
refresh_rate: 43200,
query: {
analysis_type: "count",
event_collection: "purchases",
timeframe: "this_2_weeks",
filters: [
{
property_name: "price",
operator: "gte",
property_value: 1.00
}
]
},
metadata: {
display_name: "Purchases (past 2 weeks)",
}
}
Keen.saved_queries.create("saved-query-name", saved_query_attributes)
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// To work with Saved/Cached Queries, create a KeenQueryClient as normal, then use it to create a SavedQueries implementation
KeenQueryClient queryClient = ...;
SavedQueries savedQueryApi = queryClient.getSavedQueriesInterface();
// First, we'll create a query. Let's count the number of purchases with a price >= $1 in the last
// two weeks including the current week.
SingleAnalysis count = new SingleAnalysis.Builder(QueryType.COUNT)
.withEventCollection("purchases")
.withFilter("price", FilterOperator.GREATER_THAN_EQUAL, 1.00)
.withTimeframe(new RelativeTimeframe("this_2_weeks"))
.build();
// There are variations of create*Query() to set things like the refresh rate.
Map<String, Object> rawCreateResponse = savedQueryApi.createSavedQuery("saved-query-name", count);
// Currently not supported by this SDK.
Response
{
"updated": true,
"refresh_rate": 43200,
"created": false,
"user_last_modified_date": "2018-12-13T15:39:32.899000+00:00",
"last_modified_date": "2018-12-13T15:39:32.899000+00:00",
"query_name": "QUERY_NAME",
"urls": {
"cached_query_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME",
"cached_query_results_url": "/3.0/projects/PROJECT_ID/queries/saved/QUERY_NAME/result"
},
"created_date": "2018-12-13T15:09:33.724000+00:00",
"query": {
"filters": [
{
"operator": "gte",
"property_name": "price",
"property_value": 1
}
],
"analysis_type": "count",
"timezone": null,
"order_by": null,
"group_by": null,
"timeframe": "this_1_weeks",
"interval": null,
"limit": null,
"event_collection": "purchases"
},
"metadata": null,
"run_information": null
}
By turning on caching, you can tell Keen to automatically refresh your saved queries so you can get immediate results. We will calculate the results of your query on a set interval, and have them available for you immediately when you ask for the Saved Query result.
To turn on caching, simply update the saved query, and include a refresh_rate
property.
The refresh rate value is in seconds, and must be between 4 hours (14,400 seconds) and 48 hours (172,800 seconds). If you own any specific project that requires Cached Queries to be updated more frequently, please reach out to us anytime!
Optional Parameters
Parameter | Description |
---|---|
zero_fill | It only applies to cached queries, which have the group_by clause and the interval . When zero_fill=true, then all intervals have exactly the same set of group_by keys. Those missing in a particular interval are filled with zeros. Notice that it may greatly explode the result size. Consider setting zero_fill=false especially when your result is huge. (default: true) |
Cached Datasets
Note: Cached Datasets are currently in Early Release.
Cached Datasets are a powerful way for you to build applications with charts and tables that load instantly, even as your Streams volume grows. Conceptually similar to Cached Queries, a Cached Dataset additionally allows you to retrieve results indexed by properties like customer, cohort, article, campaign and more. You can also create a Cached Dataset over a large timeframe like “365 days, daily” and then retrieve results for any arbitrary timeframes contained in that Cached Dataset.
Some example cases where you’d want to use Cached Datasets:
- You want your dashboard to retrieve the appropriate results for each user who loads the report
- You want to empower the user to change the timeframe from the last 14 days to 3 hours on Tuesday seamlessly
- You want to send an email to each user who viewed a product 3 times in the last week
- You want to build an automated job that generates account bills for each account’s monthly usage
Creating a Cached Dataset
You can create a Cached Dataset by providing the query you want to cache, the property name you want to index by, and a display name for reference. The timeframe of the query will determine how much data will be kept cached and the interval determines the granularity of results. Results will be cached for each index value in each interval in the timeframe.
Currently Cached Dataset query definitions require Relative Timeframes and the unit must match the interval, eg. this_90_days
and daily
or this_12_months
and monthly
.
Try caching a Multi-Analysis Query to get several results in a single rapid response!
Funnels are not currently supported by Cached Datasets, but funnels do work with Cached Queries.
Request
# PUT
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/datasets/DATASET_NAME \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json' \
-X PUT \
-d '{
"display_name": "Count Daily Product Purchases Over $100 by Country",
"query": {
"analysis_type": "count",
"event_collection" : "purchases",
"filters": [
{
"property_name": "price",
"operator": "gte",
"property_value": 100
}
],
"timeframe": "this_500_days",
"interval": "daily",
"group_by": "ip_geo_info.country"
},
"index_by": "product.id"
}'
// Currently not supported by this SDK
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.put(client.url('datasets', 'my-first-dataset'))
.auth(client.masterKey())
.send({
display_name: 'Count Daily Product Purchases Over $100 by Country',
query: {
analysis_type: 'count',
event_collection: 'purchases',
filters: [
{
property_name: 'price',
operator: 'gte',
property_value: 100
}
],
group_by: 'ip_geo_info.country',
interval: 'daily',
timeframe: 'this_500_days'
},
index_by: 'product.id'
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.put({
url: client.url('datasets', 'my-first-dataset'),
api_key: client.masterKey(),
params: {
displayName: 'Count Daily Product Purchases Over $100 by Country',
query: {
analysisType: 'count',
eventCollection: 'purchases',
filters: [
{
propertyName: 'price',
operator: 'gte',
propertyValue: 100,
}
],
groupBy: 'ip_geo_info.country',
interval: 'daily',
timeframe: 'this_500_days',
},
indexBy: 'product.id',
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
# Currently not supported by this SDK.
KeenProject keenProject = new KeenProject(KEEN_PROJECT_ID, KEEN_WRITE_KEY, KEEN_READ_KEY, KEEN_MASTER_KEY);
CachedDatasets cachedDatasetsClient = new KeenQueryClient
.Builder(keenProject)
.build()
.getCachedDatasetsClient();
DatasetQuery query = DatasetQueryBuilder
.aDatasetQuery()
.withAnalysisType("count")
.withEventCollection("purchases")
.withFilters(singletonList(new Filter("price", FilterOperator.GREATER_THAN, "100")))
.withGroupBy(singletonList("ip_geo_info.country"))
.withTimeframe("this_500_days")
.withInterval("daily")
.build();
DatasetDefinition datasetDefinition = cachedDatasetsClient.create(
"count-purchases-gte-100-by-country-daily",
"Count Daily Product Purchases Over $100 by Country",
query,
singletonList("product.id")
);
// Currently not supported by this SDK.
Response
{
"project_id":"PROJECT ID",
"organization_id":"ORGANIZATION",
"dataset_name":"DATASET NAME",
"display_name":"Count Daily Product Purchases Over $100 by Country",
"query": {
"project_id":"PROJECT ID",
"analysis_type":"count",
"event_collection":"purchases",
"filters": [
{
"property_name":"price",
"operator":"gte",
"property_value":100
}
],
"timeframe":"this_500_days",
"timezone":null,
"interval":"daily",
"group_by":["ip_geo_info.country"]
},
"index_by": "product.id",
"last_scheduled_date":"1970-01-01T00:00:00.000Z",
"latest_subtimeframe_available":"1970-01-01T00:00:00.000Z",
"milliseconds_behind": 3600000,
"status": "Created"
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
PUT | Master Key | Create a Cached Dataset. Updates are not currently supported. |
Required Parameters
Parameter | Description |
---|---|
query | The query definition you want Keen to optimize for your application. Currently supports Relative Timeframes. Interval should match; eg. this_90_day and daily . Cannot contain group_by on same property as index_by
|
index_by | The event property name containing an identifier, such as user_id or store.id , that will be used to index and retrieve query results. This value cannot be the same as a group_by property set in the query definition. There might be up to three index_by properties (i.e. "index_by": ["product_id", "country.name"] ). Index_by supported types: String , Number , Boolean , or json object . |
display_name | The human-readable string name for your Cached Dataset |
Getting a Dataset Definition
Request
# GET
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/datasets/DATASET_NAME \
-H "Authorization: READ_KEY"
// Supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.get(client.url('datasets', 'my-first-dataset'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.get({
url: client.url('datasets', 'my-first-dataset'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
# Currently not supported by this SDK.
KeenProject keenProject = new KeenProject(KEEN_PROJECT_ID, KEEN_WRITE_KEY, KEEN_READ_KEY, KEEN_MASTER_KEY);
CachedDatasets cachedDatasetsClient = new KeenQueryClient
.Builder(keenProject)
.build()
.getCachedDatasetsClient();
DatasetDefinition datasetDefinition = cachedDatasetsClient
.getDefinition("count-purchases-gte-100-by-country-daily");
// Currently not supported by this SDK.
Response
{
"project_id":"5011efa95f546f2ce2000000",
"organization_id":"4f3846eaa8438d17fb000001",
"dataset_name":"count-purchases-gte-100-by-country-daily",
"display_name":"Count Daily Product Purchases Over $100 by Country",
"query": {
"project_id":"5011efa95f546f2ce2000000",
"analysis_type":"count",
"event_collection":"purchases",
"filters": [
{
"property_name":"price",
"operator":"gte",
"property_value":100
}
],
"timeframe":"this_500_days",
"timezone":null,
"interval":"daily",
"group_by":["ip_geo_info.country"]
},
"index_by":["product.id"],
"last_scheduled_date":"2016-11-04T18:52:36.323Z",
"latest_subtimeframe_available":"2016-11-05T00:00:00.000Z",
"milliseconds_behind": 3600000,
"status": "OK"
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Get a Cached Dataset definition |
Status field in response
Value | Description | Result retrieval |
---|---|---|
Created | The Cached Dataset is created with status “Created” | Not allowed |
Bootstrapping | Usually within a minute from creation a Cached Dataset is picked up for processing and the “status” changes to “Bootstrapping” | Not allowed |
OK | If bootstrapping finishes successfully. It’s now possible to retrieve results form the Cached Dataset | Allowed |
BootstrappingFailed | If bootstrapping fails | Not allowed |
Warn | Bootstrapping finished successfully, but encountered some failures during a subsequent Cached Dataset update | Allowed, however, results may have missing or inaccurate data. See “errorMessage” to identify the root cause. |
Retrieving results from a Cached Dataset
Once your dataset has finished bootstrapping (“status” has changed from Bootstrapping
to OK
), you can retrieve results for specific index_by
values and timeframes.
You can retrieve results using any Relative Timeframe that is within the Cached Dataset and matches the timeframe unit in the definition. For example, if the Cached Dataset is defined for “this_365_days” you can retrieve “this_3_days” or “previous_30_days”.
You can also can also use Absolute Timeframes, but the timeframe must resolve to intervals matched by the dataset. For example, requesting "timeframe": {"start": "2016-11-02T **01**:00:00.000Z", "end": "2016-11-02T **04**:00:00.000Z"}
( a three hour window) from a daily dataset won’t resolve but "timeframe": { "start": "2016-11- **02**T00:00:00.000Z", "end": "2016-11- **05**T00:00:00.000Z" }
(a three day window) would.
Based on the raw data an index_by
value might not have nonzero results for some intervals.
Request
# GET
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/datasets/DATASET_NAME/results?api_key=READ_KEY&index_by=INDEX_VALUE&timeframe=TIMEFRAME
// Supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('dataset', {
name: 'my-first-dataset',
index_by: 'INDEX_VALUE',
timeframe: 'this_7_days'
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
datasetName: 'my-first-dataset',
indexBy: 'INDEX_VALUE',
timeframe: 'this_7_days',
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK
# Currently not supported by this SDK
# Currently not supported by this SDK
KeenProject keenProject = new KeenProject(KEEN_PROJECT_ID, KEEN_WRITE_KEY, KEEN_READ_KEY, KEEN_MASTER_KEY);
CachedDatasets cachedDatasetsClient = new KeenQueryClient
.Builder(keenProject)
.build()
.getCachedDatasetsClient();
DatasetDefinition datasetDefinition = cachedDatasetsClient
.getDefinition("count-purchases-gte-100-by-country-daily");
List<IntervalResultValue> results = cachedDatasetsClient.getResults(
datasetDefinition,
singletonMap("product.id", "some-product-id"),
new RelativeTimeframe("this_7_days")
);
// Currently not supported by this SDK.
Response
// example: just like a normal count query result with a group_by and daily interval
{
"result": [
{
"timeframe": {
"start": "2016-11-02T00:00:00.000Z",
"end": "2016-11-03T00:00:00.000Z"
},
"value": [
{
"item.name": "Golden Widget",
"result":0
},
{
"item.name": "Silver Widget",
"result":18
},
{
"item.name": "Bronze Widget",
"result":1
},
{
"item.name": "Platinum Widget",
"result":9
}
]
},
{
"timeframe": {
"start":"2016-11-03T00:00:00.000Z",
"end":"2016-11-04T00:00:00.000Z"
},
"value": [
{
"item.name": "Golden Widget",
"result":1
},
{
"item.name": "Silver Widget",
"result":13
},
{
"item.name": "Bronze Widget",
"result":0
},
{
"item.name": "Platinum Widget",
"result":3
}
]
}
]
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Get query results from a Cached Dataset |
Required Parameters
Parameter | Description |
---|---|
index_by | The index_by value for which results will be retrieved. Simplified syntax is available for a single index_by property of type String : index_by=INDEX_BY_VALUE . If there are more than one properties or the index_by property is not a String : index_by={"property1":"string_value","property2":5.0,"property3":true}
|
timeframe | Limits retrieval of results to a specific portion of the Cached Dataset |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header |
zero_fill | It only applies to cached datasets, which have the group_by clause and the timeframe spans more than one interval. When zero_fill=true, then all intervals have exactly the same set of group_by keys. Those missing in a particular interval are filled with zeros. Notice that it may greatly explode the result size. Consider setting zero_fill=false especially when your result is huge. (default: true) |
Listing Cached Dataset Definitions for Project
Request
# GET
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/datasets \
-H "Authorization: READ_KEY"
// Currently not supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.get(client.url('datasets'))
.auth(client.readKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.get({
url: client.url('datasets'),
api_key: client.readKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
# Currently not supported by this SDK.
KeenProject keenProject = new KeenProject(KEEN_PROJECT_ID, KEEN_WRITE_KEY, KEEN_READ_KEY, KEEN_MASTER_KEY);
CachedDatasets cachedDatasetsClient = new KeenQueryClient
.Builder(keenProject)
.build()
.getCachedDatasetsClient();
List<DatasetDefinition> definitions = cachedDatasetsClient.getDefinitions(10, "DATASET_NAME_0");
// Currently not supported by this SDK.
Response
{
"datasets": [{
"project_id": "PROJECT_ID",
"organization_id": "ORGANIZATION_ID",
"dataset_name": "DATASET_NAME_1",
"display_name": "a first dataset wee",
"query": {
"project_id": "PROJECT_ID",
"analysis_type": "count",
"event_collection": "best collection",
"filters": [{
"property_name": "request.foo",
"operator": "lt",
"property_value": 300
}],
"timeframe": "this_500_hours",
"timezone": "US/Pacific",
"interval": "hourly",
"group_by": [
"exception.name"
]
},
"index_by": [
"project.id"
],
"last_scheduled_date": "2016-11-04T18:03:38.430Z",
"latest_subtimeframe_available": "2016-11-04T19:00:00.000Z",
"milliseconds_behind": 3600000,
"status": "OK"
}, {
"project_id": "PROJECT_ID",
"organization_id": "ORGANIZATION_ID",
"dataset_name": "DATASET_NAME_10",
"display_name": "tenth dataset wee",
"query": {
"project_id": "PROJECT_ID",
"analysis_type": "count",
"event_collection": "tenth best collection",
"filters": [],
"timeframe": "this_500_days",
"timezone": "UTC",
"interval": "daily",
"group_by": [
"analysis_type"
]
},
"index_by": [
"project.organization.id"
],
"last_scheduled_date": "2016-11-04T19:28:36.639Z",
"latest_subtimeframe_available": "2016-11-05T00:00:00.000Z",
"milliseconds_behind": 3600000,
"status": "OK"
}],
"next_page_url": "https://api.keen.io/3.0/projects/PROJECT_ID/datasets?limit=LIMIT&after_name=DATASET_NAME_10"
}
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Get list of Cached Dataset definitions for a project |
Optional Parameters
Parameter | Description |
---|---|
limit | How many Cached Dataset definitions to return at a time (1-100). Defaults to 10. |
after_name | A cursor for use in pagination. after_name is the Cached Dataset name that defines your place in the list. For instance, if you make a list request and receive 100 Cached Dataset definitions, ending with dataset_foo you can use dataset_foo as your after_name to retrieve the next page of definitions. Lists also return with helper “next_page_url” that uses after_name, so your subsequent call can fetch the next page of the list easily. |
Deleting a Cached Dataset
Request
# DELETE
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/datasets/DATASET_NAME \
-H "Authorization: MASTER_KEY" \
-X DELETE
// Currently not supported by this SDK.
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.del(client.url('datasets', 'my-first-dataset'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.del({
url: client.url('datasets', 'my-first-dataset'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK
# Currently not supported by this SDK
# Currently not supported by this SDK
KeenProject keenProject = new KeenProject(KEEN_PROJECT_ID, KEEN_WRITE_KEY, KEEN_READ_KEY, KEEN_MASTER_KEY);
CachedDatasets cachedDatasetsClient = new KeenQueryClient
.Builder(keenProject)
.build()
.getCachedDatasetsClient();
boolean isDeleted = cachedDatasetsClient.delete("count-purchases-gte-100-by-country-daily");
// Currently not supported by this SDK.
Response
// Successful result
204 - No Content
HTTP Methods
Method | Authentication | Description |
---|---|---|
DELETE | Master Key | Delete a Cached Dataset |
Query Availability
Request
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries?api_key=MASTER_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.get(client.url('queries'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.get({
url: client.url('queries'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Response
{
"count_unique_url": "/3.0/projects/PROJECT_ID/queries/count_unique",
"count_url": "/3.0/projects/PROJECT_ID/queries/count",
"extraction_url": "/3.0/projects/PROJECT_ID/queries/extraction",
"funnel_url": "/3.0/projects/PROJECT_ID/queries/funnel",
"select_unique_url": "/3.0/projects/PROJECT_ID/queries/select_unique"
}
Return a list of available query resources with a GET request.
HTTP Methods
Method | Authentication | Response |
---|---|---|
GET | Master Key | Returns all available query resources |
HEAD | Master Key | Returns the response header |
Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
Extractions
Request
# GET
$ curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/extraction?api_key=READ_KEY&event_collection=COLLECTION_NAME&timeframe=this_7_days"
# POST
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/extraction \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_7_days\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('extraction', {
event_collection: 'purchases',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType: 'extraction',
eventCollection: 'purchases',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Keen.extraction("purchases", :timeframe => "this_7_days")
# => [{ "price" => 20, ... }, { ... }]
keen.extraction("purchases", timeframe="this_7_days")
# => [{ "price" => 20, ... }, { ... }]
<?php
$client->extraction("purchases", ["timeframe" => "this_7_days"]);
// => [{ "price" => 20, ... }, { ... }]
?>
// Currently not supported by this SDK.
// Currently not supported by this SDK.
var relativeTimeframe = QueryRelativeTimeframe.ThisWeek();
var timezone = "US/Pacific"; // If not specified, timezone defaults to "UTC"
var extraction = keenClient.Query(QueryType.Extraction(), "target_collection", "target_property", relativeTimeframe, timezone: timezone);
Response
{
"result": [
{
"keen": {
"created_at": "2012-07-30T21:21:46.566000+00:00",
"timestamp": "2012-07-30T21:21:46.566000+00:00",
"id": ""
},
"user": {
"email": "dan@keen.io",
"id": "4f4db6c7777d66ffff000000"
},
"user_agent": {
"browser": "chrome",
"browser_version": "20.0.1132.57",
"platform": "macos"
}
},
{
"keen": {
"created_at": "2012-07-30T21:40:05.386000+00:00",
"timestamp": "2012-07-30T21:40:05.386000+00:00",
"id": ""
},
"user": {
"email": "michelle@keen.io",
"id": "4fa2cccccf546ffff000006"
},
"user_agent": {
"browser": "chrome",
"browser_version": "20.0.1132.57",
"platform": "macos"
}
}
]
}
Creates an extraction request for full-form event data with all property values.
We strongly believe you should always have full access to all of your data, and we aim to make that as simple and painless as possible.
HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Read Key | Creates an extraction request for full-form event data with all property values. JSON objects passed as query string parameters need to be URL encoded. |
HEAD | Read Key | Returns the response header |
POST | Read Key | Creates an extraction request for full-form event data with all property values. Each parameter and value should be placed in a JSON object within the POST body. |
Required Parameters
Parameter | Description |
---|---|
event_collection | Specifies the name of the event collection to analyze. |
timeframe | Refines the scope of events to be included in the analysis based on when the event occurred. |
Optional Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
filters | Refines the scope of events to be included in the analysis based on event property values. |
timezone | Assigns a timezone offset to relative timeframes. |
If an email address is specified, an email will be sent to it when your extraction is ready for download. If email is not specified, your extraction will be processed synchronously and your data will be returned as JSON. | |
latest | An integer containing the number of most recent events to extract. |
property_names | A URL-encoded array of strings containing properties you wish to extract. If this parameter is omitted, all properties will be returned. |
content_type | (extractions to a file only) Specifies the extraction data format. Allowed values: “text/csv” (default), “application/json”, “application/jsonstream” |
content_encoding | (extractions to a file only) Specifies if the extraction data should be compressed. Allowed values: “gzip” |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Limits
Extractions are rate limited at 200/minute, but are considered separately from query rate limiting.
There are 3 limits for synchronous extractions:
- The maximum number of events that can be returned in a response is 100,000.
- The maximum size of a response is 100MB.
Requests exceeding any of these limits will error.
If you need to extract more than that:
- Break the request into several smaller requests by timeframe. For example, if a month’s worth of data is over 10M events, you could run one extraction for each week.
- Consider an asynchronous extraction to a file, which has a higher extraction limit of 10,000,000 events.
Extract to a file
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/queries/extraction \
-H "Authorization: READ_KEY" \
-H 'Content-Type: application/json' \
-d "{
\"event_collection\": \"COLLECTION_NAME\",
\"timeframe\": \"this_7_days\",
\"email\": \"EMAIL_ADDRESS\"
}"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY'
});
client
.query('extraction', {
event_collection: 'purchases',
email: 'team@keen.io',
timeframe: 'this_7_days'
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
readKey: 'READ_KEY',
});
client
.query({
analysisType:'extraction',
eventCollection: 'purchases',
email: 'team@keen.io',
timeframe: 'this_7_days',
})
.then(res => {
// Handle results
})
.catch(err => {
// Handle errors
});
Response
{
"result": "Processing. Check the specified email for the extraction results."
}
If the email
parameter is included with a request, the extraction will be processed asynchronously and an email will be sent to the specified address when complete.
The email will include a link to a downloadable file. That file will be available at that location for 30 days.
Otherwise, the extraction will be processed in-line and JSON results will be returned in the request response.
Data format
By default the result file is CSV, however, you can modify the response type by means of two request body parameters: content_type and content_encoding:
content_type | content_encoding | file type | Description |
---|---|---|---|
csv | Default CSV format. | ||
application/json | json | Each line contains one json object with data. | |
application/jsonstream | gzip | json.gz | Format described above, gzipped |
text/csv | gzip | csv.gz | CSV gzipped |
Limits
The maximum number of events that can be extracted per file is 10,000,000. Requests exceeding this limit will error. The maximum extraction file size is 2GB. Requests exceeding this limit will error. If you are breaking this limit consider reducing the number of events per extraction or use compression.
Access
Projects
Overview
If you’re a B2B user, certain data models may require you to provide separate projects for each of your customers. As an alternative to creating each project by hand in the Keen web UI, you can utilize this feature to programmatically create Keen projects as your new users sign up.
Get Project
Request
$ curl https://api.keen.io/3.0/organizations/ORG_ID/projects/PROJECT_ID \
-H "Authorization: ORGANIZATION_KEY" \
-H "Content-Type: application/json"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
const projectId = 'PROJECT_ID';
client
.get(client.url('version', 'organizations', orgId, 'projects', projectId))
.auth(orgKey)
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
const projectId = 'PROJECT_ID';
client
.get({
url: client.url('version', 'organizations', orgId, 'projects', projectId),
api_key: orgKey,
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
# Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
// Successsful result
200 - OK
{
"id": "5efe28d679797411542c7fbe",
"name": "My First Project",
"api_keys": {
"master": "<MASTER_API_KEY>",
"read": "<READ_API_KEY>",
"write": "<WRITE_API_KEY>"
},
"users": [
{
"email": "<USER_EMAIL>"
},
{
...
},
...
],
"preferences": {
"s3_bucket_name": "<S3_BUCKET_NAME>"
}
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Organization Key | Returns information about a project. |
Get Projects
Request
$ curl https://api.keen.io/3.0/organizations/ORG_ID/projects \
-H "Authorization: ORGANIZATION_KEY" \
-H "Content-Type: application/json"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
client
.get(client.url('version', 'organizations', orgId, 'projects'))
.auth(orgKey)
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
client
.get({
url: client.url('version', 'organizations', orgId, 'projects'),
api_key: orgKey,
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
// Successful result
200 - OK
[
{
"id": "5efe28d679797411542c7fbe",
"name": "First Project",
"api_keys": {
"master": "<MASTER_API_KEY>",
"read": "<READ_API_KEY>",
"write": "<WRITE_API_KEY>"
},
"users": [
{
"email": "<USER_EMAIL>"
},
{
"email": "<USER_EMAIL>"
},
...
],
"preferences": {
"s3_bucket_name": "<S3_BUCKET_NAME>"
}
},
{
"name": "Second Project",
...
},
...
]
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Organization Key | Returns information about all projects for the given organization. |
Create Project
Request
$ curl https://api.keen.io/3.0/organizations/ORG_ID/projects
-H "Authorization: ORGANIZATION_KEY" \
-H "Content-Type: application/json"
-d '{
"name": "My First Project",
"users": [
{
"email": "<USER_EMAIL>"
},
{
...
},
...
],
"preferences": {
"s3_bucket_name": "<S3_BUCKET_NAME>"
}
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
client
.post(client.url('version', 'organizations', orgId, 'projects'))
.auth(orgKey)
.send({
name: 'My First Project',
users: [
{ email: '<USER_EMAIL>' },
{ email: '<USER_EMAIL>' },
{ email: '<USER_EMAIL>' }
// ...
],
preferences: {
's3_bucket_name': '<S3_BUCKET_NAME>'
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
client
.post({
url: client.url('version', 'organizations', orgId, 'projects'),
api_key: orgKey,
params: {
name: 'My First Project',
users: [
{ email: '<USER_EMAIL>' },
{ email: '<USER_EMAIL>' },
{ email: '<USER_EMAIL>' }
// ...
],
preferences: {
's3_bucket_name': '<S3_BUCKET_NAME>',
}
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
<?php
$client = KeenIOClient::factory([
'organizationId' => $org_id,
'organizationKey' => $org_key
]);
$client->createProject([
'name' => 'My First Project',
'users' => [
[ 'email' => '<USER_EMAIL>' ]
]
]);
?>
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
// Successful result
200 - OK
{
"id": "5efe28d679797411542c7fbe",
"name": "My First Project",
"api_keys": {
"master": "<MASTER_API_KEY>",
"read": "<READ_API_KEY>",
"write": "<WRITE_API_KEY>"
},
"users": [
{
"email": "<USER_EMAIL>"
},
{
...
},
...
],
"preferences": {
"s3_bucket_name": "<S3_BUCKET_NAME>"
}
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Organization Key | Create a new project in the organization. |
Required Parameters
Parameter | Description |
---|---|
name | Specifies the name of the project to be created. |
users | Specifies users for the project. If any users supplied do not exist, the request will fail. You will receive an error indicating which users are invalid. |
Optional Parameters
Parameter | Description |
---|---|
preferences | A JSON object, currently only used for a s3 bucket name. |
Update Project
Request
$ curl https://api.keen.io/3.0/organizations/ORG_ID/projects/PROJECT_ID
-H "Authorization: ORGANIZATION_KEY" \
-H "Content-Type: application/json"
-d '{
"name": "My Updated Project",
"users": [
{
"email": "<USER_EMAIL>"
},
{
...
},
...
],
"preferences": {
"s3_bucket_name": "<S3_BUCKET_NAME>"
}
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
const projectId = 'PROJECT_ID';
client
.post(client.url('version', 'organizations', orgId, 'projects', projectId))
.auth(orgKey)
.send({
name: 'My Updated Project',
users: [
{ email: '<USER_EMAIL>' },
{ email: '<USER_EMAIL>' }
// ...
],
preferences: {
's3_bucket_name': '<S3_BUCKET_NAME>'
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis();
const orgId = 'ORGANIZATION_ID';
const orgKey = 'ORGANIZATION_KEY';
const projectId = 'PROJECT_ID';
client
.post({
url: client.url('version', 'organizations', orgId, 'projects', projectId),
api_key: orgKey,
params: {
name: 'My Updated Project',
users: [
{ email: '<USER_EMAIL>' },
{ email: '<USER_EMAIL>' },
// ...
],
preferences: {
's3_bucket_name': '<S3_BUCKET_NAME>'
}
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
// Successful result
200 - OK
{
"id": "5efe28d679797411542c7fbe",
"name": "My Updated Project",
"api_keys": {
"master": "<MASTER_API_KEY>",
"read": "<READ_API_KEY>",
"write": "<WRITE_API_KEY>"
},
"users": [
{
"email": "<USER_EMAIL>"
},
{
...
},
...
],
"preferences": {
"s3_bucket_name": "<S3_BUCKET_NAME>"
}
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Organization Key | Update an existing project in the organization. |
Optional Parameters
Parameter | Description |
---|---|
name | Specifies the name of the project to update to |
users | Specifies all users for the project. If any users supplied do not exist, the request will fail. You will receive an error indicating which users are invalid. |
preferences | A JSON object, currently only used for a s3 bucket name. |
Access Keys
Overview
Access Keys are used to further control access to your projects. These keys can be used to authenticate requests to collect or query data. They can also enhance those requests with various options.
Some example cases where you’d want to use a Access Key:
- You’re recording data on behalf of another user and want to make sure that all data recorded by that user is tagged with a specific property.
- You’re presenting a dashboard to a specific user and want to make sure that another user cannot see that user’s data.
- You want to allow certain queries to be accessible to certain users, but not others.
You can see more documenation on Access Keys below and here.
Example Access Key
{
"name": "This is my human_readable string name!",
"is_active": true,
"permitted": ["writes", "queries", "saved_queries", "cached_queries", "datasets", "schema"],
"options": {
"writes": {
"autofill": {
"customer": {
"id": "93iskds39kd93id",
"name": "Ada Corp."
}
}
},
"queries": {
"filters": [{
"property_name": "customer.id",
"operator": "eq",
"property_value": "93iskds39kd93id"
}]
},
"saved_queries": {
"allowed": ["my_saved_query", "my_other_one"],
"blocked": ["my_sensitive_query"],
"filters": [{
"property_name": "customer.id",
"operator": "eq",
"property_value": "93iskds39kd93id"
}]
},
"cached_queries": {
"allowed": ["my_cached_query", "my_other_one"],
"blocked": ["my_sensitive_query"]
},
"datasets": {
"operations": ["read", "list", "retrieve"],
"allowed": {
"my_single_index_dataset": {
"index_by": {
"customer.id": ["93iskds39kd93id"]
}
},
"my_other_dataset_unlimited_access": {}
},
"blocked": ["my_sensitive_dataset"]
}
}
}
The following customization is available when creating specialized Access Keys. Access Key options are represented as a JSON object with the following properties. Each of the properties can be set for your use case:
Property | Description |
---|---|
name | A human readable name for the API Key. Limited to 256 characters. |
is_active | A boolean that indicates if the key is currently active or revoked. |
permitted | A list of high level actions this key can perform. You can read more on this below. Possible options: “writes”, “queries”, “saved_queries”, “cached_queries”, “datasets”, “schema”, “query_definition” |
options | An object containing more details about the key’s permitted and restricted functionality. |
“writes” permitted
When “writes” are permitted, the Access Key will have the ability to stream data to Keen.
Property | Description |
---|---|
options.writes | Container object for write options. |
options.writes.autofill | An object containing properties to be merged with properties sent during data collection. |
“queries” permitted
When “queries” are permitted, the Access Key will have the ability to do ad-hoc queries.
Note: This does not include saved, cached queries, or datasets.
Property | Description |
---|---|
options.queries | Container object for query options. |
options.queries.filters | A list of filters that are automatically added to every query. |
“saved_queries” permitted
When “saved_queries” are permitted, the Access Key will have access to run saved queries.
If you need to create a saved query, update a saved query, delete a saved query, or anything else with a saved query that requires a Master Key, this cannot be done with an Access Key.
Note: If you have a saved query that is being cached, you will need to have “cached_queries” permitted.
Property | Description |
---|---|
options.saved_queries | Container object for saved_query options. |
options.saved_queries.allowed | A list of saved_query resource names this key is allowed to access. |
options.saved_queries.blocked | A list of saved_query resource names this key cannot access. |
options.saved_queries.filters | A list of filters added to every saved query retrieved. |
“cached_queries” permitted
When “cached_queries” are permitted, the Access Key will have access to retrieve results from cached queries.
Note: If you have a saved query that is not being cached, you will need to have “saved_queries” permitted.
Property | Description |
---|---|
options.cached_queries | Container object for cached_query options. |
options.cached_queries.allowed | A list of cached_queries this key is allowed to access. |
options.cached_queries.blocked | A list of cached_queries this key cannot access. |
“datasets” permitted
When “datasets” are permitted, the Access Key will have access to getting a dataset definition, retrieving cached dataset results, and listing cached datasets definitions for a project.
Remember: If you need to create a cached datasets or delete cached datasets, this requires a Master Key and cannot be done with an Access Key.
Property | Description |
---|---|
options.datasets | Container object for Cached Dataset options. |
options.datasets.operations | List of possible operations - “read”, for getting definition; “list”, for getting multiple definitions; “retrieve”, for getting results], create/delete require Master Key |
options.datasets.allowed | An object that says which Cached Datasets this key can access, with optional limiting of “index_by” |
options.datasets.blocked | An object that says which Cached Datasets this cannot access |
“schema” permitted
When “schema” is permitted, you can inspect schema information for a single collection or all the event collections in a given project.
“query_definition” permitted
When “query_definition” is permitted, an access key will let you fetch a definition of a saved / cached query in a given project.
Creating an Access Key
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys
-H "Authorization: MASTER_KEY" \
-H "Content-Type: application/json"
-d '{
"name": "My Access Key",
"is_active": true,
"permitted": ["queries", "cached_queries"],
"options": {
"queries": {
"filters": [
{
"property_name": "customer.id",
"operator": "eq",
"property_value": "asdf12345z"
}
]
},
"cached_queries": {
"allowed": ["my_cached_query"]
}
}
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.post(client.url('projectId', 'keys'))
.auth(client.masterKey())
.send({
name: 'My Access Key',
is_active: true,
permitted: [ 'queries', 'cached_queries' ],
options: {
queries: {
filters: [
{
property_name: 'customer.id',
operator: 'eq',
property_value: 'f234124dsfb'
}
]
},
cached_queries: {
allowed: [ 'my-cached-query' ]
}
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.post({
url: client.url('projectId', 'keys'),
api_key: client.masterKey(),
params: {
name: 'My Access Key',
isActive: true,
permitted: [ 'queries', 'cached_queries' ],
options: {
queries: {
filters: [
{
propertyName: 'customer.id',
operator: 'eq',
propertyValue: 'f234124dsfb',
}
]
},
cachedQueries: {
allowed: [ 'my-cached-query' ],
}
}
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
key_body = {
"name" => "An Example Autofill Key",
"is_active" => true,
"permitted" => ["writes"],
"options" => {
"writes" => {
"autofill": {
"foo": "bar"
}
}
}
}
new_key = Keen.access_keys.create(key_body)
autofill_write_key = new_key["key"] # autofill_write_key will contain the new key's value
from keen.client import KeenClient
client = KeenClient(
project_id='xxxx',
master_key='zzzz'
)
client.create_access_key(name='My Access Key', is_enabled=True, permitted=['queries', 'cached_queries'],
options={
'queries': {
'filters': [
{
'property_name': 'customer.id',
'operator': 'eq',
'property_value': 'f234124dsfb'
}
]
},
'cached_queries': {
'allowed': ['my-cached-query']
}
}
})
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
201 - Created
{
"key": "SDKFJSDKFJSDKFJSDKFJDSK",
"name": "My Access Key",
"is_active": true,
"permitted": ["queries", "cached_queries"],
"options": {
"queries": {
"filters": [
{
"property_name": "customer.id",
"operator": "eq",
"property_value": "asdf12345z"
}
]
},
"cached_queries": {
"allowed": ["my_cached_query"]
}
}
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Master Key | Create a new API Key for the project. |
List all Access Keys
The returned list of Access Keys is paginated by default and could be controlled by two parameters page
and per_page
described in detail below.
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys?name=MyAccessKey
-H "Authorization: MASTER_KEY"
# using pagination:
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys?page=1&per_page=10
-H "Authorization: MASTER_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.get(client.url('projectId', 'keys'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.get({
url: client.url('projectId', 'keys'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
Keen.access_keys.all
from keen.client import KeenClient
client = KeenClient(
project_id='xxxx',
master_key='zzzz'
)
client.list_access_keys()
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
200 - OK
{
"keys": [
{
"key": "SDKFJSDKFJSDKFJSDKFJDSK",
"name": "MyAccessKey",
"is_active": true,
"permitted": ["queries", "cached_queries"],
"options": {
"queries": {
"filters": [
{
"property_name": "customer.id",
"operator": "eq",
"property_value": "asdf12345z"
}
]
},
"cached_queries": {
"allowed": ["my_cached_query"]
}
}
}
]
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Master Key | Retrieves a list of Access Keys. |
Optional Query Parameters
Parameter | Description |
---|---|
name | An exact name of the Access Key to retrieve. There may be only one Access Key with the same name within a particular project. |
page | Needed for pagination. Defines which page of results to return and depends upon the per_page value. If not provided, the default is 0 . |
per_page | Needed for pagination. Defines the number of results to return within a particular page . If not provided, the default is 200 which is the maximum value as well. |
Get an Access Key
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys/CUSTOM_KEY
-H "Authorization: MASTER_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
const keyName = 'my-custom-key-name';
client
.get(client.url('projectId', 'keys?name=' + keyName))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
const keyName = 'my-custom-key-name';
client
.get({
url: client.url('projectId', `keys?name=${keyName}`),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK
Keen.access_keys.get("key-value-here")
from keen.client import KeenClient
client = KeenClient(
project_id='xxxx',
master_key='zzzz'
)
client.get_access_key('my-unique-key-id')
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
200 - OK
{
"key": "SDKFJSDKFJSDKFJSDKFJDSK",
"name": "My Access Key",
"is_active": true,
"permitted": ["queries", "cached_queries"],
"options": {
"queries": {
"filters": [
{
"property_name": "customer.id",
"operator": "eq",
"property_value": "asdf12345z"
}
]
},
"cached_queries": {
"allowed": ["my_cached_query"]
}
}
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
GET | Master Key | Retrieves an Access Key definition. |
Updating an Access Key
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys/CUSTOM_KEY
-H "Authorization: MASTER_KEY" \
-H "Content-Type: application/json"
-d '{
"name": "My Updated Access Key",
"is_active": true,
"permitted": ["queries", "cached_queries"],
"options": {
"queries": {
"filters": [
{
"property_name": "customer.id",
"operator": "eq",
"property_value": "asdf12345z"
}
]
},
"cached_queries": {
"allowed": ["my_cached_query", "another_cached_query"]
}
}
}'
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
const keyName = 'my-custom-key-name';
client
.post(client.url('projectId', 'keys', keyName))
.auth(client.masterKey())
.send({
name: 'My Updated Access Key',
is_active: true,
permitted: [ 'queries', 'cached_queries' ],
options: {
queries: {
filters: [
{
property_name: 'customer.id',
operator: 'eq',
property_value: 'f234124dsfb'
}
]
},
cached_queries: {
allowed: [
'my-cached-query',
'another-cached-query'
]
}
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
const keyName = 'my-custom-key-name';
client
.post({
url: client.url('projectId', 'keys', keyName),
api_key: client.masterKey(),
params: {
name: 'My Updated Access Key',
isActive: true,
permitted: [ 'queries', 'cached_queries' ],
options: {
queries: {
filters: [
{
propertyName: 'customer.id',
operator: 'eq',
propertyValue: 'f234124dsfb',
}
]
},
cachedQueries: {
allowed: [
'my-cached-query',
'another-cached-query',
]
}
}
}
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
update_body = {
name: "New Key Name",
is_active: false,
permitted: ['reads']
}
Keen.access_keys.update("key-value-here", update_body)
from keen.client import KeenClient
client = KeenClient(
project_id='xxxx',
master_key='zzzz'
)
client.update_access_key_full(access_key_id='my-unique-key-id', name='My Updated Access Key', is_active=True,
permitted=['queries', 'cached_queries'],
options={
'queries': {
'filters': [
{
'property_name': 'customer.id',
'operator': 'eq',
'property_value': 'f234124dsfb'
}
]
},
'cached_queries': {
'allowed': ['my-cached-query']
}
}
})
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
200 - OK
{
"name": "My Updated Access Key",
"is_active": true,
"permitted": ["queries", "cached_queries"],
"options": {
"queries": {
"filters": [
{
"property_name": "customer.id",
"operator": "eq",
"property_value": "asdf12345z"
}
]
},
"cached_queries": {
"allowed": ["my_cached_query", "another_cached_query"]
}
}
}
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Master Key | Updates an Access Key |
Revoking an Access Key
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys/CUSTOM_KEY/revoke
-H "Authorization: MASTER_KEY" \
-X POST
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
const keyName = 'my-custom-key-name';
client
.post(client.url('projectId', 'keys', keyName, 'revoke'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
const keyName = 'my-custom-key-name';
client
.post({
url: client.url('projectId', 'keys', keyName, 'revoke'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
Keen.access_keys.revoke("key-value-here")
from keen.client import KeenClient
client = KeenClient(
project_id='xxxx',
master_key='zzzz'
)
client.revoke_access_key('my-unique-key-id')
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
204 - No Content
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Master Key | Revokes an Access Key |
Un-revoking an Access Key
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys/CUSTOM_KEY/unrevoke
-H "Authorization: MASTER_KEY" \
-X POST
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
const keyName = 'my-custom-key-name';
client
.post(client.url('projectId', 'keys', keyName, 'unrevoke'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
const keyName = 'my-custom-key-name';
client
.post({
url: client.url('projectId', 'keys', keyName, 'unrevoke'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
Keen.access_keys.unrevoke("key-value-here")
from keen.client import KeenClient
client = KeenClient(
project_id='xxxx',
master_key='zzzz'
)
client.unrevoke_access_key('my-unique-key-id')
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
204 - No Content
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Master Key | Unrevokes an Access Key |
Deleting an Access Key
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/keys/CUSTOM_KEY \
-H "Authorization: MASTER_KEY" \
-X DELETE
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
204 - No Content
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Master Key | Delete an Access Key |
Maintenance
Discovery
Request
$ curl "https://api.keen.io/3.0?api_key=MASTER_KEY"
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.post(client.url('version'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.post({
url: client.url('version'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Response
{
"projects_resource_url": "/3.0/projects"
}
Returns all available child resources.
HTTP Methods
Method | Authentication | Response |
---|---|---|
GET | Master Key | All available resources |
HEAD | Master Key | Response header |
Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
Delete and Update
This section describes some of the maintenance functions of the Keen API. All maintenance API calls require the use of the Master API Key.
Tips and best practices:
- Deletes and updates are irreversible and it’s easy to make mistakes, so proceed with caution.
- Preview the data that will be deleted by running a count and an extraction with the desired filters, timeframe and timezone parameters. The extraction also gives you an extra backup of your data in case you make a mistake.
- For deletes, be sure to pass filters as query string parameters, not as a part of a content body where they will be ignored.
- Deleting and updating events should be done sparingly, not as a matter of routine. Running large number of deletes and updates, or using deletes as a way to update old events is an anti-pattern. Unlike writes and queries, deletes and updates are not a highly performant operations on the platform.
- DELETE and PUT requests are rate limited at 10/minute, regardless of which resource is being deleted. Learn more about limits.
- The max number of events you can delete or update when specifying filters is 100,000. If attempting to delete more than 100,000 events, subdivide by timeframe. Without filter specified, the limit for update and delete is set to 1,000,000 events.
- Updates are not atomic, so concurrent query can return inconsistent results. Try to avoid sending concurrent updates on the same collection.
Delete a Collection
Resource
https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME?api_key=MASTER_KEY
Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME \
-H "Authorization: MASTER_KEY" \
-H 'Content-Type: application/json' \
-X DELETE
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.del(client.url('events', 'my-event-stream'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.del({
url: client.url('events', 'my-event-stream'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
Keen.delete(:signups) # => true
keen.delete_events("signups")
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Supported by this SDK.
Response
// Successful result
204 - No Content
After testing, you may have some event collections you want to delete. You can do this using the projects page, or our API.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
DELETE | Master Key | Delete a single event collection |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
Delete Events
Resource
https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME?api_key=MASTER_KEY&filters=YOUR_FILTERS_HERE&timeframe=this_7_days
Request
# This example deletes all events that meet the criteria of a supplied set of filters:
$ curl "https://api.keen.io/3.0/projects/${PROJECT_ID}/events/${COLLECTION_NAME}?filters=%5B%7B%22property_name%22%3A%22${PROPERTY_NAME}%22%2C%22operator%22%3A%22${PROPERTY_OPERATOR}%22%2C%22property_value%22%3A%22${PROPERTY_VALUE}%22%7D%5D&timeframe=this_7_days" \
-H "Authorization: ${MASTER_KEY}" \
-X DELETE
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
/*
Filters and timeframe must be passed as encoded query string parameters. This example constructs a complete URL to ensure the request is executed properly.
*/
const url = client.url('events', 'my-event-stream', {
api_key: client.masterKey(),
filters: encodeURIComponent(JSON.stringify([
{
property_name: 'user.id',
operator: 'eq',
property_value: 'f1243353243fdb'
}
])),
timeframe: encodeURIComponent(JSON.stringify({
start: '2015-05-15T19:00:00.000Z',
end: '2015-06-07T19:00:00.000Z'
})),
timezone: 'US/Pacific'
});
client
.del(url)
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
/*
Filters and timeframe must be passed as encoded query string parameters. This example constructs a complete URL to ensure the request is executed properly.
*/
const url = {
url: client.url('events', 'my-event-stream'),
api_key: client.masterKey(),
filters: encodeURIComponent(JSON.stringify([
{
propertyName: 'user.id',
operator: 'eq',
propertyValue: 'f1243353243fdb',
}
])),
timeframe: encodeURIComponent(JSON.stringify({
start: '2015-05-15T19:00:00.000Z',
end: '2015-06-07T19:00:00.000Z'
})),
timezone: 'US/Pacific',
};
client
.del(url)
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
# Or just delete an event corresponding to a particular user
Keen.delete(:signups, :timeframe => "this_7_days", :filters => [{
property_name: 'username', operator: 'eq', property_value: "Bob"
}]) # => true
keen.delete_events("signups", timeframe="this_7_days", filters=[
{
"property_name": 'username',
"operator": 'eq',
"property_value": 'Bob'
}
])
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Supported by this SDK.
Response
// Successful result
204 - No Content
The DELETE method can be used to delete specific events that meet your filter criteria. Please note this functionality should be used in one-off cases rather than in regular use in your data model.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
DELETE | Master Key | Deletes events meeting criteria of supplied filters, timeframe and timezone parameters |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
filters | Optional filters to use when selecting events for deletion |
timeframe | Optional timeframes to use when selecting events for deletion |
timezone | Optional timezone to use when specifying a timeframe |
Delete a Property
DELETE Request
$ curl https://api.keen.io/3.0/projects/PROJECT_ID/events/COLLECTION_NAME/properties/PROPERTY_NAME \
-H "Authorization: MASTER_KEY" \
-H "Content-Type: application/json" \
-X DELETE
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY'
});
client
.del(client.url('events', 'my-event-stream', properties, 'ip_address'))
.auth(client.masterKey())
.send()
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
import KeenAnalysis from 'keen-analysis';
const client = new KeenAnalysis({
projectId: 'PROJECT_ID',
masterKey: 'MASTER_KEY',
});
client
.del({
url: client.url('events', 'my-event-stream', properties, 'ip_address'),
api_key: client.masterKey(),
})
.then(res => {
// Handle response
})
.catch(err => {
// Handle error
});
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Supported by this SDK.
Response
204 - No Content
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
DELETE | Master Key | Delete a property from an event collection. |
Optional Request Parameters
Parameter | Description |
---|---|
api_key | Optional alternative to an Authorization header |
Delete Project
Deletion specified project can be accomplished using our API.
However, please be cautious which project you would like to delete because it’s a hard delete
operation and it causes a project’s removal forever.
Request
$ curl https://api.keen.io/3.0/organizations/ORG_ID/projects/PROJECT_ID \
-X DELETE \
-H "Authorization: ORGANIZATION_KEY" \
-H "Content-Type: application/json"
// Currently not supported by this SDK.
# Currently not supported by this SDK.
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
// Successsful result
204 - No content
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
DELETE | Organization Key | Deletes specified project in the organization. |
Delete Projects
Projects can be deleted from the projects panel. Deleting a project puts it in an inactive state and removes it from the admin interface.
Update Events
Request
# This example updates events related to a specific client, labelling them as spam and updating their description so they could be handled differently in other analyses:
# PUT
$ curl "https://api.keen.io/3.0/projects/${PROJECT_ID}/events/${COLLECTION_NAME} \
-H "Authorization: ${MASTER_KEY}" \
-X PUT \
-d '{
"property_updates": [
{
"property_name": "quantity",
"property_value": 0
},
{
"property_name": "description",
"property_value": "Invalid event",
"upsert_property": true
}
],
"timeframe": {
"start": "2020-09-13T15:00:00.000Z",
"end": "2020-09-20T19:00:00.000Z"
},
"filters" : [
{
"property_name" : "customer.id",
"operator" : "eq",
"property_value" : 67kferw56lf67dd
}
]
}'
// Currently not supported by this SDK.
client = Keen::Client.new(
project_id: PROJECT_ID,
master_key: MASTER_KEY,
api_url: "https://api.keen.io"
)
client.update(:collection, {
property_updates: [
{
property_name: "description",
property_value: "Invalid event"
},
{
property_name: "is_spam",
property_value: true,
upsert_property: true
}
],
timeframe: {
"start": "2020-09-13T15:00:00.000Z",
"end": "2020-09-20T19:00:00.000Z"
},
filters: [
{
property_name: "customer.id",
operator: "eq",
property_value: "67kferw56lf67dd"
}
]
})
# Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
// Currently not supported by this SDK.
Response
// Successful result
200 - OK
{
"updated_events": 4
}
Update method can be used to update specified properties of the events falling within the timeframe and meeting filter.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
PUT | Master Key | Update events |
Request Body Parameters
Parameter | Description |
---|---|
timeframe | Limits the update operation to a specific period of time when the events occurred. |
property_updates | List containing JSON objects describing update definitions. |
filters | Optional, refines the scope of events to be included in the analysis based on event property values. |
Property Update definition
Parameter | Description |
---|---|
property_name | Name of property to be updated. |
property_value | Value to be set for updated property. |
upsert_property |
Boolean value, defaults to false . If true and property_name doesn’t exist within processed event, inserts property with property_value set, otherwise just update it. |
Optional Query String Parameters
Parameter | Description |
---|---|
api_key | Alternative authentication method to providing an Authorization header. |
include_metadata | Specifies whether to enrich query results with execution metadata or not. |
Wardrobe
Overview
The Wardrobe resource returns textiles meeting specified criteria.
Wardrobe Resource
Resource
https://api.keen.io/3.0/projects/523b527836bf5a3216000006/events/wardrobe
Request
$ curl https://api.keen.io/3.0/projects/523b527836bf5a3216000006/events/wardrobe \
-H "Authorization: SPECIAL_KEY_SEE_BELOW" \
-H 'Content-Type: application/json' \
-d '{
"name": "Lara Croft",
"email": "lara@lcroft.com",
"address_line_1": "2624 15th Ave",
"address_line_2": "Apt 1",
"city": "San Francisco",
"state": "CA",
"zip": "94127",
"country": "USA",
"garment_gender": "Womens",
"size": "L"
}'
Response
// Successful result
204 - {"created":true}
Use this resource to request a garment of a given size to a given address. This is really just the Event Collection Resource, except you must use a specific project ID, Write Key, and collection name. See below.
Here’s an example of what the event collection request would look like using the REST API.
Request a garment.
Supported HTTP Methods
Method | Authentication | Description |
---|---|---|
POST | Special Key | Request a garment |
Required Request Parameters
Parameter | Description |
---|---|
name | A name for the garment recipient |
A valid email address | |
address_line_1 | First line of your shipping address |
address_line_2 | Second line if needed |
city | Name of your home city |
state | State or Province |
country | Country |
zip | Zip or postal code |
garment_gender |
Women's or Unisex
|
size | S, M, L, or XL |