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 Sensedge, Sensedge Mini, or other device.

How to get started:

  1. Get your API key at app.kaiterra.com. Once you’ve created an account, go to your Account Settings and click API Keys. Finally, click Generate API Key.

  2. If you’re a Postman user, you can try out our API using our public Demo workspace. 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. Find the Unique Device IDs (UDIDs) for your Kaiterra devices in the device’s settings (Sensedge) or in the Kaiterra mobile app.

  4. 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 app.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.

All sampling intervals, such as for hourly-averaged data, are labelled by the ending time of the sample sample, not the starting time. This is consistent with the way all apps report hourly average data from official sensors, probably because it avoids the problem of users thinking that the data they’re seeing is an hour older than it is. Note that this implies daily data for e.g. the 15th of March is labelled as 2016-03-16T00:00Z, so clients may want to subtract a day when labelling such data.

There is no hour 24; a bucket extending from 11pm on Dec 31 to midnight is labelled as 01-01 00:00, length 1 hour.

Latitude and longitude are specified in requests as a comma-separated pair, like 39.96,115.98 (latitude, longitude), and in responses as an array of length 2 (again, latitude, then longitude).

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 two regions. The exact same Kaiterra API is published in each region, under these domains:

If your code only deals with devices in a single region, just use the domain from that region. Edge cases that arise when dealing with both regions are detailed below.

