Back to top

Kaiterra API

Version: 2022-05-24

Overview

The Kaiterra API allows your programs to access the air quality data reported by your Laser Egg, Sensedge, Square, or other device.

How to get started:

  1. Get your API key at dashboard.kaiterra.com. Once you’ve created an account, go to the Profile page (under the gear icon in the upper right), then the Developer section. Click Generate a new key.

  2. If you’re a Postman user, try out the API by using Import -> Link, and paste this link: https://www.getpostman.com/collections/c7baa037ade5b51fa190 You’ll need to use the Desktop version of Postman for our API. Note that you’ll need to create a Postman environment with a variable called api-key (set to your Kaiterra API key) to try the examples in the collection.

  3. Download some Python sample code from GitHub.

  4. Find the Unique Device IDs (UDIDs) for your Kaiterra devices in the device’s settings (Sensedge) or in the Kaiterra mobile app.

  5. Don’t have any Kaiterra devices yet? Feel free to use our test devices from this documentation – the ones with IDs beginning with “00000000-” – to get familiar with the API:

    1. 00000000-0031-0101-0000-00007e57c0de is a test Sensedge

    2. 00000000-0001-0101-0000-00007e57c0de is a test Laser Egg

Auth

All requests to the API must be authenticated. Get your API key by signing up at dashboard.kaiterra.com.

We support the following kinds of authentication:

  • URL-based key: When issuing an HTTP request, the client includes the key in a URL parameter called key (this is how e.g. Google Maps works). All requests must use HTTPS, so the key is safe from eavesdropping. This method is simple, but it’s only suitable for clients that are running on trusted devices, such as a server you control, or a researcher’s workstation. These keys must NOT be embedded directly into web pages, or iOS or Android apps.

If authentication information is required but not present, or if it is not accepted, HTTP 401 is returned (401 is called “Unauthorized”, but it should really be called “Unauthenticated”).

Common Parameters

Unique Device IDs (UDIDs) for Kaiterra devices are 128-bit random UUIDs. They’re case-insensitive and may be submitted with or without intermediate dashes (46048117-86a7-488e-9c58-708f3470ee11 and 4604811786a7488e9c58708f3470ee11 are both OK).

The API enforces access control on device data as follows:

  • For devices managed under an organization, access is restricted to users within that organization, and the API keys they create. For example, consider a device owned by Organization A: if a user who belongs to another organization (or who does not belong to any organization) creates an API key and attempts to access data for that device, the API will return HTTP 403 Forbidden.

  • Data for devices not added to an organization can be accessed by any API key. While UDIDs consist of 128 random bits and are therefore unguessable, API users should treat them as secrets that should not be published online (such as in a public Git repository).

All dates/times follow RFC3339 (a refinement of ISO8601), which looks like 2016-12-07T05:32:16Z. Milliseconds are accepted but are ignored (truncated). All times must be in UTC; any that aren’t will be rejected with 400 bad request.

The aqi query string parameter, if present, causes APIs returning pollutant data to precalculate corresponding air quality index values according to the air quality index of the given country or region. Codes are the 2-character codes from ISO3166. Supported values are cn, in, and us.

Request Headers

The following headers are technically optional but are a good idea:

  • Accept-Encoding: gzip is supported; clients are encouraged to use it.

  • Content-Encoding: if there is a request body, this header denotes its encoding. Always use UTF-8.

Sensor Reading Data Format

Data on various pollutants or other parameters (like temperature and humidity) are returned first as an array of parameters, with a series of data points under each parameter:

{
    "data": [
        {
            "param": "rpm25c",
            "units": "µg/m³",
            "source": "km100",
            "span": 60,
            "points": [
                {
                    "ts": "2020-06-17T03:40:00Z",
                    "value": 120
                }
            ]
        },
        {
            "param": "rtemp",
            "units": "%",
            "span": 60,
            "points": [
                {
                    "ts": "2020-06-17T03:40:00Z",
                    "value": 62
                }
            ]
        }
    ]
}

Each series under data has the following properties:

  • param: The parameter code. These are listed below.

  • units: The units under which the parameter is expressed. These are listed below.

  • source: (Present for sensor modules) The module that captured the parameter reading. For instance, km102 identifies the KM-102 sensor module.

  • span: The sampling interval, in seconds, over which this measurement was taken. For example, 60 refers to a sample taken over the span of one minute, and 3600 designates hourly averages.

  • points: The actual array of data points for this series. Even if the series has only one data point, this is still an array.

PARAMETER CODES

