# Rune Labs V2 Stream API

An HTTP API to query timeseries data.

## Overview

### V1 vs V2

Like the [V1 Stream API](https://docs.runelabs.io/stream/v1), the V2 API can be used to query timeseries which are derived from data uploaded to the Rune Platform. However, V2 introduces new [concepts](https://docs.runelabs.io/stream/v2/#section/Overview/Definitions) that streamline how data is organized, stored, and accessed. While the data from both APIs is derived from the same uploaded datasets, the underlying storage is entirely independent.

### Definitions

#### Patient

The person about whom data was measured. This may include a [StrivePD](https://www.strive.group/) user, a person with a neural implant, etc. Each patient has a unique identifier ("patient ID"). Patient IDs are generated by Rune and cannot be changed.

#### Device

The sensor serving as the data source (e.g. a DBS implant, iOS application, wearable device, etc.). A patient may have multiple devices: each one has a unique identifier ("device ID"). Device IDs are generated by Rune and cannot be changed. They do not correspond to any identifier or information from the device itself.

#### Stream

A single timeseries, defined by a **stream type** and a unique combination of **parameters** (key/value pairs). Each stream has a unique identifier ("stream ID").

To access data from this API, you must know which stream IDs you're interested in querying. Use Rune's GraphQL API to _discover_ the stream IDs that are available to you.

#### Stream Type

A categorization of streams, both semantically and structurally. Roughly speaking, it represents the **physical quantity being measured** (voltage, acceleration, etc), including the unit of measurement. It also describes the shape of the stream’s data, as one or more dimensions. Think of a dimension as a column in a table, where each value in the timeseries is a row. Stream type definitions also include human-readable labels and descriptions.

#### Algorithm

A label associated with each stream, which describes how data was processed to produce the stream values (including how it is parameterized). Each algorithm label is formatted as `algorithm-name.version` (e.g. `ingest-strive-applewatch-motion.0`).

The term "algorithm" does not necessarily imply meaningful data manipulation or calculations (though it can). For example, the `ingest-strive-applewatch-motion` algorithm simply ingests the raw accelerometry data that is recorded on an Apple Watch and uploaded by the Strive iOS application.

#### Stream Parameters

Key/value pairs that are used to label streams.

All streams are labeled with the following:

- `patient_id` — the anonymized ID of the person whose data this is.
- `device_id` — the device from which the stream was recorded.
- `algorithm` — describes how data was processed to produce the stream (see "Algorithm" definition).

Streams may _also_ have any number of parameters, with other key/value pairs. By convention, all of our streams include the following parameters:

- `category`– a broad categorization of the data type.  
  E.g. `neural`, `vital_sign`, etc
- `measurement`– a specific label for what is being measured  
  E.g. `heart_rate`, `step_count`, etc

For details about how streams are parameterized, refer to the documentation for a particular algorithm.

### Authentication

Authentication is required for all API requests. The V2 API uses the same authentication mechanisms as V1: see the [V1 API documentation](https://docs.runelabs.io/stream/v1/#section/Overview/Authentication) for details.

### Errors

Standard HTTP status codes are used to indicate success or failure. In the event of most errors, the response body is JSON, with the following format:

```json
{
  "success": false,
  "error": {
    "message": "Human-readable error description",
    "type": "EnumeratedErrorKind"
  }
}
```

### Pagination

There is a limit on the amount of data that is returned in a response. This page size is determined by the server. If the queried time range contains more than one page’s worth of data, you must make multiple API requests to fetch the complete result.

If an API response includes a `X-Rune-Next-Page-Token` header, make another request to fetch the next page of data. In the next request, provide the _value_ of this header in the `page_token` query parameter. Keep the rest of the query parameters identical. When the end of pagination has been reached, the `X-Rune-Next-Page-Token` header field will not exist in the response.

Each pagination token is **valid for 5 minutes**.

## Single Stream

### Stream Data

A successful request returns data from a single stream. The response format is determined largely by the **stream type** of the data: CSV columns (or JSON keys) are named for the dimensions of the corresponding stream type.

There is always a column/key that contains the **duration** of each measurement in the timeseries (in nanoseconds): "measurement_duration_ns". The duration value is extracted from the device data, with the following order of precedence:

- Some devices specify a start and end time for each measured value. Whenever this is the case, the **start time** of each interval is used as the timestamp for each observation in the timeseries, and the duration is `end_time – start_time`.
- Other devices specify a **sampling rate** for a particular measurement. When this is the case (and the true duration is unknown), the duration of each observation is set to `1/sample rate`.
- Otherwise, the duration is `0` (which indicates that it is unknown).

##### path Parameters

|     |     |
| --- | --- |
| stream_id<br>required | string<br>Example: a1379454321e63222f547d8f929a2451d0f295cfbd77fabf9ce8090e1c31c571<br>Unique ID for a Stream. |

##### query Parameters

|     |     |
| --- | --- |
| start_time | float<br>Unix timestamp (in seconds, with optional fraction) of the start of the<br>time range to query. This is an inclusive boundary (`>= start_time`). <br>It is invalid to specify both `start_time` and `start_time_ns`. |
| start_time_ns | int<br>The same as `start_time`, but expressed as **integer nanoseconds**.<br>It is invalid to specify both `start_time` and `start_time_ns`. |
| end_time | float<br>Unix timestamp (in seconds, with optional fraction) of the end of the<br>time range to query. This is an exclusive boundary (`< end_time`). <br>It is invalid to specify both `end_time` and `end_time_ns`. |
| end_time_ns | int<br>The same as `end_time`, but expressed as **integer nanoseconds**.<br>It is invalid to specify both `end_time` and `end_time_ns`. |
| format | string<br>Default: "csv"<br>Enum:"json""csv"<br>Determines the content type of the response. |
| limit | int<br>The maximum number of timestamps to return, across _all pages_ of the <br>response. |
| page_token | string <byte> <br>A token provided in the request to obtain the subsequent page of<br>records in a dataset. The value is obtained from the<br>`X-Rune-Next-Page-Token` response header field. See [Pagination](https://docs.runelabs.io/stream/v2/#section/Overview/Pagination)<br>for details. |
| timestamp | string<br>Default: "unix"<br>Enum:"unix""unixns""iso"<br>Determines how timestamps are formatted in the response:<br>- `unix` \- Unix seconds, with up to 7 fractional digit precision.<br>- `unixns` \- Unix nanoseconds (integer).<br>- `iso` \- Formatted as RFC3339, including the `timezone` offset. |
| timezone | integer<br>Default: 0<br>Example: timezone=-28800<br>The timezone offset, in seconds, used to calculate string-based<br>timestamp formats such as `datetime` and `iso`. For example,<br>PST (UTC-0800) is represented as `-28800`. If omitted, the<br>timezone is UTC. |
| timezone_name | string<br>Default: ""<br>Example: timezone_name=America/Los_Angeles<br>The name from the IANA timezone database used to calculate string-based<br>timestamp formats such as `datetime` and `iso`. For example,<br>`America/Los_Angeles`. While `timezone` uses a fixed offset,<br>`timezone_name` will return the correct UTC offset for a<br>given date/time in order to account for daylight saving time.<br>If omitted, the timezone is UTC unless a `timezone` parameter<br>is provided. If both a `timezone` and a `timezone_name` are provided<br>the request will return an error. If `timezone_name` is provided, aggregate<br>queries with resolution equivalent to multiples of a day (86400 seconds)<br>will group result windows by calendar day(s) in the provided timezone. |
| translate_enums | string<br>Default: false<br>Enum:truefalse<br>When fetching a stream that includes an `enum` data type, this determines<br>whether to translate enums into their string representation. By default,<br>enums are returned as integer values.<br>All possible enum values for a given stream can be queried from the <br>GraphQL API, as part of the stream metadata. |

### Responses

**200**

Successful request

**400**

Bad request

**401**

Not authorized to perform the request

**404**

Not found

**500**

Internal server error

### Stream Availability

Returns a boolean timeseries, indicating whether stream data exists in a given window of time. The `resolution` parameter determines the granularity of this timeseries (i.e. the interval between each timestamp in the series).

The timeseries is aligned such that each timestamp (as Unix seconds), is evenly divisible by the `resolution`. If the requested `start_time` is not evenly divisible by the `resolution`, the query range is extended to the nearest _preceding_ aligned time. If the `end_time` is not evenly divisible by the `resolution`, the query range is extended to the nearest _following_ aligned time. This ensures that the returned timestamps are evenly spaced _and_ that each one represents a duration of time equal to the `resolution` (rather than getting truncated).

##### path Parameters

|     |     |
| --- | --- |
| stream_id<br>required | string<br>Example: a1379454321e63222f547d8f929a2451d0f295cfbd77fabf9ce8090e1c31c571<br>Unique ID for a Stream. |

##### query Parameters

|     |     |
| --- | --- |
| start_time<br>required | float<br>Unix timestamp (in seconds, with optional fraction) of the start of the<br>time range to query. This is an inclusive boundary (`>= start_time`). |
| end_time<br>required | float<br>Unix timestamp (in seconds, with optional fraction) of the end of the<br>time range to query. This is an exclusive boundary (`< end_time`). |
| resolution<br>required | integer \[ 300 .. 86400 \] <br>Interval between returned timestamps, in seconds. |
| format | string<br>Default: "csv"<br>Enum:"json""csv"<br>Determines the content type of the response. |
| limit | int<br>The maximum number of timestamps to return, across _all pages_ of the <br>response. |
| page_token | string <byte> <br>A token provided in the request to obtain the subsequent page of<br>records in a dataset. The value is obtained from the<br>`X-Rune-Next-Page-Token` response header field. See [Pagination](https://docs.runelabs.io/stream/v2/#section/Overview/Pagination)<br>for details. |
| partition_size | integer<br>If set, break up result data for each stream into subsets of<br>`partition_size` number of points. The last partition returned<br>may not necessarily contain `partition_size` points.<br>Only applicable for JSON-formatted responses (`format=json`).<br>See response example. |
| timestamp | string<br>Default: "unix"<br>Enum:"unix""unixns""iso"<br>Determines how timestamps are formatted in the response:<br>- `unix` \- Unix seconds, with up to 7 fractional digit precision.<br>- `unixns` \- Unix nanoseconds (integer).<br>- `iso` \- Formatted as RFC3339, including the `timezone` offset. |
| timezone | integer<br>Default: 0<br>Example: timezone=-28800<br>The timezone offset, in seconds, used to calculate string-based<br>timestamp formats such as `datetime` and `iso`. For example,<br>PST (UTC-0800) is represented as `-28800`. If omitted, the<br>timezone is UTC. |

### Responses

**200**

Successful request

**400**

Bad request

**401**

Not authorized to perform the request

**404**

Not found

**500**

Internal server error

### Daily Aggregate

Returns an average aggregated day divided into intervals. This endpoint is useful for powering visualizations that highlight daily patterns based on time of day.

Daily aggregates are only supported for stream types with two dimensions (i.e. a timestamp and a numeric value).

To calculate a daily aggregate, all data points within `n_days` from `start_time` are considered. Each data point is grouped together into intervals by rounding down the time portion of the timestamp to the nearest `resolution` seconds. Then, the mean is calculated from the data points in each interval to arrive at an aggregate value.

If no data points appeared within an interval for any of the days being aggregated, a `null` is returned for the value.

##### path Parameters

|     |     |
| --- | --- |
| stream_id<br>required | string<br>Example: a1379454321e63222f547d8f929a2451d0f295cfbd77fabf9ce8090e1c31c571<br>Unique ID for a Stream. |

##### query Parameters

|     |     |
| --- | --- |
| start_time<br>required | float<br>Unix timestamp (in seconds, with optional fraction) of the start of the<br>time range to query. This is an inclusive boundary (data points considered<br>in calculating averages will be `>= start_time`).<br>The `start_time` must be evenly divisible by `resolution`. |
| resolution<br>required | int \[ 60 .. 86400 \] <br>Length of time (in seconds) for the window used to calculate intervals.<br>Must evenly divide into a 24 hour period. |
| n_days<br>required | int \[ 1 .. 14 \] <br>Number of days across which to compute daily aggregate.<br>Each day is a 24 hour period beginning from the `start_time`. |

### Responses

**200**

Successful request

**400**

Bad request

**401**

Not authorized to perform the request

**404**

Not found

**500**

Internal server error

### Aggregate Window

Downsamples data into windows of `resolution` seconds and applies the specified `aggregate_function` to the data points that fall within each window.

Downsampled data is returned as a "time" array containing timestamps respresenting the the first timestamp (inclusive) of the window and a corresponding "aggregate_values" array where each item represents the aggregated value of all values that fell within that window.

#### path Parameters

|     |     |
| --- | --- |
| stream_id<br>required | string<br>Example: a1379454321e63222f547d8f929a2451d0f295cfbd77fabf9ce8090e1c31c571<br>Unique ID for a Stream. |

#### query Parameters

|     |     |
| --- | --- |
| start_time<br>required | any<br>Unix timestamp (in seconds, with optional fraction) of the start of the<br>time range to query. This is an inclusive boundary (data points considered<br>will be `>= start_time`).<br>The value will be automatically truncated to be evenly divisible by `resolution` seconds. |
| end_time<br>required | float<br>Unix timestamp (in seconds, with optional fraction) of the end of the<br>time range to query. This is an exclusive boundary (data points considered<br>will be `< end_time`).<br>The value will be automatically padded to be evenly divisible by `resolution` seconds. |
| resolution<br>required | int \[ 60 .. 604800 \] <br>Length of time (in seconds) for the window used to aggregate over.<br>Must evenly divide into a 24 hour period. |
| aggregate_function<br>required | string<br>Enum:"sum""mean"<br>Determines the aggregate function to apply to the values of data points within<br>each window. Possible values are:<br>- `sum`: The total of all values is calculated. Aggregate value will be `null` if<br>there were no data points in the window.<br>  <br>- `mean`: The mean of all values is calculated. Aggregate value will be `null` if there were no data points in the window. |
| timestamp | string<br>Default: "unix"<br>Enum:"unix""unixns""iso"<br>Determines how timestamps are formatted in the response:<br>- `unix` \- Unix seconds, with up to 7 fractional digit precision.<br>- `unixns` \- Unix nanoseconds (integer).<br>- `iso` \- Formatted as RFC3339, including the `timezone` offset. |
| timezone | integer<br>Default: 0<br>Example: timezone=-28800<br>The timezone offset, in seconds, used to calculate string-based<br>timestamp formats such as `datetime` and `iso`. For example,<br>PST (UTC-0800) is represented as `-28800`. If omitted, the<br>timezone is UTC. |
| timezone_name | string<br>Default: ""<br>Example: timezone_name=America/Los_Angeles<br>The name from the IANA timezone database used to calculate string-based<br>timestamp formats such as `datetime` and `iso`. For example,<br>`America/Los_Angeles`. While `timezone` uses a fixed offset,<br>`timezone_name` will return the correct UTC offset for a<br>given date/time in order to account for daylight saving time.<br>If omitted, the timezone is UTC unless a `timezone` parameter<br>is provided. If both a `timezone` and a `timezone_name` are provided<br>the request will return an error. If `timezone_name` is provided, aggregate<br>queries with resolution equivalent to multiples of a day (86400 seconds)<br>will group result windows by calendar day(s) in the provided timezone. |

### Responses

**200**

Successful request

**400**

Bad request

**401**

Not authorized to perform the request

**404**

Not found

**500**

Internal server error

## Multi Stream

### Batch Availability

Returns a boolean timeseries, indicating whether data exists in a given window of time for a batch of streams. The `resolution` parameter determines the granularity of this timeseries (i.e. the interval between each timestamp in the series). The `batch_operation` parameter determines whether data is considered available when data exists for all streams, or when data exists for any of the streams.