Edge Cases

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/devices/00000000-0001-0101-0000-00007e57c0de/top?key=$KAITERRA_APIV1_URL_KEY"
> GET /v1/devices/00000000-0001-0101-0000-00007e57c0de/top?key=0123456789abcdef-YOUR-API-KEY HTTP/2
> Host: api.kaiterra.cn
> user-agent: curl/7.81.0
> accept: */*
>
< HTTP/2 301
< location: https://api.eur.kaiterra.com/v1/devices/00000000-0001-0101-0000-00007e57c0de/top?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 allows the client to gather data from multiple devices in a single HTTP request. 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 special case, only for requests for the most recent data for a device (/devices/{id}/top), 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

Device metadata
GET/devices/{id}

Retrieves metadata, such as name and firmware version, for the given device.

Example URI

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

Device unique ID

Response  200
HideShow

Here is some text.

  • name (string) - Friendly name of the device assigned by the owner in the mobile app or dashboard.

  • model (string) - Model number of the device.

  • firmware_version (string) - The last reported firmware version of the device.

  • home_region (string) - Home region of the current device.

  • handshake (object) - Extended metadata reported by the device in its last hourly handshake. Properties are as follows:

    • _device_ts (string) - The device’s internal clock

    • dmac_eth or dmac_ethernet (string) - The device’s Ethernet MAC address, if applicable

    • dmac_wifi or dmac (string) - The device’s Wifi MAC address

    • dsn (string) - The device’s serial number

    • sbay102.slifetime (number) - The fraction of useful sensor module lifetime remaining (left sensor bay)

    • sbay102.stype (string) - The model number of the sensor module installed (left sensor bay)

    • sbay103.slifetime (number) - The fraction of useful sensor module lifetime remaining (right sensor bay)

    • sbay103.stype (string) - The model number of the sensor module installed (right sensor bay)

    • ts (string) - The timestamp on the server when the handshake was received

Body
{
  "id": "00000000-0031-0101-0000-00007e57c0de",
  "name": "Office",
  "model": "SE-100",
  "firmware_version": "1.15.0.2",
  "home_region": "row.europe",
  "handshake": {
    "_device_ts": "2022-07-25T02:48:00Z",
    "dmac_eth": "00:00:00:00:0F:FF",
    "dmac_wifi": "54:86:FF:28:04:87",
    "dsn": "VC27911111",
    "sbay102": {
      "slifetime": 0.2996,
      "stype": "KM103"
    },
    "sbay103": {
      "slifetime": 0.2994,
      "stype": "KM100"
    },
    "ts": "2022-07-25T02:47:59Z"
  }
}
Response  404
HideShow

Indicates that a device with the given UDID has not yet been registered.

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
        }
      ]
    }
  ]
}

Historical sensor readings
GET/devices/{id}/history{?limit,group_by}

Retrieves sensor air quality historical data using the specified parameters. Returned data points are marked with the end of the sampling interval; for instance, the one hour average from 1pm to 2pm is marked with a timestamp of 14:00, not 13:00. All available intervals that overlap the requested range are returned. If data is unavailable, it is omitted or null is the value for that data point.

Notes on display of hourly and daily data:

  • Time stamps are returned in UTC, so they must be converted back to local time for display. Recall that the timestamp of an interval is the end of that interval, so data for the 10th of January UTC is stamped 2017-01-11T00:00:00Z. You will probably want to subtract a day before displaying the data’s date.

Notes on data availability:

  • Hourly and daily averages are computed only after the hour- or day-long average window has already closed. Furthermore, late-arriving data may continue to contribute to the averages even after the window has closed. In particular, Sensedges that lose internet connectivity will buffer data indefinitely, then upload it when connectivity is restored.

Example URI

GET https://api.kaiterra.com/v1/devices/00000000-0001-0101-0000-00007e57c0de/history?limit=10&group_by=15m
URI Parameters
HideShow
id
string (required) Example: 00000000-0001-0101-0000-00007e57c0de

Laser egg unique ID

begin
string (optional) 

Beginning timestamp of the data being requested. Default is 1 week (168 hours) before end.

end
string (optional) Example: 2017-01-17T10:24:12Z

Ending timestamp of the data being requested. If unspecified, the current UTC time is used.

limit
number (optional) Example: 10

Retrieves only the latest N data points. This value has no upper limit; if the number of results is too large to fit into a single response, pagination is used to allow the client to continue the query.

group_by
string (optional) Example: 15m

Performs time averaging on the raw data. Valid intervals are: 1m, 5m, 15m, or any number of minutes that divides evenly into one hour; 1h, 2h, or any whole number of hours that divides evenly into one day; or 1d. Use of larger averaging intervals requires use of the time_zone parameter to specify where hour or day boundaries lie.

time_zone
string (optional) Example: Europe/Brussels

Defines the hourly and daily averaging window boundaries for larger values of group_by. Must be an entry from the TZ Database.

Response  200
HideShow

The the data array contains available data in the query range, in ascending order by timestamp, subject to the defaults for begin and end described above. For queries that return large amounts of data (for example, more than one week of raw data), see the below section on pagination.

Body
{
  "data": [
    {
      "param": "rhumid",
      "units": "%",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:25:00Z",
          "value": 33
        },
        {
          "ts": "2020-06-17T06:30:00Z",
          "value": 81
        },
        {
          "ts": "2020-06-17T06:35:00Z",
          "value": 43
        },
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 55
        },
        {
          "ts": "2020-06-17T06:45:00Z",
          "value": 44
        }
      ]
    },
    {
      "param": "rpm10c",
      "units": "µg/m³",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:25:00Z",
          "value": 120
        },
        {
          "ts": "2020-06-17T06:30:00Z",
          "value": 120
        },
        {
          "ts": "2020-06-17T06:35:00Z",
          "value": 120
        },
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 120
        },
        {
          "ts": "2020-06-17T06:45:00Z",
          "value": 120
        }
      ]
    },
    {
      "param": "rpm25c",
      "units": "µg/m³",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:25:00Z",
          "value": 234
        },
        {
          "ts": "2020-06-17T06:30:00Z",
          "value": 135
        },
        {
          "ts": "2020-06-17T06:35:00Z",
          "value": 250
        },
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 205
        },
        {
          "ts": "2020-06-17T06:45:00Z",
          "value": 192
        }
      ]
    },
    {
      "param": "rtemp",
      "units": "C",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:25:00Z",
          "value": 12
        },
        {
          "ts": "2020-06-17T06:30:00Z",
          "value": -3
        },
        {
          "ts": "2020-06-17T06:35:00Z",
          "value": 39
        },
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 3
        },
        {
          "ts": "2020-06-17T06:45:00Z",
          "value": 13
        }
      ]
    },
    {
      "param": "rtvoc",
      "units": "ppb",
      "span": 60,
      "points": [
        {
          "ts": "2020-06-17T06:25:00Z",
          "value": 369
        },
        {
          "ts": "2020-06-17T06:30:00Z",
          "value": 313
        },
        {
          "ts": "2020-06-17T06:35:00Z",
          "value": 303
        },
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 397
        },
        {
          "ts": "2020-06-17T06:45:00Z",
          "value": 234
        }
      ]
    }
  ]
}
Request  Hourly Average Data
Response  200
HideShow

When group_by is 1h, hourly data is reported. The example below is for time_zone=Asia/Kathmandu, which has a UTC offset of +5:45; the hourly divisions happen on the hour in local time, which in UTC time is not 45 but 15 minutes after the hour.

Body
{
  "data": [
    {
      "param": "rpm25c",
      "units": "µg/m³",
      "span": 3600,
      "points": [
        {
          "ts": "2020-06-17T02:15:00Z",
          "value": 195.3
        },
        {
          "ts": "2020-06-17T03:15:00Z",
          "value": 192.5
        },
        {
          "ts": "2020-06-17T04:15:00Z",
          "value": 198.5
        },
        {
          "ts": "2020-06-17T05:15:00Z",
          "value": 208
        },
        {
          "ts": "2020-06-17T06:15:00Z",
          "value": 211.1
        }
      ]
    },
    {
      "...": "..."
    }
  ]
}
Request  Daily Average Data
Response  200
Request  Pagination for Large Amounts of Data
Response  200
HideShow

Since most devices record a sensor reading once per minute, requesting a week or more of data results in very large response bodies – for example, the compressed size of a reponse body for 10,000 items can approach 1 megabyte. So, a single response body is capped around 10,000 items, with items with the latest timestamps being returned first, along with a link to the next “page” of data.

Warning: This item count cap is subject to change without notice. Clients must not hard-code this value.

The link to the next page in the results is returned under _links, next. This is an absolute URL; just add your auth credentials and make the call.

Body
{
  "_links": {
    "next": "https://api.kaiterra.com/v1/devices/00000000-0001-0101-0000-00007e57c0de/history?begin=2020-06-10T06%3A29%3A00Z&end=2020-06-17T06%3A29%3A00Z"
  },
  "data": [
    {
      "param": "rpm25c",
      "units": "µg/m³",
      "span": 60,
      "points": [
        {
          "...": "..."
        },
        {
          "ts": "2020-06-17T06:30:00Z",
          "value": 135
        },
        {
          "ts": "2020-06-17T06:35:00Z",
          "value": 250
        },
        {
          "ts": "2020-06-17T06:40:00Z",
          "value": 205
        },
        {
          "ts": "2020-06-17T06:45:00Z",
          "value": 192
        },
        {
          "ts": "2020-06-17T06:50:00Z",
          "value": 206
        }
      ]
    },
    {
      "...": "..."
    }
  ]
}
Response  400
HideShow

The request is improperly formatted; check the response body for additional context. Also, if the entire time range (after defaults for unspecified parameters are applied) lies more than one hour in the future, this error code is returned.

Response  403
Response  404

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 06 Feb 2024