Code Meaning Default Units
rco2 Carbon dioxide ppm
ro3 Ozone ppb
rpm25c PM2.5 µg/m³
rpm10c PM10 µg/m³
rhumid Relative humidity %
rtemp Temperature C
rtvoc Total Volatile Organic Compounds (TVOC) ppb

UNIT CODES

Code Meaning
ppm Parts per million (volumetric concentration)
ppb Parts per billion
µg/m³ Micrograms per cubic meter (mass concentration)
mg/m³ Milligrams per cubic meter
C Degrees Celsius
F Degrees Fahrenheit
x Count of something, such as readings in a sampling interval
% Percentage, as with relative humidity

Multi-Region

The Kaiterra cloud is split into multiple regions. The exact same Kaiterra API is published in each region, under these domains:

In most cases, code written against the Kaiterra API before the multi-region split will continue to work unmodified. There are a few exceptions, which are detailed below.

Home Region and HTTP Redirects

Data for a particular device is stored in exactly one region. The region where a device’s data is stored is called the device’s home region.

To query data for a device, you must make the request against the Kaiterra API in the device’s home region. If you make the request against the API in any other region, you will receive an HTTP 301 (Moved Permanently) response, with the response’s Location header indicating where the query should be retried from now on.

For HTTP verbs other than GET, you’ll receive HTTP 308 (Permanent Redirect) instead. This is because, as RFC 7538 states, “This status code is similar to 301 (Moved Permanently) … except that it does not allow changing the request method from POST to GET.”

For example:

$ curl -v "https://api.kaiterra.cn/v1/lasereggs/00000000-0001-0101-0000-00007e57c0de?key=$KAITERRA_APIV1_URL_KEY"
> GET /v1/lasereggs/00000000-0001-0101-0000-00007e57c0de?key=0123456789abcdef-YOUR-API-KEY HTTP/2
> Host: api.kaiterra.cn
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/2 301
< location: https://api.eur.kaiterra.com/v1/lasereggs/00000000-0001-0101-0000-00007e57c0de?key=0123456789abcdef-YOUR-API-KEY
< date: Wed, 13 May 2020 07:21:32 GMT
< content-length: 0
< vary: Origin
<
* Connection #0 to host api.kaiterra.cn left intact

Since HTTP 301/308 are part of the HTTP standard, most HTTP libraries will follow the redirects automatically.

Redirect vs. Proxy

Why does the Kaiterra API redirect the client, instead of proxying the call (making the call to the correct region on behalf of the client)? There are at least two reasons for this:

  1. In a strict sense, the resource has indeed permanently moved – it is no longer available at the domain under which it was requested. This is precisely what response code 301 is for.

  2. Internet connections crossing in and out of Mainland China are not nearly as reliable as connections that do not need to cross this boundary. It’s far better that the client make the call to the device’s home region directly, especially if both the client and device are outside (or inside) Mainland China.

The one exception to this is the /batch endpoint, which is described below.

Redirects and the /batch Endpoint

The /batch API endpoint, which allows the client to gather data from multiple devices in a single HTTP request, continues to function exactly as before. However, this includes the possibility of sub-requests returning HTTP 301 or 308 if the device’s home region is elsewhere. This behavior is problematic for clients not written to deal with it.

As a concession, only for requests for the most recent data for a device (/lasereggs/{id}, /sensedges/{id}), the Kaiterra API does proxy those requests to the device’s home region on behalf of the client.

However, there is a nonzero chance that these calls will fail. So, ideally, the client would detect these redirects and itself make the /batch request directly against the other region.

Redirects and Root Certificates

The SSL certificates for api.kaiterra.com and api.kaiterra.cn use the ISRG Root X1 certificate as their root. While this certificate is already trusted by all modern environments, some older Android devices do not trust it.

The deprecated api.origins-china.cn domain uses an IdentTrust DST root. If your code runs in an environment without the ISRG Root X1 certificate, connects to api.origins-china.cn, and receives a redirect to api.kaiterra.com or .cn, then the SSL handshake will fail. In this case, you will need to either add the ISRG Root X1 certificate to your device’s trusted root certificate store, or supply it as a trusted root certificate at connection time. Most SSL libraries support this.

Devices

Data and metadata for all types of Kaiterra devices are available under the /devices endpoint.

All Devices

Latest sensor reading
GET/devices/{id}/top

Retrieves the last sensor reading uploaded by the device.

Example URI

GET https://api.kaiterra.com/v1/devices/00000000-0031-0101-0000-00007e57c0de/top
URI Parameters
HideShow
id
string (required) Example: 00000000-0031-0101-0000-00007e57c0de

Unique ID of the device whose details are being requested.

Response  200
HideShow

The device’s most recent sensor reading is returned.

Body
{
  "data": [
    {
      "param": "rco2",
      "units": "ppm",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 1673
        }
      ]
    },
    {
      "param": "rhumid",
      "source": "km102",
      "units": "%",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 55.81
        }
      ]
    },
    {
      "param": "rpm10c",
      "source": "km100",
      "units": "µg/m³",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 125
        }
      ]
    },
    {
      "param": "rpm25c",
      "source": "km100",
      "units": "µg/m³",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 169
        }
      ]
    },
    {
      "param": "rtemp",
      "source": "km102",
      "units": "C",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 26.25
        }
      ]
    },
    {
      "param": "rtvoc",
      "source": "km102",
      "units": "ppb",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 397
        }
      ]
    }
  ]
}

Other

Batch Requests

Submit a Batch Request
POST/batch

This endpoint makes it faster to make many API requests against the Kaiterra API.

For example: the API request to get a sensor’s latest reading, GET /devices/{id}/top, typically takes less than 1ms of processing time on the server. This means the time for your application to retrieve the data will be dominated by the round-trip time to Kaiterra’s servers and back. If you need to get data for many devices, then that round-trip time becomes non-negligible.

The POST /batch endpoint allows you to make up to 100 API requests at once, with only one round-trip. For endpoints like GET /devices/{id}/top, you’ll get the information nearly 100 times faster than if you had made the requests individually.

The format is nearly identical to Facebook’s batch request API, except that there is no support for dependent requests and other advanced features.

Authentication parameters, such as the key URL parameter, must be included in URL or headers of the POST batch request. Each “sub-request” inherits the auth context of the POST batch request; any auth parameters on the sub-requests will be ignored. Therefore, it’s possible that some sub-requests will succeed, and others will fail with 403 Forbidden.

While the JSON objects in the batch response will be in the same order as the objects in the batch request, the relative order in which the server fulfills each request is undefined. For example, in a batch request that contains a PATCH and a GET request on the same resource, the result of the GET request may or may not reflect the changes made by the PATCH request.

Example URI

POST https://api.kaiterra.com/v1/batch
URI Parameters
HideShow
include_headers
boolean (required) Example: false

Default: false. Whether to include the response headers for individual responses.

Request
HideShow

The batch request body is a JSON array of request objects. Each object must have a method and relative_url property; other properties are optional.

  • method (string) - The HTTP method to use.

  • relative_url (string) - The URL to request, relative to the Kaiterra API’s base URL.

  • headers (json, optional) - A JSON array of header description objects, each of which has a name and value object.

  • body (string, optional) - The request body for this request. JSON objects must be JSON-encoded before being placed in this string.

Headers
Content-Type: application/json
Body
[
  {
    "method": "GET",
    "relative_url": "/devices/00000000-0001-0101-0000-00007e57c0de/top"
  },
  {
    "method": "GET",
    "relative_url": "/devices/00000000-0031-0001-0000-00007e57c0de/top"
  }
]
Response  200
HideShow

Returns the result of each of the requested operations, with the body returned as a JSON string. Note that HTTP 200 will be returned as long as the batch request itself did not fail processing, even if none of the sub-requests succeeded.

Body
[
  {
    "body": "{\"data\":[{\"param\":\"rhumid\",\"units\":\"%\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":54}]},{\"param\":\"rpm10c\",\"units\":\"µg/m³\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":120}]},{\"param\":\"rpm25c\",\"units\":\"µg/m³\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":191}]},{\"param\":\"rtemp\",\"units\":\"C\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":16}]},{\"param\":\"rtvoc\",\"units\":\"ppb\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":342}]}]}",
    "code": 200
  },
  {
    "body": "{\"data\":[{\"param\":\"rco2\",\"units\":\"ppm\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":1673}]},{\"param\":\"rhumid\",\"source\":\"km102\",\"units\":\"%\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":54.79}]},{\"param\":\"rpm10c\",\"source\":\"km100\",\"units\":\"µg/m³\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":125}]},{\"param\":\"rpm25c\",\"source\":\"km100\",\"units\":\"µg/m³\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":275}]},{\"param\":\"rtemp\",\"source\":\"km102\",\"units\":\"C\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":20.57}]},{\"param\":\"rtvoc\",\"source\":\"km102\",\"units\":\"ppb\",\"span\":60,\"points\":[{\"ts\":\"2020-06-17T07:05:00Z\",\"value\":435.6}]}]}",
    "code": 200
  }
]

Generated by aglio on 30 Aug 2022