# Introduction

The Raa Labs Time Series API provides endpoints to access time series measurements, metadata, and aggregated statistics. This reference outlines each endpoint and how to use them, including authentication requirements, query parameters, and example usage.

We recommend starting by reading through the [Authentication](/2025-10-31/getting-started/authentication) section, and then head over to the [Quickstart](/2025-10-31/getting-started/quickstart) guide and [Best Practices](/2025-10-31/getting-started/best-practices).


# Authentication

All API requests must be authenticated using OAuth 2.0 **Client Credentials** flow. You will need to obtain a **Client ID** and **Client Secret** to generate an access token from Raa Labs' authentication service. This access token (a JSON Web Token) encapsulates the permissions (scopes) for your client, determining which time series data you are allowed to access. Include the token in the header of every API request.

## API Credentials

**Client ID** and **Client Secret** are the credentials for your API client. After you obtain these, use them to request an access token. The token grants access to the time series data permitted for your client.

| Key               | Description                                             |
| ----------------- | ------------------------------------------------------- |
| **Client ID**     | The identifier for your client (use as `client_id`)     |
| **Client Secret** | The secret key for your client (use as `client_secret`) |

{% hint style="danger" %}
**Important:** Handle your **Client ID** and **Client Secret** with care. **Do not share or**\
**expose these credentials** in source code, public repositories, or unsecured locations. If you suspect that a Client Secret has been compromised, contact Raa Labs support immediately to regenerate it. Generating a new secret will invalidate the old one and block any requests made with the old secret. *(This action is irreversible.)*
{% endhint %}

### Obtaining API Credentials

To get a Client ID and Secret for the Raa Labs API, please contact our support team. You can request credentials by emailing <support@raalabs.com>. Raa Labs will provide the necessary credentials for your client.

### Getting an Access Token

Use the OAuth 2.0 Client Credentials grant to obtain an access token. This is done by making a POST request to the authentication endpoint with your **Client** **ID** and **Client Secret**. For example, using cURL:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://auth.raalabs.io/oauth2/token" \
  -u YOUR_CLIENT_ID:YOUR_CLIENT_SECRET \
  -d "grant_type=client_credentials"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

url = "https://auth.raalabs.io/oauth2/token"
response = requests.post(
    url,
    auth=("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
    data={"grant_type": "client_credentials"},
)
token = response.json()["access_token"]
```

{% endcode %}
{% endtab %}
{% endtabs %}

Replace `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` with the credentials provided to you. This request returns a JSON response containing an **access token** (JWT). The token will look like a long string of characters.

Once you have the token, include it in the **Authorization** header of all API requests:

| Header        | Value                      |
| ------------- | -------------------------- |
| Authorization | Bearer `YOUR_ACCESS_TOKEN` |

For example:\
`Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...`

### Refreshing Access Tokens

Access tokens have a limited lifetime (by default, **1 hour**). The expiration time is encoded within the token. After a token expires, you must request a new one by repeating the Client Credentials grant (i.e. call the `/oauth2/token` endpoint again with your Client ID and Secret). There is no separate "refresh token"; simply obtain a new access token when needed.


# Quick Start

Fetch a `Bearer` token by calling the token endpoint. In this example we assign the token to a variable for reuse within the shell session:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
TOKEN=$(curl "https://auth.raalabs.io/oauth2/token" \
  -X POST \
  -u YOUR_CLIENT_ID:YOUR_CLIENT_SECRET \
  -d "grant_type=client_credentials" | jq -r '.access_token')
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

url = "https://auth.raalabs.io/oauth2/token"
response = requests.post(
    url,
    auth=("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
    data={"grant_type": "client_credentials"},
)
TOKEN = response.json()["access_token"]
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now you can start to make requests to the metadata, measurements and statistics endpoints. For ease of use, you can also store your tenant name as a variable.

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
ENVIRONMENT=$YOUR_ENVIRONMENT
curl "https://portal.raalabs.io/$ENVIRONMENT/measurements/raalabs/*?last=10minutes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

ENVIRONMENT = "YOUR_ENVIRONMENT"
url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/*?last=10minutes"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Best Practices

## Reuse Access Tokens

Bearer tokens fetched from the token endpoint are valid for **60 minutes**. It is recommended to store the token and reuse it for as long as it is valid.

## Use a Version Header

Use a version header to target a specific API version in production scenarios. See more details on [Versioning](/2025-10-31/getting-started/versioning).

## Use specific values in query parameters

The API often allows one to use default values for query parameters. This is convenient when exploring the API using command line tools because it shortens the requests. However, this can lead to responses being dependent on the time when a query is run (e.g. querying the last hour of data returns different results when run at different points in time). It is therefore advised to explicitly set query parameters whenever possible.

## Avoid Concurrent Requests

It is recommended to avoid making a large number of concurrent requests, and instead spread requests evenly over time.

## Fewer, but Larger Requests

The API is designed to be flexible and supports handling a lot of different use cases in a single request. If possible, choose fewer but larger requests over many small requests.

## Request Only Required Data

Request data only for required tags and vessels. Especially requests to the `/measurements` endpoint can result in transfer of a large amount of data.

Using the POST method on the `/measurements` endpoint allows you to cherry-pick required tags. This results in less data transferred than using the GET method with the `*` wildcard at the tag level.

Examples on how to use the POST method can be found here: [POST /measurements](/2025-10-31/docs/api-endpoints/measurements-endpoint#selecting-multiple-time-series-in-one-request).


# Versioning

The Raa Labs Time Series API aims to be predictable and uses a versioning strategy to ensure stability and transparency for all users.

A new version is released when a breaking change occurs. When a new version is released, the previous version is supported for **six months** before deactivation. Raa Labs staff will notify you in advance.

Each API version comes with a migration guide explaining changes and upgrade suggestions. All current and past API versions can be found under [Versions](/2025-10-31/getting-started/changelog/versions) in the changelog.

## Breaking Changes

The following changes are considered breaking changes and trigger a new release:

* Removing or changing an HTTP route or method
* Changing authentication or authorization
* Removing or changing required input parameters
* Changes to the response format

All other additive changes are considered backwards compatible, and will be made available within the latest version only. These updates can be found under [Updates](/2025-10-31/getting-started/changelog/updates) in the changelog.

## Versioning Format

* **Header-based versioning**: API versions are specified using a custom HTTP header
* **Date-based identifiers**: Versions are identified by a date with format `YYYY-MM-DD` (e.g., `2025-10-31`).

The header takes the following form:

```json
"x-api-version": "2025-10-31"
```

Use the `x-api-version` header to target a specific API version. Our approach to this API is to make it easy and quick to use. The `x-api-version` header is therefore **optional**. If omitted, the API targets the default version, which is marked as `(default)` in the version list under [Versions](/2025-10-31/getting-started/changelog/versions).

{% hint style="info" %}
**Note:** The default version can change when older versions are depreceated. It is therefore recommended to use the `x-api-version` header in production scenarios, so that you know which version you are targeting.
{% endhint %}

## Beta Versions

We release versions marked as `Beta` versions. Beta versions are under development, and the implementation and response formats might change. Beta versions can be added and removed without prior notification, and should not be used in production environments.


# Changelog


# API Versions

{% updates format="full" %}
{% update date="2026-09-10" tags="new-release,stable" %}

## 2026-09-10

#### Updates

Use this version by specifying the header:

```json
"x-api-version": "2026-09-10"
```

This version was previously available as the beta version `2026-01-01-beta`. The two are identical, and `2026-01-01-beta` remains callable, so if you are already using the beta you only need to change the version header. Omitting the `x-api-version` header targets the default version, which is unchanged by this release.

This version includes several updates for all endpoints. Two of them address the same underlying fact: an `id` is not guaranteed to be unique within a domain, because some domains name groups of time series rather than every time series individually, so a single query can match several time series that share one `id`.

* **Metadata**: changed response format for `/metadata`, the response is now a JSON array with metadata objects, where the `id` is part of the metadata object. In earlier versions this is a JSON object with the `id` as the key, so time series sharing an `id` arrive as repeated keys in the same object. While the JSON standard does allow duplicate object keys, most parsers resolve them by keeping only the last occurrence, which drops the other time series without any error. With the array, every matched time series is its own element, so nothing is lost regardless of what parser or software library is used, and the number of elements tells you how many time series the query matched. Additionally, NDJSON has been introduced as a new response format for `/metadata`.
* **Measurements**: the response format for `/measurements` is unchanged from `2025-10-31`, each data point carries an `id`, a `timestamp` and a `value`.
* **Statistics**: changed response format for `/statistics`, which now follows more closely the response format of the `/measurements` endpoint. The response contains `timestamp` and `id` (like in `/measurements`), and an `aggregations` object containing all requested aggregations. The aggregation type is not part of the `id` anymore, and the default aggregation has been changed from `avg` to `mean` (`avg` is not defined when there is only 1 datapoint within a window, while the `mean` is equal to that datapoint). In earlier versions each result key is `{domainId}/{AggregationName}`, so time series sharing a `domainId` produce several objects with the same key and the same `timestamp`. Nothing in the response says which time series a value came from, and code that indexes the results by key and timestamp overwrites one value with another. This version gives two well defined outcomes instead:
  * By default, time series that resolve to the same `id` are merged into a single result per time window and `id`, covering all of their data points.
  * Query with `output_domain=id` to keep every time series separate. The `id` domain guarantees uniqueness, so each result carries its own unique `id` and every value can be traced back to one time series.

#### Migration guide

This guide walks you through the breaking changes in `2026-09-10` compared to `2025-10-31`. Requests and query parameters remain identical to the previous version, apart from one changed default aggregation, so this guide focuses on how to update parsing logic to the changed response formats (with Python code examples).

**Measurements endpoint**

The `/measurements` response format is identical in API versions `2025-10-31` and `2026-09-10`, in every response format including `parquet`, so your code does not need updating.

**Metadata endpoint** The `/metadata` response has undergone substantial changes. See below for a sample of how the new format looks like, and how to update parsing logic.

Version `2025-10-31` response:

```json
{
  "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/": {
    "name": "Longitude",
    "description": "SHIP POSITION (LONGITUDE)",
    "source": "FurunoVDR",
    "scale": 1.0,
    "uom": "deg",
    "mapsTo": {
      "jsmea": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/",
      "raalabs": "IMO1000003/GPS_LONG"
    },
    "rangeHigh": 180.0,
    "rangeLow": -180.0,
    "timeSeriesId": "11000001-0000-0000-0000-000000000000",
    "vesselImo": "1000003",
    "vesselName": "Flying Dutchman"
  }
}
```

Version `2026-09-10` response:

```json
[
  {
    "id": "IMO1234567/jsmea_mac/MainEngine/Cylinder9/ScavAir//Temp/",
    "timeSeriesId": "a8009573-5013-4d18-8e63-1ae2f91d6b80",
    "source": "Aconis",
    "dataProvider": "Raa Labs",
    "unitOfMeasure": "°C",
    "scale": 1.0,
    "vessel": {
      "name": "Flying Dutchman",
      "imo": "1234567"
    },
    "domains": {
      "jsmea": {
        "id": "IMO1234567/jsmea_mac/MainEngine/Cylinder9/ScavAir//Temp/",
        "namingRule": "jsmea_mac",
        "category": "MainEngine",
        "subcategory": "Cylinder9",
        "content": "ScavAir",
        "item": "Temp"
      },
      "raalabs": {
        "id": "IMO1234567/ME CylinderScavAirTemp_9",
        "shortName": "ME CylinderScavAirTemp_9"
      },
      "maker": {
        "id": "IMO1234567/Aconis/0517",
        "name": "0517",
        "description": "M/E CYL.#9 SCAV. AIR BOX FIRE",
        "rangeHigh": 200.0,
        "rangeLow": 0.0
      }
    }
  }
]
```

How to update your code:

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10"  # was "2025-10-31"
}

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/IMO1234567/*"
response = requests.get(url, headers=headers)

# Version 2025-10-31 — response is a dict with domainIds as keys
# note that standard json parsers drop duplicate keys (this is one of major issues fixed with the new api version)
metadata = response.json()  # dict
for tag_id, meta in metadata.items():
    name = meta["name"]
    description = meta["description"]
    unit = meta["uom"]
    scale = meta["scale"]
    source = meta["source"]
    time_series_id = meta["timeSeriesId"]
    vessel_imo = meta["vesselImo"]
    vessel_name = meta["vesselName"]
    range_high = meta["rangeHigh"]
    range_low = meta["rangeLow"]
    raalabs_id = meta["mapsTo"].get("raalabs")

# Version 2026-09-10 — response is a list of metadata objects

metadata = response.json()  # list
for meta in metadata:
    tag_id = meta["id"]                            # was the dict key
    unit = meta["unitOfMeasure"]                   # was "uom"
    scale = meta["scale"]                          # unchanged
    source = meta["source"]                        # unchanged
    time_series_id = meta["timeSeriesId"]          # unchanged
    data_provider = meta["dataProvider"]           # new in this version
    vessel_imo = meta["vessel"]["imo"]             # was "vesselImo"
    vessel_name = meta["vessel"]["name"]           # was "vesselName"
    raalabs_id = meta["domains"]["raalabs"]["id"]  # was mapsTo["raalabs"]
    maker = meta["domains"].get("maker", {})       # a domain is only present if the time series has a name in it
    name = maker.get("name")                       # was "name"
    description = maker.get("description")         # was "description"
    range_high = maker.get("rangeHigh")            # was "rangeHigh"
    range_low = maker.get("rangeLow")              # was "rangeLow"
```

A time series that has no name in a given domain has no entry for it in `domains`, so guard the reads that go through one.

If your code needs to look up metadata by identifier, build the dictionary yourself and key it by `timeSeriesId`. Keying it by `id` reintroduces the dropped duplicates that the array response fixes.

**Statistics endpoint**

Two changes affect your queries and their results before you get to the response shape:

* The default aggregation is `mean` instead of `avg`. A request that does not set the `aggregations` parameter now returns a plain mean of the values in the window, where it previously returned a time-weighted average. Request `aggregations=avg` explicitly to keep the old calculation.
* The aggregation names in the response are the lowercase names you requested, for example `mean` and `count`. In `2025-10-31` the names and the delimiter were determined by the queried domain, and appeared capitalized in the result keys (`.../Mean`, `.../Count`). Code that matches on `/Avg` or `/Mean`, or that splits a result key to recover the aggregation type, needs updating. See below:

Version `2025-10-31` response:

```json
[
  {
    "IMO1000001/ME ShaftPower/Avg": 13.12,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Max": 15.47,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Min": 10.85,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Count": 3600,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  }
]
```

Version `2026-09-10` response:

```json
[
  {
    "timestamp": "2025-09-10T09:00:00.000000Z",
    "id": "IMO1000001/ME ShaftPower",
    "aggregations": {
      "avg": 13.12,
      "max": 15.47,
      "min": 10.85,
      "count": 3600
    }
  }
]
```

How to update your code:

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10"  # was "2025-10-31"
}

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/IMO1234567/ME%20ShaftPower"
params = {"aggregations": "avg,max,min,count", "window": "1h", "last": "1day"}
response = requests.get(url, headers=headers, params=params)
data = response.json()

# Version 2025-10-31 — each object has a dynamic key like "{domainId}/{AggType}"
for entry in data:
    timestamp = entry["timestamp"]
    for key, value in entry.items():
        if key != "timestamp":
            # key is e.g. "IMO1234567/ME ShaftPower/Avg"
            # Split to extract the domainId and aggregation type
            *id_parts, agg_type = key.split("/")
            domain_id = "/".join(id_parts)
            print(f"{domain_id} {agg_type}={value} at {timestamp}")

# Version 2026-09-10 — structured fields with aggregations object
# there is no need for string splitting to get aggregation types
for entry in data:
    timestamp = entry["timestamp"]
    domain_id = entry["id"]
    for agg_type, value in entry["aggregations"].items():
        print(f"{domain_id} {agg_type}={value} at {timestamp}")
```

{% endupdate %}

{% update date="2025-11-03" tags="stable" %}

## 2025-10-31 (default)

Initial dated version. Target this version by using the following version header in your requests.

```json
"x-api-version": "2025-10-31"
```

This version is supported until 2027-03-10, six months after the release of `2026-09-10`, and is deactivated after that date. The migration guide in the `2026-09-10` entry above covers the changes you need to make in order to change to this version.

#### Migration guide

The functionality of this version is identical to the `default` version, which is targeted when no version header is specified. There are no changes to client code required when updating to version `2025-10-31` from the unversioned API.

### Unversioned API (default)

The unversioned API, this is currently the `default` version. This version is targeted when no `x-api-version` header is provided in the request.
{% endupdate %}
{% endupdates %}


# Updates

{% updates format="full" %}
{% update date="2026-09-10" tags="new-release,stable" %}

## Stable release of API version `2026-09-10`

* API version `2026-09-10` is now available. It is the stable release of the response formats that were developed under `2026-01-01-beta`, and the two versions are identical, so moving over from the beta only requires changing the `x-api-version` header.
* See the full release details and migration guide here: [2026-09-10](/2025-10-31/getting-started/changelog/versions#2026-09-10)
* `2026-01-01-beta` remains callable, and continues to return the same responses as `2026-09-10`.
* Omitting the `x-api-version` header targets the default version, which is unchanged by this release.
* Version `2025-10-31` is supported until 2027-03-10, six months from this release, and is deactivated after that date.
  {% endupdate %}

{% update date="2026-09-02" tags="beta,new-feature" %}

## Merged statistics for shared domain ids, and `timeSeriesId` removed in `2026-01-01-beta` version

* The `/statistics` endpoint now returns a single result per time window and `id` in API version `2026-01-01-beta`. Where several time series resolve to the same `id` (based on chosen `output_domain`), their aggregations are merged into one result covering the data points of all of those time series.
* The `domainId` field has been renamed to `id` in `/measurements` and `/statistics` responses in API version `2026-01-01-beta`, it contains the same values as before.
* The `timeSeriesId` field has been removed from the `/measurements` and `/statistics` responses in API version `2026-01-01-beta`, in every response format. Rows are identified by `id` alone, and the `/measurements` `parquet` schema now carries a single `id` column in place of `time_series_id` and `domain_id`, which makes it identical to the one in `2025-10-31`.
  {% endupdate %}

{% update date="2026-08-31" tags="new-feature" %}

## Output domain for measurements and statistics

* The `/measurements` and `/statistics` endpoints now accept an `output_domain` query parameter, which names the results in that domain instead of the domain that was queried. For example, query the `raalabs` domain with `output_domain=jsmea` to select time series by their flat Raa Labs tag name and get JSMEA names back. A time series that has no name in the requested output domain is left out of the response. This is available in both API versions `2025-10-31` and `2026-09-10`.
  {% endupdate %}

{% update date="2026-08-26" tags="new-feature" %}

## MCP server documentation

* The documentation now covers the Raa Labs MCP server, which exposes vessel time series data to AI agents over the Model Context Protocol. See the MCP Server page for how to connect a client.
  {% endupdate %}

{% update date="2026-08-06" tags="new-feature" %}

## Partial edge windows for statistics

* The `/statistics` endpoint now accepts a `window_edges` query parameter, which controls what happens to the windows at each end of the query time range. With `whole` (the default, and the previous behavior) the range is expanded to whole windows, and the leading partial window is dropped and the trailing window is returned in full. With `partial` the query range is used exactly as given, and the windows at each end are returned as partial windows. The parameter is ignored when `window = none`. This is available in both API versions `2025-10-31` and `2026-09-10`.
  {% endupdate %}

{% update date="2026-08-05" tags="bug-fix" %}

## Maker domain range values now consistently returned as floats

* In API version `2026-01-01-beta`, the `rangeLow` and `rangeHigh` fields of the `maker` domain in the `/metadata` response were returned as integers when the value had no decimal part, for example `200`. They are now consistently returned as floating point numbers, for example `200.0`, matching the behavior of API version `2025-10-31`.
  {% endupdate %}

{% update date="2026-05-18" tags="new-feature" %}

## NDJSON response format for metadata

* The `/metadata` endpoint now supports `ndjson` output, in addition to `json`. Use `format=ndjson` as query parameter, or set the `Accept: application/x-ndjson` header. This is available only in API version `2026-09-10`.
  {% endupdate %}

{% update date="2026-05-11" tags="bug-fix" %}

## Return status code `406` for unsupported response formats

* Requests to API endpoints with an unsupported response format now return a `406 Not Acceptable` error, instead of `500 Internal Server Error`. For example, requesting `format=html` on `/measurements`, or `format=csv` on `/metadata`, will now return a `406` response.
  {% endupdate %}

{% update date="2026-04-28" %}

## Statistics endpoint: fix and clarify window alignment behavior

* Fixed inconsistent behavior in the `/statistics` endpoint (API version `2025-10-31`) for boundary aggregation windows when windows were not aligned with the query range.
* Updated documentation to clarify the behavior.
  {% endupdate %}

{% update date="2026-04-16" tags="experimental-feature" %}

## CSV and HTML response formats for statistics

* The `/statistics` endpoint now supports `csv` and `html` output formats, in addition to `json` and `ndjson`. Use `format=csv` or `format=html` as query parameter. This feature is in experimental state.
  {% endupdate %}

{% update date="2026-04-08" %}

## Python code samples

* This documentation now includes Python code samples for all API endpoints and examples.
  {% endupdate %}

{% update date="2026-03-20" tags="new-feature" %}

## Delta aggregation support for statistics

* The `/statistics` endpoint now supports the `delta` aggregation, which computes the difference between the last and first value (`last_val - first_val`) within the aggregation window.
  {% endupdate %}

{% update date="2026-03-18" tags="new-feature,bug-fix" %}

## JSON data type aggregations, timestamp normalization, and trailing comma fix

* The `/statistics` endpoint now supports the following aggregations for the `JSON` data type:
  * `count`
  * `last_val`
  * `first_val`
  * `last_time`
  * `first_time`
* The `timestamp` field in API responses has been normalized to always include microsecond precision, sample `timestamp`: `2024-12-04T21:00:00.000000Z`
* Requests to `/statistics` using API version `2026-01-01-beta` occasionally returned trailing commas in the JSON response, this has been fixed.
  {% endupdate %}

{% update date="2026-03-16" %}

## Documentation host updated to `portal.raalabs.io`

* This API documentation has been updated to use `portal.raalabs.io` as host for all examples. `prism.raalabs.io` continues to be accessible.
  {% endupdate %}

{% update date="2026-02-20" tags="new-feature" %}

## Multi-parameter queries and arbitrary time window aggregation

* The `parameters` domain now supports querying multiple parameters with the `{}`-syntax in a single `GET` request.
* The `/statistics` HTTP endpoint now supports calculating aggregates over an arbitrary time window, returning one window with one value per aggregation across the whole time range requested.
  {% endupdate %}

{% update date="2026-01-22" tags="new-feature" %}

## `mapsTo` field now includes id domainIds

* The `mapsTo` field in the response to `/metadata` now contains the `id` domainIds.
  {% endupdate %}

{% update date="2026-01-15" tags="bug-fix" %}

## Trailing comma fix in measurements and statistics responses

* This update fixes a bug where in some cases `/measurements` and `/statistics` returned a trailing comma after the last JSON element within the array, which can cause errors in JSON parsing.
  {% endupdate %}

{% update date="2025-12-10" tags="bug-fix" %}

## Accept both aggregation and aggregations query parameter

* Both `aggregations` and `aggregation` are accepted as query parameter name for the `/statistics` endpoint. Previously, only `aggregations` was accepted, and using for example `aggregation=count` would not be recognised as the query parameter, and the API would return the default aggregation of `avg` instead.
  {% endupdate %}

{% update date="2025-12-09" tags="new-feature" %}

## Verbose domainIds query parameter

* All three endpoints `/metadata`, `/measurements` and `/statistics` now accept a `verbose` query parameter. This is a flag to toggle on verbose domainIds. Some domains offer more human-friendly, verbose domainIds; if `verbose` is set to `true`, the domainIds in the response will be verbose. Below is an example of a verbose versus regular domainId within the `vis-3-8a` domain:
  * regular: `IMO1234567/411.1/C101.31-6/meta/qty-temperature/cnt-coolant/pos-outlet`
  * verbose: `IMO1234567/411.1/C101.31-6/~propulsion.engine/~cylinder.6/meta/qty-temperature/cnt-coolant/pos-outlet`
    {% endupdate %}

{% update date="2025-11-19" tags="new-feature" %}

## VIS 3-8a domain support

* Added general support for the `vis-3-8a` domain. Queries can now be done using `vis-3-8a` domainIds, see the relevant section in the docs here: [vis 3-8a domain](/2025-10-31/docs/supported-domains#the-vis-3-8a-domain). Note that the availability of domainIds is subject to time series contextualization, which happens independent of the API development.
  {% endupdate %}

{% update date="2025-11-17" tags="bug-fix,new-feature" %}

## JSMEA POST improvements and Content-Type error handling

* `POST` requests for `/measurements` and `/statistics` using the JSMEA domain now support tags with empty hierarchy levels within the `POST` body.
* `POST` requests without the `Content-Type` header now return a more appropriate `Unsupported Media Type` error message. `application/json` is the only allowed value for the header.
  {% endupdate %}

{% update date="2025-11-06" tags="new-feature" %}

## `mapsTo` field now includes maker domainIds

* The `mapsTo` field in the response to `/metadata` now contains `maker` domainIds.
  {% endupdate %}
  {% endupdates %}


# Limitations

Raa Labs limits the number of REST API calls that can be made within a specific amount of time. This limit helps prevent abuse and denial-of-service attacks, and ensures that the API remains available for all users.

Contact your administrator for details about your environment.

## Rate Limits

The rate limit applies per IP address and is not tied to the client credentials used. If the limit is exceeded, the API returns a `429` status code.

## Concurrent Requests

The maximum allowed number of concurrent requests applies per client credentials, but is independent of the IP address used to make the request. If the maximum is exceeded, the API returns a `429` status code.


# API Endpoints

## Overview

The API is organized into three main categories of endpoints, each serving a different purpose:

* **Measurements (Time Series) Endpoints:** Retrieve actual time series data points (raw sensor measurements over time).
* **Metadata Endpoints:** Retrieve descriptive information (metadata) about time series tags (e.g. name, description, units, source of a sensor).
* **Statistics Endpoints:** Retrieve aggregated statistics (computed metrics) on time series data over specified time windows.

All endpoint URLs include a **domain** and an **expression** as path parameters, which together specify what data you are querying. The *domain* represents a context or naming schema (for example, a standard for tag names), and the *expression* is a query string identifying one or many time series within that domain.

### Common Headers

There are two common headers:

* the `Authorization` header is required for all endpoints
* the `x-api-version` header is optional, but it is strongly encouraged to use this header in production scenarios and specify what API version should be used. Read about versioning [here](/2025-10-31/getting-started/versioning).

### Common Path Parameters <a href="#common-path-parameters-domain-and-expression" id="common-path-parameters-domain-and-expression"></a>

Every endpoint path includes `{domain}/{expression}`:

* **`domain`:** The contextual domain or naming scheme for the data. For example: `jsmea` (Japan Ship Machinery and Equipment Association standard for ISO 19848), `raalabs` (a flat naming scheme used by Raa Labs), `id` (the raw UUID of a time series), etc. The domain determines how the API interprets the expression part.
* **`expression`:** A query string that identifies the hierarchy or path of the data requested within the chosen domain. This could be a full specific path to a tag or include wildcard characters (`*`) to match multiple items.

**Wildcard Support:** You can use `*` in expressions to match individual levels of the hierarchy (matching multiple hierarchy levels is not supported), allowing broad queries. This is similar to wildcards in MQTT topics. For example:

* **Hierarchical Path Example:** `IMO1234567/411.1/C101.61/S203/meta/qty-mass.flow.rate` – a full path locating a specific measurement in the hierarchy (for a given IMO number and subsystem).
* **Wildcard Example:** `IMO1234567/*/qty-mass.flow.rate` – uses `*` to match any value in the second level, returning all measurements ending in `qty-mass.flow.rate` for the vessel `IMO1234567`.

When you query data, the domain and expression together determine which time series are returned. You can retrieve a single tag's data or use wildcards to retrieve multiple related tags in one query.

All examples in this documentation use curl with the base URL: `https://portal.raalabs.io/{ENVIRONMENT}`. Replace `{ENVIRONMENT}` with your tenant name.


# Measurements Endpoint

The **Measurements** endpoint provides access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time series data.

The Measurements API supports both `GET` and `POST` methods for fetching data:

* `GET` is used to retrieve data for a given domain/expression directly via the URL path.

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/{domain}/{expression}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/{domain}/{expression}"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

* `POST` can be used to retrieve data for multiple queries in one request (you provide a list of domain/expression queries in the JSON body).

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/measurements/query" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2025-10-31" \
  -H "Content-Type: application/json" \
  -d '[
    "domain/expression",
    "domain/expression"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/query"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
    "Content-Type": "application/json",
}
data = [
    "domain/expression",
    "domain/expression",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Use the `GET` method for most queries. Use the `POST /measurements/query` if you need to **cherry-pick multiple specific time series** in one request (by providing an array of queries as shown above). Only `POST` requests support domainIds with empty hierarchy levels.

## Request <a href="#request" id="request"></a>

### Path Parameters

| Parameter    | Description                                                                                                                                                                                          |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `domain`     | The contextualization domain (e.g. `jsmea`, `raalabs`, `id`, etc.) defining the naming scheme.                                                                                                       |
| `expression` | The query expression within that domain to specify the desired time series. This can be a full path or include wildcards (`*`). In many domains, the expression begins with the vessel’s IMO number. |

### Query Parameters

All query parameters below are **optional**. They let you refine the time range and format of the data returned. If no time range is specified, the default is the last **1 hour** of data up to the current time.

* **Time Range Parameters:** You can specify the time window for data using any of the following:
  * `from` / `to` (string): Start and end timestamps in ISO 8601 format (e.g., `2025-06-12T07:00:00Z`).
  * `from_epoch` / `to_epoch` (integer): Start and end time as Unix epoch timestamps in seconds.
  * `since` (string): A relative time duration string (e.g., `1day`, `6hours`) indicating how far back from now to retrieve data. For example, `since=1day` returns data from 24 hours ago *until now*. Valid values are `minute(s)`, `hour(s)`, `day(s)`, `month(s)`.
  * `last` (string): Similar to `since`, but anchored to the last full period of time. For example, `last=1day` returns data for the last full day (yesterday), as opposed to the past 24 hours.
  * `year`, `month`, `day` (numbers): Specify a calendar year, month, or day to retrieve data for those specific periods. These can be combined (e.g., `year=2024&month=3` for March 2024, or add `day` for a specific date).
* **Format Parameters:**
  * `format` (string): Desired output format of the data. Options are:
    * `json` (default): JSON array response (see format details below).
    * `ndjson`: Newline-delimited JSON, suitable for streaming large results.
    * `parquet`: Apache Parquet binary format. (Set the `Accept` header to the corresponding MIME type if using this.)
* **Verbose Parameter:**
  * `verbose` (boolean): Some domains offer more human-readable, verbose domainIds. This parameter allows you to toggle verbose domainIds on and off. If the domain does not distinguish between verbose and regular IDs, this parameter does not have any effect.
    * `false` (default): response includes regular domainIds
    * `true`: response includes verbose domainIds
* **Output Domain Parameter:**
  * `output_domain` (string): Returns the `id` of each matched time series in this domain, instead of the domain that was queried. For example, query the `raalabs` domain with `output_domain=jsmea` to get JSMEA ids back. Defaults to the queried domain. A time series that has no name in the requested output domain is left out of the response, so a query can return fewer time series with `output_domain` set than without it. The `id` domain is defined for every time series, so `output_domain=id` never leaves anything out.

You can also specify the response format via the **HTTP Accept Header** instead of the `format` query param:

* For JSON: `Accept: application/json` (default if unspecified).
* For NDJSON: `Accept: application/x-ndjson`.
* For Parquet: `Accept: application/vnd.apache.parquet`.

## Response <a href="#response" id="response"></a>

If the request is valid and the domain/expression matches one or more time series, the API returns all data points for those time series within the requested time range. The structure of the response depends on the format requested. In all cases, each data point is associated with an **ID** (the time series identifier in the specified domain), a **timestamp**, and a **value**. The data points are ordered by timestamp (ascending from oldest to newest).

{% hint style="info" %}
**Note:** The `id` field in each data point will use the same domain naming scheme as your query. For example, if you query the `raalabs` domain, each `id` in the response will be in the Raa Labs naming format; if you query `jsmea`, the `id` will be in the JSMEA hierarchical format. Set the `output_domain` query parameter to get the `id` in a different domain than the one you queried.
{% endhint %}

### Formats

{% tabs %}
{% tab title="JSON" %}
For `format=json` (or default JSON response), the result is a JSON **array** of data point objects. Each object has the following fields:

* `id` – The identifier of the measurement in the domain you specified.
* `timestamp` – ISO 8601 timestamp of the measurement.
* `value` – The sensor reading value at that timestamp (numeric or JSON, depending on the data type).

Example JSON response:

```json
[
  {
    "id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/",
    "timestamp": "2025-06-12T07:42:03.361000Z",
    "value": 120.225685
  },
  {
    "id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Latitude/",
    "timestamp": "2025-06-12T07:42:03.361000Z",
    "value": 36.008892
  }
]

```

*(In this example, two different time series — Longitude and Latitude — returned data at the same timestamp.)*
{% endtab %}

{% tab title="NDJSON" %}
For `format=ndjson` (Newline-Delimited JSON), the response contains one JSON object per line, instead of a single array. This format is useful for streaming large datasets where each line can be processed independently. The content of each JSON object is the same as in the regular JSON format (with `id`, `timestamp`, `value` fields).

Example NDJSON response (two lines, each a separate JSON object):

```ndjson
{"id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/", "timestamp": "2025-06-12T07:42:03.361000Z", "value": 120.225685}
{"id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Latitude/", "timestamp": "2025-06-12T07:42:03.361000Z", "value": 36.008892}
```

{% endtab %}

{% tab title="Parquet" %}
For `format=parquet`, the response will be a binary **Parquet file** download. The Parquet schema includes the following columns for the time series data (each data point will populate one of the value columns depending on its type):

| Column Name    | Data Type | Description                                                                                          |
| -------------- | --------- | ---------------------------------------------------------------------------------------------------- |
| `timestamp`    | int64     | Timestamp of the data point in Unix epoch milliseconds.                                              |
| `id`           | string    | The time series identifier (path) within the specified domain.                                       |
| `long_value`   | int64     | If the data point value is an integer (64-bit), it will appear here. Otherwise null for this column. |
| `double_value` | float64   | If the data point value is a floating-point number, it will appear here. Otherwise null.             |
| `json_value`   | string    | If the data point value is a JSON string or non-numeric, it will appear here. Otherwise null.        |

Each row in the Parquet file represents one data point. **Only one** of `long_value`, `double_value`, or `json_value` will be populated for each row, depending on the data type of the measurement.

{% hint style="info" %}
**Note:** Use tools or libraries that support Apache Parquet to read the returned file. Parquet format is useful for efficient storage and transfer of large datasets.
{% endhint %}
{% endtab %}
{% endtabs %}

***

## Examples

Be sure to include the Authorization header with your access token in each request (omitted in examples for brevity).

### **Retrieve Recent Measurements (last 1 hour, all tags)**

Get all measurements for the last 1 hour using the `raalabs` domain (this will fetch the most recent hour of data for all available time series in the Raa Labs naming scheme):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/*?last=1hour" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/*?last=1hour"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This returns one hour of recent data points for all tags. The `*` wildcard in place of the IMO number means all vessels/data in the `raalabs` domain.

### **Query Multiple Specific Measurements (last 1 hour, fleet-wide)**

Retrieve specific measurements (speed through water, shaft power, and fuel mass flow) for all vessels over the last hour. Here we use the JSMEA domain with a POST query to select multiple expressions:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?last=1hour" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31" \
  -H "Content-Type: application/json" \
  -d '[
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?last=1hour"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
    "Content-Type": "application/json",
}
data = [
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In the JSON body, we provided three query expressions:

* `SpeedThroughWater` for all vessels (`*` in the IMO position),
* `Main Engine Shaft Power` for all vessels,
* `Main Engine Fuel Oil Mass Flow Rate` for all vessels.

Each of these will return the last hour of data for the matching time series across the fleet.

### **Measurements for a Specific Tag and Vessel (exact match)**

Retrieve **Mass Flow Meter** measurements from the Main Engine Fuel Oil Line for a specific vessel (IMO 1234567) over a given date range (March 1–10, 2025). This example shows two ways to query the same data using different domains:

JSMEA domain (hierarchical tag):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/\
IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate\
?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Raa Labs domain (flat tag, spaces in tag names must be URL-encoded using `%20`):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs\
/IMO1234567/ME%20FuelMassFlow\
?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/IMO1234567/ME%20FuelMassFlow?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Both requests will return the fuel mass flow rate measurements for vessel 1234567 in the specified date range. The first uses the JSMEA standard path; the second uses the Raa Labs short tag (`ME FuelMassFlow`).

### Using Wildcards in Measurements Query

Retrieve all **Mass Flow Rate** measurements for vessel IMO 1234567 over a date range, without specifying the exact sub-paths:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea\
/IMO1234567/*/*/MassFlowRate\
?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/IMO1234567/*/*/MassFlowRate?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, the wildcards (`*/*`) in the middle of the path will match any system and sub-system. This query will return any time series that end with `MassFlowRate` for the given vessel (for example, it could match fuel mass flow, if that is the naming, or any other "MassFlowRate" under different systems).

### **Fleet-Wide Query by Month**

Retrieve Main Engine fuel mass flow measurements for **all vessels in the fleet** for a specific month (March 2024):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea\
/*/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate\
?year=2024&month=03" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/*/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate?year=2024&month=03"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

By using `year` and `month` parameters along with wildcards for IMO and sub-components, this request fetches all Main Engine fuel mass flow data across the fleet for March 2024.

### Selecting Multiple Time Series in One Request

You can query multiple specific time series in one call using a POST request. For example, to fetch **Speed Through Water**, **Shaft Power**, and **Fuel Mass Flow** for the entire fleet (each for the last hour):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?since=1hour" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31" \
  -H "Content-Type: application/json" \
  -d '[
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?since=1hour"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
    "Content-Type": "application/json",
}
data = [
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This is similar to the earlier POST example, but using `since=1hour` (which also retrieves the last 1 hour of data). The response will intermix data from the three requested time series, each identified by its `id` in the output.


# Metadata Endpoint

The **Metadata** endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/{domain}/{expression}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/{domain}/{expression}"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Request

### Path Parameters

| Parameter    | Description                                                                                                                                                                                                        |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `domain`     | The contextual domain for the metadata query (e.g. `jsmea`, `raalabs`, `id`).                                                                                                                                      |
| `expression` | The query expression to filter which tags' metadata to retrieve. This can be a specific path or include wildcards (`*`) to get multiple tags. In many domains, the expression begins with the vessel’s IMO number. |

### Query Parameters

* **Verbose Parameter:**
  * `verbose` (boolean): Some domains offer more human-readable, verbose domainIds. This parameter allows you to toggle verbose domainIds on and off. If the domain does not distinguish between verbose and regular IDs, this parameter does not have any effect.
    * `false` (default): response includes regular domainIds
    * `true`: response includes verbose domainIds

## Response

If the query is valid, the response will include metadata for all matching time series in JSON format. The response structure is a **JSON object**, where each key is a time series ID (the domainId, according to the chosen domain) and the value is an object containing that tag's metadata fields. Each individual metadata object contains the following fields (keys can vary depending on the data source):

* `name` – Human-friendly name of the signal.
* `description` – Detailed description of the measurement.
* `source` – Source system or origin of the data (e.g., sensor or system name).
* `scale` – Scaling factor applied to the raw data (if any).
* `uom` – Unit of measure (abbreviation) for the values (e.g., `deg` for degrees, `kW` for kilowatts).
* `mapsTo` – Mappings of this tag in other domains (if available). This is an object where keys are domain names and values are the corresponding tag identifiers in those domains (e.g., mapping a JSMEA standard name to a Raa Labs short tag).
* `rangeHigh` / `rangeLow` – High and low range values (if defined) that the measurements are expected to lie within.
* `timeSeriesId` – A unique UUID for the time series (internal identifier).
* `vesselImo` – The IMO number of the vessel this data is associated with.
* `vesselName` – The name of the vessel.

{% hint style="info" %}
**Important:** Note that not all domains necessarily return unique domainIds. There could be multiple time series that map to the same domainId. In such a case the metadata endpoint returns metadata objects for different time series with the same domainId. This requires extra considerations when parsing the response with JSON libraries (the standard is that duplicate object keys are not allowed in JSON, most software libraries follow this standard).
{% endhint %}

### Formats

{% tabs %}
{% tab title="JSON" %}
Example JSON metadata response:

```json
{
  "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/": {
    "name": "Longitude",
    "description": "SHIP POSITION (LONGITUDE)",
    "source": "FurunoVDR",
    "scale": 1.0,
    "uom": "deg",
    "mapsTo": {
        "jsmea": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/",
        "raalabs": "IMO1000003/GPS_LONG"
    },
    "rangeHigh": 180.0,
    "rangeLow": -180.0,
    "timeSeriesId": "11000001-0000-0000-0000-000000000000",
    "vesselImo": "1000003",
    "vesselName": "Flying Dutchman"
  },
  "IMO1000003/jsmea_nav/PositioningSystem/GPS///Latitude/": {
    "name": "Latitude",
    "description": "SHIP POSITION (LATITUDE)",
    "source": "FurunoVDR",
    "scale": 1.0,
    "uom": "deg",
    "mapsTo": {
        "jsmea": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Latitude/",
        "raalabs": "IMO1000003/GPS_LAT"
    },
    "rangeHigh": 90.0,
    "rangeLow": -90.0,
    "timeSeriesId": "11000001-0000-0000-0000-000000000001",
    "vesselImo": "1000003",
    "vesselName": "Flying Dutchman"
  }
}
```

(This example shows two metadata entries for Longitude and Latitude tags of vessel Flying Dutchman. It includes mappings in both JSMEA and Raa Labs domains for each tag.)
{% endtab %}
{% endtabs %}

## Examples

### **Retrieve Metadata for a Single Vessel**

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/IMO1234567/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/IMO1234567/*"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This request returns a JSON array containing metadata objects for all time series associated with vessel IMO 1234567, using the Raa Labs domain naming convention. The trailing wildcard (\*) selects all tags for the vessel.

### **Retrieve Metadata for Entire Fleet**

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/*"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This request returns metadata for all accessible time series across all vessels, using the Raa Labs domain naming convention. This is achieved by using a wildcard (`*`) in the IMO position of the expression.

### **Filtered Metadata Query (e.g., main engine tags)**

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/jsmea/*/jsmea_mac/MainEngine/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/jsmea/*/jsmea_mac/MainEngine/*"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This request returns metadata for all Main Engine-related time series across all vessels, using the JSMEA domain naming convention. The wildcard in the IMO position selects all vessels, while the remaining expression path filters results to time series under the `jsmea_mac` engine and machinery naming rule with the `MainEngine` category.


# Statistics Endpoint

The **Statistics** endpoint provides **aggregated metrics** calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.

You can query one or multiple time series for statistics using `GET` requests:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/{domain}/{expression}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/{domain}/{expression}"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

...or `POST` requests:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/statistics/query" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2025-10-31" \
  -H "Content-Type: application/json" \
  -d '[
    "domain/expression",
    "domain/expression"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/query"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
    "Content-Type": "application/json",
}
data = [
    "domain/expression",
    "domain/expression",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

As with measurements, use `GET` for a single domain/expression query and the `POST /statistics/query` (with a JSON array in the body) to retrieve multiple series in one call. Only `POST` requests support domainIds with empty hierarchy levels.

## Request

### Path Parameters

| Parameter    | Description                                                                                                                                                                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `domain`     | The contextual domain for the query (e.g. `jsmea`, `raalabs`, `id`, `vis-3-8a`).                                                                                                                                                                        |
| `expression` | The domain-specific query expression used to select which time series to compute statistics for. This can be a specific path or include wildcards (`*`) to retrieve multiple tags. In many domains, the expression begins with the vessel’s IMO number. |

### Query Parameters

Like the measurements endpoint, the statistics query supports time range parameters and format selection. If a requested window doesn't both start and end in the past, the statistics for it are calculated using the available data, which may be incomplete.

It also introduces parameters to specify the type of aggregation and the window size for aggregation. If no time range is given, the default is `from = -1h` to `to = now` (last one hour). If no aggregation is specified, the default is `avg` (average). If no window is specified, the default window is **1 minute** (`1m`).

* **Time Range:** Use `from`, `to`, `from_epoch`, `to_epoch`, `since`, `last`, `year`, `month`, `day` as described in the Measurements section to define the time range of data over which statistics are computed.
* **Aggregation Functions:** `aggregations` (string) – A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. If this parameter is omitted, the API uses `avg` by default.
* **Window Size:** `window` (string) – The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.<br>

  Windows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\
  \
  Set `window = none` to disable windowing. A single aggregation covering the full query range is returned.<br>

  A window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:

  ```
  [#####] = time window returned by the API
  11:30                                   16:30
    ↓ 12:00   13:00   14:00   15:00   16:00 ↓
    ¦   |       |       |       |       |   ¦
    ¦    [#####] [#####] [#####] [#####] [#####]
  ```

  Set `window_edges = partial` to use the query range exactly as given instead.
* **Window Edges:** `window_edges` (string) – Controls what happens to the windows at each end of the query time range when the range does not align with the time windows. This parameter is ignored when `window = none`, which always uses the query range as given.
  * `whole` (default): the range is expanded to whole windows, so the leading partial window is dropped and the trailing window is returned in full — meaning it can include data from after `to`.
  * `partial`: the range is used exactly as given, and the windows at each end are returned as partial windows covering only the part that falls inside the query range.<br>

    A partial window aggregates less data than a whole one, so compare values across windows with care. The first window carries the exact query start as its `timestamp` when it is partial. All other windows are aligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:

    ```
    [#####] = whole time window
    [###..] = partial time window, including data from the beginning of the window
    [..###] = partial time window, including data at the end of the window

      11:30                                   16:30
        ↓ 12:00   13:00   14:00   15:00   16:00 ↓
        ¦   |       |       |       |       |   ¦
        ¦   [#####] [#####] [#####] [#####] [#####]   window_edges = whole
     [..###][#####] [#####] [#####] [#####] [###..]   window_edges = partial
    ```

    With `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00` and `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.
* **Format:** `format` – Output format: `json` (default), `ndjson`, `csv`, or `html`.
* **Verbose Parameter:**
  * `verbose` (boolean): Some domains offer more human-readable, verbose domainIds. This parameter allows you to toggle verbose domainIds on and off. If the domain does not distinguish between verbose and regular IDs, this parameter does not have any effect.
    * `false` (default): response includes regular domainIds
    * `true`: response includes verbose domainIds
* **Output Domain Parameter:**
  * `output_domain` (string): Returns the domainId part of the result keys in this domain, instead of the domain that was queried. For example, query the `raalabs` domain with `output_domain=jsmea` to get result keys built from JSMEA domainIds. Defaults to the queried domain. A time series that has no name in the requested output domain is left out of the response, so a query can return fewer time series with `output_domain` set than without it. The `id` domain is defined for every time series, so `output_domain=id` never leaves anything out.

#### Supported Aggregations

The following aggregation functions are available to use in the `aggregations` parameter:

| `min`        | Minimum value in the window                                                                                                                                                                                                                     |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max`        | Maximum value in the window                                                                                                                                                                                                                     |
| `mean`       | Mean value in the window (if there is exactly one value, `mean` returns that value)                                                                                                                                                             |
| `avg`        | Average (time-weighted) value in the window (if there is exactly one value, `avg` returns `null`)                                                                                                                                               |
| `count`      | Count of data points (sample count)                                                                                                                                                                                                             |
| `sum`        | Sum of all values                                                                                                                                                                                                                               |
| `stddev`     | Standard deviation of values                                                                                                                                                                                                                    |
| `var`        | Variance of values                                                                                                                                                                                                                              |
| `skewness`   | Statistical skewness of the values distribution                                                                                                                                                                                                 |
| `kurtosis`   | Statistical kurtosis of the values distribution                                                                                                                                                                                                 |
| `first_val`  | The first value in the time window                                                                                                                                                                                                              |
| `last_val`   | The last value in the time window                                                                                                                                                                                                               |
| `integral`   | Integral (area under the curve) over the window. The result is expressed in \[source unit] × seconds. If your source data is a rate (e.g., km/h), ensure you account for the conversion from hours to seconds to obtain the expected magnitude. |
| `first_time` | Timestamp of the first data point in the window                                                                                                                                                                                                 |
| `last_time`  | Timestamp of the last data point in the window                                                                                                                                                                                                  |
| `delta`      | Difference between the last and first value in the window (`last_val - first_val`). Useful for calculating the change in a measurement over the aggregation window.                                                                             |

You may combine multiple aggregations in one query by listing them separated with commas (e.g. `aggregations=avg,max,min,count`).

{% hint style="info" %}
**Note:** For time series with JSON data types, only a limited set of aggregations are available: `last_val`, `first_val`, `last_time`, `first_time`, and `count`. Other aggregations will return `null` for JSON data types.
{% endhint %}

## Response

If the request is valid, the API returns the computed statistics for each requested time series over the specified time range and window. The results are returned in time order, one object per time window, for each aggregation requested.

{% hint style="info" %}
**Note:** Similar to the measurements endpoint, the `id` portion of the result keys will use the domain format you queried, unless you set the `output_domain` query parameter.
{% endhint %}

### Formats

{% tabs %}
{% tab title="JSON" %}
For `format=json` (default), the response is a JSON **array** of objects. Each object represents the results of one time window and aggregation type. Within each object:

* There will be a field for the aggregation name requested, `{DomainID}/{AggregationName}`. The aggregation names and delimiter are determined by the domain that is requested. (Note: `DomainID` here represents the domainId format used in the response.)
* There will also be a `timestamp` field, which marks the timestamp for that window's result. The timestamp corresponds to the start of the aggregation window. (For example, if window=1h, a timestamp of `2025-09-10T09:00:00Z` represents the window from 09:00 to 10:00.)

Example JSON response (for a query that requested `mean`, `max`, `min`, and `count` aggregations):

```json
[
  {
    "IMO1000001/ME ShaftPower/Mean": 13.12,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Max": 15.47,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Min": 10.85,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Count": 3600,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Mean": 12.98,
    "timestamp": "2025-09-10T10:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Max": 14.92,
    "timestamp": "2025-09-10T10:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Min": 11.03,
    "timestamp": "2025-09-10T10:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Count": 3600,
    "timestamp": "2025-09-10T10:00:00.000000Z"
  }
]
```

(This example shows two 1-hour windows. Between 09:00 and 10:00 on Sept 10, 2025, the mean ShaftPower was 13.12, the max was 15.47, the min was 10.85, and there were 3600 data points. The next hour shows similar statistics with slightly different values.)

If multiple time series were matched by the query (e.g., using a wildcard expression), the results will be returned as individual JSON objects.
{% endtab %}

{% tab title="NDJSON" %}
For `format=ndjson`, the output is similar to the JSON format, but each aggregation object is written on a separate line (newline-delimited). This is helpful for streaming or incremental processing of large results.

Each line will be a JSON object containing one or more `{id/Aggregation}` fields and a `timestamp` field, identical in structure to the objects shown in the JSON example above.

Example NDJSON response:

```ndjson
{"IMO1000001/ME ShaftPower/Mean": 13.12, "timestamp": "2025-09-10T09:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Max": 15.47, "timestamp": "2025-09-10T09:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Min": 10.85, "timestamp": "2025-09-10T09:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Count": 3600, "timestamp": "2025-09-10T09:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Mean": 12.98, "timestamp": "2025-09-10T10:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Max": 14.92, "timestamp": "2025-09-10T10:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Min": 11.03, "timestamp": "2025-09-10T10:00:00.000000Z"}
{"IMO1000001/ME ShaftPower/Count": 3600, "timestamp": "2025-09-10T10:00:00.000000Z"}
```

{% endtab %}

{% tab title="CSV" %}
{% hint style="warning" %}
The CSV response format is an experimental feature, and is subject to change.
{% endhint %}

For `format=csv`, the response is returned as `text/csv` with a header row followed by data rows. Each row corresponds to one aggregation window timestamp, and columns represent the aggregated values for each time series.

Column names follow the pattern `{DomainID}/{AggregationName}`, matching the keys used in the JSON format. If multiple time series are matched, each gets its own set of columns. Columns are sorted alphabetically.

Example CSV response (for a query that requested `mean`, `max`, `min`, and `count` aggregations with a 1-hour window):

```csv
timestamp,IMO1000001/ME ShaftPower/Mean,IMO1000001/ME ShaftPower/Count,IMO1000001/ME ShaftPower/Max,IMO1000001/ME ShaftPower/Min
2025-09-10T09:00:00.000000Z,13.12,3600,15.47,10.85
2025-09-10T10:00:00.000000Z,12.98,3600,14.92,11.03
```

{% endtab %}

{% tab title="HTML" %}
{% hint style="warning" %}
The HTML response format is an experimental feature, and is subject to change.
{% endhint %}

For `format=html`, the response is returned as `text/html; charset=utf-8` containing an HTML `<table>` element. The structure mirrors the CSV format, with each row representing one aggregation window.

Example HTML response:

```html
<table>
<thead>
<tr><th>timestamp</th><th>IMO1000001/ME ShaftPower/Mean</th><th>IMO1000001/ME ShaftPower/Count</th><th>IMO1000001/ME ShaftPower/Max</th><th>IMO1000001/ME ShaftPower/Min</th></tr>
</thead>
<tbody>
<tr><td>2025-09-10T09:00:00.000000Z</td><td>13.12</td><td>3600</td><td>15.47</td><td>10.85</td></tr>
<tr><td>2025-09-10T10:00:00.000000Z</td><td>12.98</td><td>3600</td><td>14.92</td><td>11.03</td></tr>
</tbody>
</table>
```

{% endtab %}
{% endtabs %}

## Examples

### **Aggregated Statistics (average & max over time)**

Retrieve the **average and maximum** Main Engine Shaft Power for all vessels, aggregated in 1-hour windows, over the last day (note that spaces in tag names must be URL-encoded using `%20`):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/*/ME%20ShaftPower\
?aggregations=avg,max&window=1h&last=1day" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/*/ME%20ShaftPower?aggregations=avg,max&window=1h&last=1day"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this example:

* `raalabs/*/ME ShaftPower` targets the *ShaftPower* measurement for all vessels (`*` wildcard for IMO) in Raa Labs naming.
* `aggregations=avg,max` asks for Average and Maximum values.
* `window=1h` sets 1-hour aggregation windows.
* `last=1day` requests data for the last full day (24 hours).

The response will be a series of time-windowed results, each with an Avg and Max for that hour, along with a timestamp for the hour.

### **Statistics for a Specific Vessel (multiple metrics)**

Get the **mean** and **standard deviation** of fuel oil consumption (mass flow rate) for a specific vessel (IMO 1234567), calculated over daily windows for the last 30 days:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/jsmea\
/IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate\
?aggregations=mean,stddev&window=1d&last=30days" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/jsmea/IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate?aggregations=mean,stddev&window=1d&last=30days"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this example:

* The domain/expression targets the MassFlowRate in the Main Engine Fuel Oil Line for vessel 1234567 (using wildcards for any sub-levels under FuelOilLine).
* `aggregations=mean,stddev` will return the daily average and daily standard deviation of that measurement.
* `window=1d` uses a one-day window for each data point (each result represents one day’s stats).
* `last=30days` means the last 30 full days (approximately the previous month).

The output will list one JSON object per day and aggregation type, each containing an aggregation name, and a `timestamp` (likely the start of the day).

### **Fleet-Wide Comparative Statistics**

Compare **minimum and maximum engine temperatures across the fleet** with 15-minute aggregation windows for the past week:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs\
/*/ME*Temp*?aggregations=min,max&window=15m&since=7days" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/*/ME*Temp*?aggregations=min,max&window=15m&since=7days"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2025-10-31",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this example:

* The expression `raalabs/*/ME*Temp*` uses wildcards to match any Raa Labs tag that contains "ME" and "Temp" (e.g., it could match tags like "ME ExhaustTemp" or "ME CoolingTemp" depending on naming). This effectively tries to fetch engine temperature-related metrics for all vessels.
* `aggregations=min,max` will produce two values per window: the minimum and maximum temperature observed in each 15-minute interval.
* `window=15m` sets the aggregation interval to 15 minutes.
* `since=7days` retrieves data from one week ago up until now.

The result will show 15-minute snapshots of the lowest and highest recorded values among those temperature measurements, across the fleet, for the last week.


# Supported Domains

Raa Labs supports multiple **domains** for organizing and querying time series data. A domain defines how a time series is named and structured. Knowing the domains helps you form expressions for queries. The current supported domains include:

| Domain         | Description                                                                                                                                                                                                                   | Example DomainId                                              |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| **id**         | The raw time series UUID. This is a unique identifier assigned by Raa Labs for each signal. Use this for a direct lookup if you know the UUID.                                                                                | `62e23903-9db5-43cc-949d-78452ffb81bc`                        |
| **jsmea**      | JSMEA (Japan Ship Machinery and Equipment Association) naming standard, based on ISO 19848 for ship sensor data. This is a hierarchical naming scheme with multiple levels (e.g., system, subsystem, component, parameter).   | `IMO1234567/jsmea_mac/MainEngine/DrivingShaft/Output//Power/` |
| **raalabs**    | Raa Labs flat tag schema. A simpler, flattened naming convention that usually includes just the IMO number and a short descriptive tag.                                                                                       | `IMO1234567/ME ShaftPower`                                    |
| **parameters** | Parameter grouping schema. It includes the IMO number, a parameter group name, and a short name. This groups related signals under a category.                                                                                | `IMO1234567/ME Power/ME ShaftPower`                           |
| **maker**      | The name given to a tag by the maker of the machinery or equipment. Includes the maker name and a tag name.                                                                                                                   | `IMO1234567/Maker/12345`                                      |
| **vis-3-8a**   | The VIS (Vessel Information Structures) naming convention from DNV is a standardized way to uniquely identify vessel functions and onboard equipment. It uses a hierarchical structure based on GMOD (Generic Product Model). | `IMO1234567/913/S130.1-1/S121/sec/406i/H233/meta/qty-power`   |

{% hint style="info" %}
**Note:** For the `jsmea` and `vis-3-8a` domains, some signals may not yet have been mapped. If an expected signal is unavailable in these domains, use the `raalabs` domain. For specific requests, please contact us at <support@raalabs.com>.
{% endhint %}

## Querying Within Each Domain

This section describes the query expression structure for each supported domain. All domains support wildcard queries. Wildcards (`*`) can be used to match partial names or hierarchy levels, allowing you to retrieve data without specifying the full expression.

### The ID Domain

**Query Expression Format:**

`{timeseries_id}`

In this domain, the query expression consists solely of the UUID of the time series.

* `62e23903-9db5-43cc-949d-78452ffb81bc` Retrieves the time series with this exact UUID.
* `*` Retrieves all time series from *all vessels*

### The JSMEA Domain

**Query Expression Format:**

`{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}`

For a comprehensive explanation of the JSMEA naming scheme, refer to the [Description of Local ID Definitions (JSMEA Codebook)](https://www.jsmea.or.jp/ssap/topics/jsmea_codebook.html). In a hierarchical domain such as **`jsmea`**, wildcards may be applied at multiple levels:

* `IMO1234567/*/*/*/*/*/Power/` Retrieves all time series under vessel IMO1234567 whose final component is "Power" (regardless of which subsystem or component).
* `IMO1234567/jsmea_mac/MainEngine/*` Retrieves all Main Engine-related time series for vessel IMO1234567 (wildcard at the level below MainEngine to get everything under it).
* `IMO1234567/jsmea_mac/MainEngine/Fuel*` Retrieves all Main Engine with some kind of fuel component related time series for vessel IMO1234567 (wildcard at the level below MainEngine to get everything under it).

### The Raalabs Domain

**Query Expression Format:**

`{imo_number}/{tag_name}`

In a flat domain like **`raalabs`**, you can query multiple vessels or tags:

* `*/ME ShaftPower` Retrieves the **Main Engine ShaftPower** measurements for *all vessels* ( `*` in place of the IMO number matches every vessel).
* `raalabs/IMO1000002/ME* Shaft*` Retrieves all tags that start with ME and have the word Shaft in them.

### The Parameters Domain

**Query Expression Format:**

`{imo_number}/{parameter_group_name}`

A parameter represents a logical grouping of related time series, from one or more vessels. This enables analysis of specific operational topics (e.g., fuel consumption), either within a single vessel or across multiple vessels.

* `parameters/IMO1000001/AE Fuel Oil Consumption` Retrieves all time series in the AE Fuel Oil Consumption parameter group, for vessel IMO1000001.
* `parameters/{IMO1000001, IMO1000002}/AE Fuel Oil Consumption` Retrieves the same parameter group for multiple vessels.

### The Maker Domain

**Query Expression Format:**

`{imo_number}/{maker_name}{maker_tag_name}`

* `IMO1234567/Enamor/*` Retrieves all time series for vessel IMO1234567 originating from the Enamor system.
* `IMO1234567/Enamor/Water Depth` Retrieves the water depth time series for vessel IMO1234567 originating from the Enamor system.

### The VIS-3-8a Domain

**Query Expression Format:**

`{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}`

The **`vis-3-8a`** domain utilizes the hierarchical GMOD (Generic Product Model) framework to define VIS-paths for vessel items. DNV provides a comprehensive [learning tool](https://vista.dnv.com/learning-tool) for generating VIS-paths and metadata, along with detailed [naming rule documentation](https://docs.vista.dnv.com/docs/standards/dnv-naming-rule).

Wildcards (`*`) can be applied at multiple levels within VIS-paths, as well as at the metadata type and IMO number specifications:

* `*` Retrieves all time series from *all vessels*
* `IMO1234567/411.1` Retrieves all time series under vessel IMO1234567 with primary path root 411.1 ("Propulsion Driver") and arbitrary amounts of descendants in the VIS-path. The latter means that specifying {primary\_item\_vis\_path} = 411.1 is equal to specifying {primary\_item\_vis\_path} = 411.1/\*
* `IMO1234567/400a` Retrieves all time series under vessel IMO1234567 with primary path root being a code that lies within the group 400a ("Propulsion and steering arrangements") in the hierarchy tree, and arbitrary amounts of descendants in the VIS-path.
* `IMO1234567/*/S130-2AMOS` Retrieves all time series for vessel IMO1234567 with arbitrary primary path root and includes S130 (code for "fan unit") anywhere in the VIS-path of their primary item, except root position. Note that the code we require can be both exactly S130 or a code that is under it in the hierarchy, e.g. S130.2. In addition this code is required to have Location 2AMOS.
* `*/*/sec/*/H233` Retrieves all time series from *all vessels* with secondary item VIS-path including H233, and wildcard root.
* `IMO1234567/meta/qty-temperature` Retrieves all time series for vessel IMO1234567 which have the metadata element "qty-temperature".
* `IMO1234567/meta/qty-*` Retrieves all time series for vessel IMO1234567 which have the metadata category "qty" and any metadata type.

#### Notes

* Wildcards in primary or secondary VIS-paths match zero or more path segments, except at the root position where they match one or more segments to ensure a valid root is specified
* You can combine vessel, path, and meta wildcards: `*/400a/*/C663/sec/*/meta/qty-*`
* Use `{IMO1234567, IMO7654321}/*` to query multiple specific vessels

#### Verbose DomainIds

Add the query parameter `?verbose=true` to include human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. This optional parameter is disabled by default and is intended solely to improve readability of the output. Including verbose in the query expression will not affect the resolver behavior.

**Example (verbose=true):**

`IMO1234567/411.1-2P/C101.31-4/~propulsion.engine.2P/~cylinder.4/meta/qty-pressure`

In this example, the verbose segments (prefixed with \~) show that the time series represents the pressure of cylinder 4 in propulsion engine 2 on the port side. The verbose parameter is available across all endpoints and domains, but its use outside the vis-3-8a domain is only useful when querying metadata.

{% hint style="info" %}
**Note:** Wildcards are currently under development, not all functionality is available in all domains.
{% endhint %}

## Finding Domain Mappings

If you are unsure how a time series ID appears in different domains, you can use the metadata endpoint on the `id` domain to see its mappings. For example, you can fetch all metadata using the raw `id` domain and look at the `mapsTo` field in the response. The `mapsTo` object will show equivalent identifiers in other domains for each time series.

For instance, a metadata query like `GET https://portal.raalabs.io/{ENVIRONMENT}/metadata/id/*` might return:

```json
{
  "46fd4739-ad03-43c3-b027-7db0d0b093ec": {
    "name": "12153",
    "description": "ENGINE ROOM TEMP.",
    "source": "Aconis",
    "scale": 1.0,
    "vesselName": "Happy Wanderer",
    "vesselImo": "1000005",
    "uom": "°C",
    "timeSeriesId": "46fd4739-ad03-43c3-b027-7db0d0b093ec",
    "rangeLow": -40.0,
    "rangeHigh": 60.0,
    "mapsTo": {
      "jsmea": "IMO1000005/jsmea_mac/EngineRoomAmbience/RoomSpace/AmbientAir//Temp/",
      "maker": "IMO1000005/Aconis/12153",
      "raalabs": "IMO1000005/EngineRoom AmbientAirTemp",
      "vis-3-8a": "IMO1000005/406i/H233/meta/cnt-ambient.air/qty-temperature"
    }
  }
}
```

## Returning DomainIds in Another Domain

The `/measurements` and `/statistics` endpoints accept an `output_domain` query parameter. It sets the domain that the results are named in, independently of the domain that was queried. This lets you query in whichever naming scheme is most convenient, and get the results labelled in the scheme of your choice. Without the parameter, results are named in the domain that was queried.

For example, this query selects a time series by its flat `raalabs` tag name, but asks for JSMEA names in the response:

```sh
curl -X GET "https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/IMO1234567/ME%20ShaftPower?last=1hour&output_domain=jsmea" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2025-10-31"
```

It returns the same measurements as the query without `output_domain`, but each `domainId` is the JSMEA name of the time series instead of the `raalabs` one:

```json
[
  {
    "timestamp": "2025-06-12T07:42:03.361000Z",
    "id": "IMO1234567/jsmea_mac/MainEngine/DrivingShaft/Output//Power/",
    "value": 12351.4
  }
]
```

{% hint style="info" %}
**Note:** A time series that has no name in the requested output domain is left out of the response, so a query can return fewer time series with `output_domain` set than without it. The `id` domain is defined for every time series, so `output_domain=id` never leaves anything out.
{% endhint %}


# Response Codes and Error Handling

The API uses standard HTTP status codes for error reporting, along with a JSON error message in the response body to help diagnose issues.

* `2xx` response codes indicate success, and require no action from the user
* `4xx` response codes indicate a problem with the request, the user can resolve these problems with the help of the error message (see example below)
* `5xx` response codes indicate a problem with the service, and cannot be addressed by the user.

Common error responses include:

| HTTP Status Code               | Meaning                                                                                                                                          |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **200 OK**                     | The request was successful and the response contains the requested data.                                                                         |
| **400 Bad Request**            | The request was malformed or failed validation (e.g., missing required fields, invalid parameter format, etc.).                                  |
| **401 Unauthorized**           | Authentication failed – either the `Authorization` header is missing, or the Bearer token is invalid or expired.                                 |
| **403 Forbidden**              | The user is authorized, but does not have permission to access the requested content.                                                            |
| **404 Not Found**              | The requested resource does not exist. This could mean the endpoint is incorrect or the query is incorrect `{domain}/{expression}`               |
| **406 Not Acceptable**         | The server cannot produce a response matching the list of acceptable values defined in the request's headers (e.g., `Accept` header).            |
| **415 Unsupported Media Type** | Some endpoints expect a `Content-Type` header, see the API endpoints documentation for details.                                                  |
| **429 Too Many Requests**      | The user has sent too many requests in a given amount of time. Retry requests at a later time. This is configurable, contact your administrator. |
| **500 Internal Server Error**  | Internal server error. Please retry your request or contact support if the problem persists.                                                     |
| **503 Service Unavailable**    | The service is temporarily unavailable. Please retry your request after a short delay or contact support if the problem persists.                |

When an error occurs, the response body will typically include a JSON object with an `error` field describing what went wrong. For example:

```json
{
  "error": "Invalid 'to' timestamp: Invalid timestamp or format: 2025-05-25T09:41:00, valid formats are ISO8601 or YYYY-MM-DD"
}
```

In this example, the error message indicates that the `to` query parameter was not in an acceptable format. These messages can help you adjust your request accordingly.


# Examples

Below are some example use-cases and API requests.

{% hint style="info" %}
**Note:** In the example URLs, replace `{ENVIRONMENT}` with your target environment or customer identifier provided by Raa Labs.
{% endhint %}

A typical base URL is `https://portal.raalabs.io/{ENVIRONMENT}/...`. Be sure to include the Authorization header with your access token in each request (omitted in examples for brevity).

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Measurements Endpoint</strong></td><td>Example API requests for the Measurements Endpoint</td><td><a href="/2025-10-31/docs/api-endpoints/measurements-endpoint#examples">Measurements Endpoint</a></td></tr><tr><td><strong>Metadata Endpoint</strong></td><td>Example API requests for the Metadata Endpoint</td><td><a href="/2025-10-31/docs/api-endpoints/metadata-endpoint#examples">Metadata Endpoint</a></td></tr><tr><td><strong>Statistics Endpoint</strong></td><td>Example API requests for the Statistics Endpoint</td><td><a href="/2025-10-31/docs/api-endpoints/statistics-endpoint#examples">Statistics Endpoint</a></td></tr></tbody></table>


# API Reference


# Measurements

The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.

## GET /measurements/id/{ids}

> Get measurements from the id domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"IdTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"IdTimeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/measurements/id/{ids}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_id","parameters":[{"description":"A comma-separated list of one or more GUIDs","in":"path","name":"ids","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/IdTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/IdTimeseries"}}},"description":"All data points for the times series within the requested time range.","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get measurements from the id domain","tags":["Measurements"]}}}}
```

## GET /measurements/jsmea/{imo\_number}/{naming\_rule}/{category}/{sub\_category}/{content}/{position}/{item}/{modifier}

> Get measurement data from the jsmea domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"JsmeaTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"JsmeaTimeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/measurements/jsmea/{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_jsmea","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"The naming rule to use for retrieval","in":"path","name":"naming_rule","required":false,"schema":{"type":"string"}},{"description":"The category to retrieve from","in":"path","name":"category","required":false,"schema":{"type":"string"}},{"description":"The sub category to retrieve from","in":"path","name":"sub_category","required":false,"schema":{"type":"string"}},{"description":"The content to retrieve from","in":"path","name":"content","required":false,"schema":{"type":"string"}},{"description":"The position to retrieve from","in":"path","name":"position","required":false,"schema":{"type":"string"}},{"description":"The item to retrieve from","in":"path","name":"item","required":false,"schema":{"type":"string"}},{"description":"The modifier to retrieve from","in":"path","name":"modifier","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsmeaTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/JsmeaTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/JsmeaTimeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get measurement data from the jsmea domain","tags":["Measurements"]}}}}
```

## Retrieve data from multiple queries

> Used to retrieve data for multiple queries in one request (you provide a list of domain/expression queries in the JSON body).

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"MeasurementBatchQueryRequest":{"description":"Array of domain/expression query strings","items":{"type":"string"},"minItems":1,"title":"MeasurementBatchQueryRequest","type":"array"},"MeasurementBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"MeasurementBatchQueryResponse","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/measurements/query":{"post":{"callbacks":{},"deprecated":false,"description":"Used to retrieve data for multiple queries in one request (you provide a list of domain/expression queries in the JSON body).","operationId":"get_measurements_post","parameters":[{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryRequest"}}},"description":"Batch measurement query request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryResponse"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryResponse"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryResponse"}}},"description":"Batch measurement data","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Retrieve data from multiple queries","tags":["Measurements"]}}}}
```

## GET /measurements/raalabs/{imo\_number}/{tag\_name}

> Get measurement data from the raalabs domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"RaalabsTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"RaalabsTimeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/measurements/raalabs/{imo_number}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_raalabs","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Short descriptive tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaalabsTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/RaalabsTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/RaalabsTimeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get measurement data from the raalabs domain","tags":["Measurements"]}}}}
```

## GET /measurements/vis-3-8a/{imo\_number}/{primary\_item\_vis\_path}/sec/{secondary\_item\_vis\_path}/meta/{metadata}

> Get measurement data from the vis-3-8a domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"Vis38Timeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"Vis38Timeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/measurements/vis-3-8a/{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_vis38","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Primary item VIS path","in":"path","name":"primary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Secondary item VIS path","in":"path","name":"secondary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Metadata","in":"path","name":"metadata","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vis38Timeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/Vis38Timeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/Vis38Timeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get measurement data from the vis-3-8a domain","tags":["Measurements"]}}}}
```


# Metadata

The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.

## GET /metadata/id/{ids}

> Get metadata from the id domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"IDMetadata":{"allOf":[{"$ref":"#/components/schemas/CommonMetadata"}],"description":"Metadata from the Raalabs domain","title":"IDMetadata","type":"object"},"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/metadata/id/{ids}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_id","parameters":[{"description":"A comma-separated list of one or more GUIDs","in":"path","name":"ids","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IDMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get metadata from the id domain","tags":["Metadata"]}}}}
```

## Get metadata from the jsmea domain

> Get metadata from the jsmea domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"JsmeaMetadata":{"allOf":[{"$ref":"#/components/schemas/CommonMetadata"}],"title":"JsmeaMetadata","type":"object"},"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/metadata/jsmea/{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}":{"get":{"callbacks":{},"deprecated":false,"description":"Get metadata from the jsmea domain","operationId":"get_metadata_jsmea","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"The naming rule to use for retrieval","in":"path","name":"naming_rule","required":false,"schema":{"type":"string"}},{"description":"The category to retrieve from","in":"path","name":"category","required":false,"schema":{"type":"string"}},{"description":"The sub category to retrieve from","in":"path","name":"sub_category","required":false,"schema":{"type":"string"}},{"description":"The content to retrieve from","in":"path","name":"content","required":false,"schema":{"type":"string"}},{"description":"The position to retrieve from","in":"path","name":"position","required":false,"schema":{"type":"string"}},{"description":"The item to retrieve from","in":"path","name":"item","required":false,"schema":{"type":"string"}},{"description":"The modifier to retrieve from","in":"path","name":"modifier","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsmeaMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get metadata from the jsmea domain","tags":["Metadata"]}}}}
```

## GET /metadata/raalabs/{imo\_number}/{tag\_name}

> Get metadata from the raalabs domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"RaalabsMetadata":{"allOf":[{"$ref":"#/components/schemas/CommonMetadata"}],"description":"Metadata from the Raalabs domain","title":"RaalabsMetadata","type":"object"},"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/metadata/raalabs/{imo_number}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_raalabs","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Short descriptive tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaalabsMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get metadata from the raalabs domain","tags":["Metadata"]}}}}
```

## GET /metadata/vis-3-8a/{imo\_number}/{primary\_item\_vis\_path}/sec/{secondary\_item\_vis\_path}/meta/{metadata}

> Get metadata from the vis-3-8a domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"Vis38Metadata":{"items":{"properties":{"id":{"format":"uuid","type":"string"},"imo":{"type":"integer"},"vis_path":{"type":"string"}},"type":"object"},"title":"Vis38Metadata","type":"array"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/metadata/vis-3-8a/{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_vis38","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Primary item VIS path","in":"path","name":"primary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Secondary item VIS path","in":"path","name":"secondary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Metadata","in":"path","name":"metadata","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vis38Metadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get metadata from the vis-3-8a domain","tags":["Metadata"]}}}}
```


# Statistics

The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.

## GET /statistics/id/{ids}

> Get statistics from the id domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"IdStatistics":{"description":"Statistics response from the ID domain","items":{"$ref":"#/components/schemas/Statistics"},"title":"IdStatistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/statistics/id/{ids}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_id","parameters":[{"description":"A comma-separated list of one or more GUIDs","in":"path","name":"ids","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/IdStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get statistics from the id domain","tags":["Statistics"]}}}}
```

## GET /statistics/jsmea/{imo\_number}/{naming\_rule}/{category}/{sub\_category}/{content}/{position}/{item}/{modifier}

> Get statistics from the jsmea domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"JsmeaStatistics":{"description":"Statistics response from the JSMEA domain. If multiple time series were matched by the query (e.g., using a wildcard expression), each aggregation result object may contain multiple different {id/Aggregation} fields, one for each series.","items":{"$ref":"#/components/schemas/Statistics"},"title":"JsmeaStatistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/statistics/jsmea/{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_jsmea","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"The naming rule to use for retrieval","in":"path","name":"naming_rule","required":false,"schema":{"type":"string"}},{"description":"The category to retrieve from","in":"path","name":"category","required":false,"schema":{"type":"string"}},{"description":"The sub category to retrieve from","in":"path","name":"sub_category","required":false,"schema":{"type":"string"}},{"description":"The content to retrieve from","in":"path","name":"content","required":false,"schema":{"type":"string"}},{"description":"The position to retrieve from","in":"path","name":"position","required":false,"schema":{"type":"string"}},{"description":"The item to retrieve from","in":"path","name":"item","required":false,"schema":{"type":"string"}},{"description":"The modifier to retrieve from","in":"path","name":"modifier","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g., 1m (1 minute), 2h (2 hours), 2d (2 days), etc. All data within each window will be used to calculate the aggregations.","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsmeaStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/JsmeaStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get statistics from the jsmea domain","tags":["Statistics"]}}}}
```

## Retrieve multiple series

> Used to retrieve multiple series in one request (you provide a list of domain/expression queries in the JSON body).

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"StatisticsBatchQueryRequest":{"description":"","items":{"type":"string"},"minItems":1,"title":"StatisticsBatchQueryRequest","type":"array"},"StatisticsBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"StatisticsBatchQueryResponse","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/statistics/query":{"post":{"callbacks":{},"deprecated":false,"description":"Used to retrieve multiple series in one request (you provide a list of domain/expression queries in the JSON body).","operationId":"get_statistics_post","parameters":[{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g., 1m (1 minute), 2h (2 hours), 2d (2 days), etc. All data within each window will be used to calculate the aggregations.","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatisticsBatchQueryRequest"}}},"description":"Batch statistics query request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatisticsBatchQueryResponse"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/StatisticsBatchQueryResponse"}}},"description":"Batch statistics data","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Retrieve multiple series","tags":["Statistics"]}}}}
```

## GET /statistics/raalabs/{imo\_number}/{tag\_name}

> Get statistics from the raalabs domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"RaalabsStatistics":{"description":"Statistics response from the Raalabs domain. If multiple time series were matched by the query (e.g., using a wildcard expression), each aggregation result object may contain multiple different {id/Aggregation} fields, one for each series.","items":{"$ref":"#/components/schemas/Statistics"},"title":"RaalabsStatistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/statistics/raalabs/{imo_number}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_raalabs","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Short descriptive tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g., 1m (1 minute), 2h (2 hours), 2d (2 days), etc. All data within each window will be used to calculate the aggregations.","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaalabsStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/RaalabsStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get statistics from the raalabs domain","tags":["Statistics"]}}}}
```

## GET /statistics/vis-3-8a/{imo\_number}/{primary\_item\_vis\_path}/sec/{secondary\_item\_vis\_path}/meta/{metadata}

> Get statistics from the vis-3-8a domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"http://localhost:8080","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"Vis38Statistics":{"description":"Statistics response from the Vis38 domain. If multiple time series were matched by the query (e.g., using a wildcard expression), each aggregation result object may contain multiple different {id/Aggregation} fields, one for each series.","items":{"$ref":"#/components/schemas/Statistics"},"title":"Vis38Statistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"},"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}},"paths":{"/statistics/vis-3-8a/{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_vis38","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Primary item VIS path","in":"path","name":"primary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Secondary item VIS path","in":"path","name":"secondary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Metadata","in":"path","name":"metadata","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g., 1m (1 minute), 2h (2 hours), 2d (2 days), etc. All data within each window will be used to calculate the aggregations.","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vis38Statistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/Vis38Statistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get statistics from the vis-3-8a domain","tags":["Statistics"]}}}}
```


# Models

## The BadRequestResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"}}}}
```

## The CommonMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"}}}}
```

## The IDMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"IDMetadata":{"allOf":[{"$ref":"#/components/schemas/CommonMetadata"}],"description":"Metadata from the Raalabs domain","title":"IDMetadata","type":"object"},"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"}}}}
```

## The IdStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"IdStatistics":{"description":"Statistics response from the ID domain","items":{"$ref":"#/components/schemas/Statistics"},"title":"IdStatistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The IdTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"IdTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"IdTimeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The InternalServerErrorResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"InternalServerErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"InternalServerErrorResponse","type":"object"}}}}
```

## The JsmeaMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"JsmeaMetadata":{"allOf":[{"$ref":"#/components/schemas/CommonMetadata"}],"title":"JsmeaMetadata","type":"object"},"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"}}}}
```

## The JsmeaStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"JsmeaStatistics":{"description":"Statistics response from the JSMEA domain. If multiple time series were matched by the query (e.g., using a wildcard expression), each aggregation result object may contain multiple different {id/Aggregation} fields, one for each series.","items":{"$ref":"#/components/schemas/Statistics"},"title":"JsmeaStatistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The JsmeaTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"JsmeaTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"JsmeaTimeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The Measurement object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The MeasurementBatchQueryRequest object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"MeasurementBatchQueryRequest":{"description":"Array of domain/expression query strings","items":{"type":"string"},"minItems":1,"title":"MeasurementBatchQueryRequest","type":"array"}}}}
```

## The MeasurementBatchQueryResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"MeasurementBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"MeasurementBatchQueryResponse","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The NotFoundErrorresponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}}}
```

## The RaalabsMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"RaalabsMetadata":{"allOf":[{"$ref":"#/components/schemas/CommonMetadata"}],"description":"Metadata from the Raalabs domain","title":"RaalabsMetadata","type":"object"},"CommonMetadata":{"description":"Properties common to all Metadata types","properties":{"description":{"type":"string"},"mapsTo":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"rangeHigh":{"type":"integer"},"rangeLow":{"type":"integer"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"uom":{"type":"string"},"vesselImo":{"type":"string"},"vesselName":{"type":"string"}},"title":"CommonMetadata","type":"object"}}}}
```

## The RaalabsStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"RaalabsStatistics":{"description":"Statistics response from the Raalabs domain. If multiple time series were matched by the query (e.g., using a wildcard expression), each aggregation result object may contain multiple different {id/Aggregation} fields, one for each series.","items":{"$ref":"#/components/schemas/Statistics"},"title":"RaalabsStatistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The RaalabsTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"RaalabsTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"RaalabsTimeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The Statistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The StatisticsBatchQueryRequest object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"StatisticsBatchQueryRequest":{"description":"","items":{"type":"string"},"minItems":1,"title":"StatisticsBatchQueryRequest","type":"array"}}}}
```

## The StatisticsBatchQueryResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"StatisticsBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"StatisticsBatchQueryResponse","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The UnauthorizedErrorResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"}}}}
```

## The Vis38Metadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"Vis38Metadata":{"items":{"properties":{"id":{"format":"uuid","type":"string"},"imo":{"type":"integer"},"vis_path":{"type":"string"}},"type":"object"},"title":"Vis38Metadata","type":"array"}}}}
```

## The Vis38Statistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"Vis38Statistics":{"description":"Statistics response from the Vis38 domain. If multiple time series were matched by the query (e.g., using a wildcard expression), each aggregation result object may contain multiple different {id/Aggregation} fields, one for each series.","items":{"$ref":"#/components/schemas/Statistics"},"title":"Vis38Statistics","type":"array"},"Statistics":{"additionalProperties":{"description":"Property containing the aggregated value","oneOf":[{"type":"number"},{"format":"date-time","type":"string"}]},"properties":{"timestamp":{"description":"The timestamp for this aggregation window","format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The Vis38Timeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2025-10-31"},"components":{"schemas":{"Vis38Timeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"Vis38Timeseries","type":"array"},"Measurement":{"properties":{"id":{"format":"uuid","type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```


# Support

Get support for functionality or technical questions related to the API by contacting Raa Labs at <support@raalabs.com>.


# Introduction

The Raa Labs Time Series API provides endpoints to access time series measurements, metadata, and aggregated statistics. This reference outlines each endpoint and how to use them, including authentication requirements, query parameters, and example usage.

You are reading the documentation for API version `2026-09-10`. If you are upgrading from version `2025-10-31` you can find a migration guide and key updates [here](/getting-started/changelog/versions#2026-09-10).

We recommend to start by reading through the [Authentication](/getting-started/authentication) section, and then head over to the [Quickstart](/getting-started/quickstart) guide and [Best Practices](/getting-started/best-practices).


# Authentication

All API requests must be authenticated using OAuth 2.0 **Client Credentials** flow. You will need to obtain a **Client ID** and **Client Secret** to generate an access token from Raa Labs' authentication service. This access token (a JSON Web Token) encapsulates the permissions (scopes) for your client, determining which time series data you are allowed to access. Include the token in the header of every API request.

## API Credentials

**Client ID** and **Client Secret** are the credentials for your API client. After you obtain these, use them to request an access token. The token grants access to the time series data permitted for your client.

| Key               | Description                                             |
| ----------------- | ------------------------------------------------------- |
| **Client ID**     | The identifier for your client (use as `client_id`)     |
| **Client Secret** | The secret key for your client (use as `client_secret`) |

{% hint style="danger" %}
**Important:** Handle your **Client ID** and **Client Secret** with care. **Do not share or**\
**expose these credentials** in source code, public repositories, or unsecured locations. If you suspect that a Client Secret has been compromised, contact Raa Labs support immediately to regenerate it. Generating a new secret will invalidate the old one and block any requests made with the old secret. *(This action is irreversible.)*
{% endhint %}

### Obtaining API Credentials

To get a Client ID and Secret for the Raa Labs API, please contact our support team. You can request credentials by emailing <support@raalabs.com>. Raa Labs will provide the necessary credentials for your client.

### Getting an Access Token

Use the OAuth 2.0 Client Credentials grant to obtain an access token. This is done by making a POST request to the authentication endpoint with your **Client** **ID** and **Client Secret**. For example, using cURL:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://auth.raalabs.io/oauth2/token" \
  -u YOUR_CLIENT_ID:YOUR_CLIENT_SECRET \
  -d "grant_type=client_credentials"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

url = "https://auth.raalabs.io/oauth2/token"
response = requests.post(
    url,
    auth=("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
    data={"grant_type": "client_credentials"},
)
token = response.json()["access_token"]
```

{% endcode %}
{% endtab %}
{% endtabs %}

Replace `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` with the credentials provided to you. This request returns a JSON response containing an **access token** (JWT). The token will look like a long string of characters.

Once you have the token, include it in the **Authorization** header of all API requests:

| Header        | Value                      |
| ------------- | -------------------------- |
| Authorization | Bearer `YOUR_ACCESS_TOKEN` |

For example:\
`Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...`

### Refreshing Access Tokens

Access tokens have a limited lifetime (by default, **1 hour**). The expiration time is encoded within the token. After a token expires, you must request a new one by repeating the Client Credentials grant (i.e. call the `/oauth2/token` endpoint again with your Client ID and Secret). There is no separate "refresh token"; simply obtain a new access token when needed.


# Quick Start

Fetch a `Bearer` token by calling the token endpoint. In this example we assign the token to a variable for reuse within the shell session:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
TOKEN=$(curl "https://auth.raalabs.io/oauth2/token" \
  -X POST \
  -u YOUR_CLIENT_ID:YOUR_CLIENT_SECRET \
  -d "grant_type=client_credentials" | jq -r '.access_token')
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

url = "https://auth.raalabs.io/oauth2/token"
response = requests.post(
    url,
    auth=("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
    data={"grant_type": "client_credentials"},
)
TOKEN = response.json()["access_token"]
```

{% endcode %}
{% endtab %}
{% endtabs %}

Now you can start to make requests to the metadata, measurements and statistics endpoints. For ease of use, you can also store your tenant name as a variable.

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
ENVIRONMENT=$YOUR_ENVIRONMENT
curl "https://portal.raalabs.io/$ENVIRONMENT/measurements/raalabs/*?last=10minutes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

ENVIRONMENT = "YOUR_ENVIRONMENT"
url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/*?last=10minutes"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Best Practices

## Reuse Access Tokens

Bearer tokens fetched from the token endpoint are valid for **60 minutes**. It is recommended to store the token and reuse it for as long as it is valid.

## Use a Version Header

Use a version header to target a specific API version in production scenarios. See more details on [Versioning](/2025-10-31/getting-started/versioning).

## Use specific values in query parameters

The API often allows one to use default values for query parameters. This is convenient when exploring the API using command line tools because it shortens the requests. However, this can lead to responses being dependent on the time when a query is run (e.g. querying the last hour of data returns different results when run at different points in time). It is therefore advised to explicitly set query parameters whenever possible.

## Avoid Concurrent Requests

It is recommended to avoid making a large number of concurrent requests, and instead spread requests evenly over time.

## Fewer, but Larger Requests

The API is designed to be flexible and supports handling a lot of different use cases in a single request. If possible, choose fewer but larger requests over many small requests.

## Request Only Required Data

Request data only for required tags and vessels. Especially requests to the `/measurements` endpoint can result in transfer of a large amount of data.

Using the POST method on the `/measurements` endpoint allows you to cherry-pick required tags. This results in less data transferred than using the GET method with the `*` wildcard at the tag level.

Examples on how to use the POST method can be found here: [POST /measurements](/2025-10-31/docs/api-endpoints/measurements-endpoint#selecting-multiple-time-series-in-one-request).


# Versioning

The Raa Labs Time Series API aims to be predictable and uses a versioning strategy to ensure stability and transparency for all users.

A new version is released when a breaking change occurs. When a new version is released, the previous version is supported for **six months** before deactivation. Raa Labs staff will notify you in advance.

Each API version comes with a migration guide explaining changes and upgrade suggestions. All current and past API versions can be found under [Versions](/2025-10-31/getting-started/changelog/versions) in the changelog.

## Breaking Changes

The following changes are considered breaking changes and trigger a new release:

* Removing or changing an HTTP route or method
* Changing authentication or authorization
* Removing or changing required input parameters
* Changes to the response format

All other additive changes are considered backwards compatible, and will be made available within the latest version only. These updates can be found under [Updates](/2025-10-31/getting-started/changelog/updates) in the changelog.

## Versioning Format

* **Header-based versioning**: API versions are specified using a custom HTTP header
* **Date-based identifiers**: Versions are identified by a date with format `YYYY-MM-DD` (e.g., `2025-10-31`).

The header takes the following form:

```json
"x-api-version": "2025-10-31"
```

Use the `x-api-version` header to target a specific API version. Our approach to this API is to make it easy and quick to use. The `x-api-version` header is therefore **optional**. If omitted, the API targets the default version, which is marked as `(default)` in the version list under [Versions](/2025-10-31/getting-started/changelog/versions).

{% hint style="info" %}
**Note:** The default version can change when older versions are depreceated. It is therefore recommended to use the `x-api-version` header in production scenarios, so that you know which version you are targeting.
{% endhint %}

## Beta Versions

We release versions marked as `Beta` versions. Beta versions are under development, and the implementation and response formats might change. Beta versions can be added and removed without prior notification, and should not be used in production environments.


# Changelog


# API Versions

{% updates format="full" %}
{% update date="2026-09-10" tags="new-release,stable" %}

## 2026-09-10

#### Updates

Use this version by specifying the header:

```json
"x-api-version": "2026-09-10"
```

This version was previously available as the beta version `2026-01-01-beta`. The two are identical, and `2026-01-01-beta` remains callable, so if you are already using the beta you only need to change the version header. Omitting the `x-api-version` header targets the default version, which is unchanged by this release.

This version includes several updates for all endpoints. Two of them address the same underlying fact: an `id` is not guaranteed to be unique within a domain, because some domains name groups of time series rather than every time series individually, so a single query can match several time series that share one `id`.

* **Metadata**: changed response format for `/metadata`, the response is now a JSON array with metadata objects, where the `id` is part of the metadata object. In earlier versions this is a JSON object with the `id` as the key, so time series sharing an `id` arrive as repeated keys in the same object. While the JSON standard does allow duplicate object keys, most parsers resolve them by keeping only the last occurrence, which drops the other time series without any error. With the array, every matched time series is its own element, so nothing is lost regardless of what parser or software library is used, and the number of elements tells you how many time series the query matched. Additionally, NDJSON has been introduced as a new response format for `/metadata`.
* **Measurements**: the response format for `/measurements` is unchanged from `2025-10-31`, each data point carries an `id`, a `timestamp` and a `value`.
* **Statistics**: changed response format for `/statistics`, which now follows more closely the response format of the `/measurements` endpoint. The response contains `timestamp` and `id` (like in `/measurements`), and an `aggregations` object containing all requested aggregations. The aggregation type is not part of the `id` anymore, and the default aggregation has been changed from `avg` to `mean` (`avg` is not defined when there is only 1 datapoint within a window, while the `mean` is equal to that datapoint). In earlier versions each result key is `{domainId}/{AggregationName}`, so time series sharing a `domainId` produce several objects with the same key and the same `timestamp`. Nothing in the response says which time series a value came from, and code that indexes the results by key and timestamp overwrites one value with another. This version gives two well defined outcomes instead:
  * By default, time series that resolve to the same `id` are merged into a single result per time window and `id`, covering all of their data points.
  * Query with `output_domain=id` to keep every time series separate. The `id` domain guarantees uniqueness, so each result carries its own unique `id` and every value can be traced back to one time series.

#### Migration guide

This guide walks you through the breaking changes in `2026-09-10` compared to `2025-10-31`. Requests and query parameters remain identical to the previous version, apart from one changed default aggregation, so this guide focuses on how to update parsing logic to the changed response formats (with Python code examples).

**Measurements endpoint**

The `/measurements` response format is identical in API versions `2025-10-31` and `2026-09-10`, in every response format including `parquet`, so your code does not need updating.

**Metadata endpoint** The `/metadata` response has undergone substantial changes. See below for a sample of how the new format looks like, and how to update parsing logic.

Version `2025-10-31` response:

```json
{
  "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/": {
    "name": "Longitude",
    "description": "SHIP POSITION (LONGITUDE)",
    "source": "FurunoVDR",
    "scale": 1.0,
    "uom": "deg",
    "mapsTo": {
      "jsmea": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/",
      "raalabs": "IMO1000003/GPS_LONG"
    },
    "rangeHigh": 180.0,
    "rangeLow": -180.0,
    "timeSeriesId": "11000001-0000-0000-0000-000000000000",
    "vesselImo": "1000003",
    "vesselName": "Flying Dutchman"
  }
}
```

Version `2026-09-10` response:

```json
[
  {
    "id": "IMO1234567/jsmea_mac/MainEngine/Cylinder9/ScavAir//Temp/",
    "timeSeriesId": "a8009573-5013-4d18-8e63-1ae2f91d6b80",
    "source": "Aconis",
    "dataProvider": "Raa Labs",
    "unitOfMeasure": "°C",
    "scale": 1.0,
    "vessel": {
      "name": "Flying Dutchman",
      "imo": "1234567"
    },
    "domains": {
      "jsmea": {
        "id": "IMO1234567/jsmea_mac/MainEngine/Cylinder9/ScavAir//Temp/",
        "namingRule": "jsmea_mac",
        "category": "MainEngine",
        "subcategory": "Cylinder9",
        "content": "ScavAir",
        "item": "Temp"
      },
      "raalabs": {
        "id": "IMO1234567/ME CylinderScavAirTemp_9",
        "shortName": "ME CylinderScavAirTemp_9"
      },
      "maker": {
        "id": "IMO1234567/Aconis/0517",
        "name": "0517",
        "description": "M/E CYL.#9 SCAV. AIR BOX FIRE",
        "rangeHigh": 200.0,
        "rangeLow": 0.0
      }
    }
  }
]
```

How to update your code:

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10"  # was "2025-10-31"
}

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/IMO1234567/*"
response = requests.get(url, headers=headers)

# Version 2025-10-31 — response is a dict with domainIds as keys
# note that standard json parsers drop duplicate keys (this is one of major issues fixed with the new api version)
metadata = response.json()  # dict
for tag_id, meta in metadata.items():
    name = meta["name"]
    description = meta["description"]
    unit = meta["uom"]
    scale = meta["scale"]
    source = meta["source"]
    time_series_id = meta["timeSeriesId"]
    vessel_imo = meta["vesselImo"]
    vessel_name = meta["vesselName"]
    range_high = meta["rangeHigh"]
    range_low = meta["rangeLow"]
    raalabs_id = meta["mapsTo"].get("raalabs")

# Version 2026-09-10 — response is a list of metadata objects

metadata = response.json()  # list
for meta in metadata:
    tag_id = meta["id"]                            # was the dict key
    unit = meta["unitOfMeasure"]                   # was "uom"
    scale = meta["scale"]                          # unchanged
    source = meta["source"]                        # unchanged
    time_series_id = meta["timeSeriesId"]          # unchanged
    data_provider = meta["dataProvider"]           # new in this version
    vessel_imo = meta["vessel"]["imo"]             # was "vesselImo"
    vessel_name = meta["vessel"]["name"]           # was "vesselName"
    raalabs_id = meta["domains"]["raalabs"]["id"]  # was mapsTo["raalabs"]
    maker = meta["domains"].get("maker", {})       # a domain is only present if the time series has a name in it
    name = maker.get("name")                       # was "name"
    description = maker.get("description")         # was "description"
    range_high = maker.get("rangeHigh")            # was "rangeHigh"
    range_low = maker.get("rangeLow")              # was "rangeLow"
```

A time series that has no name in a given domain has no entry for it in `domains`, so guard the reads that go through one.

If your code needs to look up metadata by identifier, build the dictionary yourself and key it by `timeSeriesId`. Keying it by `id` reintroduces the dropped duplicates that the array response fixes.

**Statistics endpoint**

Two changes affect your queries and their results before you get to the response shape:

* The default aggregation is `mean` instead of `avg`. A request that does not set the `aggregations` parameter now returns a plain mean of the values in the window, where it previously returned a time-weighted average. Request `aggregations=avg` explicitly to keep the old calculation.
* The aggregation names in the response are the lowercase names you requested, for example `mean` and `count`. In `2025-10-31` the names and the delimiter were determined by the queried domain, and appeared capitalized in the result keys (`.../Mean`, `.../Count`). Code that matches on `/Avg` or `/Mean`, or that splits a result key to recover the aggregation type, needs updating. See below:

Version `2025-10-31` response:

```json
[
  {
    "IMO1000001/ME ShaftPower/Avg": 13.12,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Max": 15.47,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Min": 10.85,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  },
  {
    "IMO1000001/ME ShaftPower/Count": 3600,
    "timestamp": "2025-09-10T09:00:00.000000Z"
  }
]
```

Version `2026-09-10` response:

```json
[
  {
    "timestamp": "2025-09-10T09:00:00.000000Z",
    "id": "IMO1000001/ME ShaftPower",
    "aggregations": {
      "avg": 13.12,
      "max": 15.47,
      "min": 10.85,
      "count": 3600
    }
  }
]
```

How to update your code:

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10"  # was "2025-10-31"
}

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/IMO1234567/ME%20ShaftPower"
params = {"aggregations": "avg,max,min,count", "window": "1h", "last": "1day"}
response = requests.get(url, headers=headers, params=params)
data = response.json()

# Version 2025-10-31 — each object has a dynamic key like "{domainId}/{AggType}"
for entry in data:
    timestamp = entry["timestamp"]
    for key, value in entry.items():
        if key != "timestamp":
            # key is e.g. "IMO1234567/ME ShaftPower/Avg"
            # Split to extract the domainId and aggregation type
            *id_parts, agg_type = key.split("/")
            domain_id = "/".join(id_parts)
            print(f"{domain_id} {agg_type}={value} at {timestamp}")

# Version 2026-09-10 — structured fields with aggregations object
# there is no need for string splitting to get aggregation types
for entry in data:
    timestamp = entry["timestamp"]
    domain_id = entry["id"]
    for agg_type, value in entry["aggregations"].items():
        print(f"{domain_id} {agg_type}={value} at {timestamp}")
```

{% endupdate %}

{% update date="2025-11-03" tags="stable" %}

## 2025-10-31 (default)

Initial dated version. Target this version by using the following version header in your requests.

```json
"x-api-version": "2025-10-31"
```

This version is supported until 2027-03-10, six months after the release of `2026-09-10`, and is deactivated after that date. The migration guide in the `2026-09-10` entry above covers the changes you need to make in order to change to this version.

#### Migration guide

The functionality of this version is identical to the `default` version, which is targeted when no version header is specified. There are no changes to client code required when updating to version `2025-10-31` from the unversioned API.

### Unversioned API (default)

The unversioned API, this is currently the `default` version. This version is targeted when no `x-api-version` header is provided in the request.
{% endupdate %}
{% endupdates %}


# Updates

{% updates format="full" %}
{% update date="2026-09-10" tags="new-release,stable" %}

## Stable release of API version `2026-09-10`

* API version `2026-09-10` is now available. It is the stable release of the response formats that were developed under `2026-01-01-beta`, and the two versions are identical, so moving over from the beta only requires changing the `x-api-version` header.
* See the full release details and migration guide here: [2026-09-10](/2025-10-31/getting-started/changelog/versions#2026-09-10)
* `2026-01-01-beta` remains callable, and continues to return the same responses as `2026-09-10`.
* Omitting the `x-api-version` header targets the default version, which is unchanged by this release.
* Version `2025-10-31` is supported until 2027-03-10, six months from this release, and is deactivated after that date.
  {% endupdate %}

{% update date="2026-09-02" tags="beta,new-feature" %}

## Merged statistics for shared domain ids, and `timeSeriesId` removed in `2026-01-01-beta` version

* The `/statistics` endpoint now returns a single result per time window and `id` in API version `2026-01-01-beta`. Where several time series resolve to the same `id` (based on chosen `output_domain`), their aggregations are merged into one result covering the data points of all of those time series.
* The `domainId` field has been renamed to `id` in `/measurements` and `/statistics` responses in API version `2026-01-01-beta`, it contains the same values as before.
* The `timeSeriesId` field has been removed from the `/measurements` and `/statistics` responses in API version `2026-01-01-beta`, in every response format. Rows are identified by `id` alone, and the `/measurements` `parquet` schema now carries a single `id` column in place of `time_series_id` and `domain_id`, which makes it identical to the one in `2025-10-31`.
  {% endupdate %}

{% update date="2026-08-31" tags="new-feature" %}

## Output domain for measurements and statistics

* The `/measurements` and `/statistics` endpoints now accept an `output_domain` query parameter, which names the results in that domain instead of the domain that was queried. For example, query the `raalabs` domain with `output_domain=jsmea` to select time series by their flat Raa Labs tag name and get JSMEA names back. A time series that has no name in the requested output domain is left out of the response. This is available in both API versions `2025-10-31` and `2026-09-10`.
  {% endupdate %}

{% update date="2026-08-26" tags="new-feature" %}

## MCP server documentation

* The documentation now covers the Raa Labs MCP server, which exposes vessel time series data to AI agents over the Model Context Protocol. See the MCP Server page for how to connect a client.
  {% endupdate %}

{% update date="2026-08-06" tags="new-feature" %}

## Partial edge windows for statistics

* The `/statistics` endpoint now accepts a `window_edges` query parameter, which controls what happens to the windows at each end of the query time range. With `whole` (the default, and the previous behavior) the range is expanded to whole windows, and the leading partial window is dropped and the trailing window is returned in full. With `partial` the query range is used exactly as given, and the windows at each end are returned as partial windows. The parameter is ignored when `window = none`. This is available in both API versions `2025-10-31` and `2026-09-10`.
  {% endupdate %}

{% update date="2026-08-05" tags="bug-fix" %}

## Maker domain range values now consistently returned as floats

* In API version `2026-01-01-beta`, the `rangeLow` and `rangeHigh` fields of the `maker` domain in the `/metadata` response were returned as integers when the value had no decimal part, for example `200`. They are now consistently returned as floating point numbers, for example `200.0`, matching the behavior of API version `2025-10-31`.
  {% endupdate %}

{% update date="2026-05-18" tags="new-feature" %}

## NDJSON response format for metadata

* The `/metadata` endpoint now supports `ndjson` output, in addition to `json`. Use `format=ndjson` as query parameter, or set the `Accept: application/x-ndjson` header. This is available only in API version `2026-09-10`.
  {% endupdate %}

{% update date="2026-05-11" tags="bug-fix" %}

## Return status code `406` for unsupported response formats

* Requests to API endpoints with an unsupported response format now return a `406 Not Acceptable` error, instead of `500 Internal Server Error`. For example, requesting `format=html` on `/measurements`, or `format=csv` on `/metadata`, will now return a `406` response.
  {% endupdate %}

{% update date="2026-04-28" %}

## Statistics endpoint: fix and clarify window alignment behavior

* Fixed inconsistent behavior in the `/statistics` endpoint (API version `2025-10-31`) for boundary aggregation windows when windows were not aligned with the query range.
* Updated documentation to clarify the behavior.
  {% endupdate %}

{% update date="2026-04-16" tags="experimental-feature" %}

## CSV and HTML response formats for statistics

* The `/statistics` endpoint now supports `csv` and `html` output formats, in addition to `json` and `ndjson`. Use `format=csv` or `format=html` as query parameter. This feature is in experimental state.
  {% endupdate %}

{% update date="2026-04-08" %}

## Python code samples

* This documentation now includes Python code samples for all API endpoints and examples.
  {% endupdate %}

{% update date="2026-03-20" tags="new-feature" %}

## Delta aggregation support for statistics

* The `/statistics` endpoint now supports the `delta` aggregation, which computes the difference between the last and first value (`last_val - first_val`) within the aggregation window.
  {% endupdate %}

{% update date="2026-03-18" tags="new-feature,bug-fix" %}

## JSON data type aggregations, timestamp normalization, and trailing comma fix

* The `/statistics` endpoint now supports the following aggregations for the `JSON` data type:
  * `count`
  * `last_val`
  * `first_val`
  * `last_time`
  * `first_time`
* The `timestamp` field in API responses has been normalized to always include microsecond precision, sample `timestamp`: `2024-12-04T21:00:00.000000Z`
* Requests to `/statistics` using API version `2026-01-01-beta` occasionally returned trailing commas in the JSON response, this has been fixed.
  {% endupdate %}

{% update date="2026-03-16" %}

## Documentation host updated to `portal.raalabs.io`

* This API documentation has been updated to use `portal.raalabs.io` as host for all examples. `prism.raalabs.io` continues to be accessible.
  {% endupdate %}

{% update date="2026-02-20" tags="new-feature" %}

## Multi-parameter queries and arbitrary time window aggregation

* The `parameters` domain now supports querying multiple parameters with the `{}`-syntax in a single `GET` request.
* The `/statistics` HTTP endpoint now supports calculating aggregates over an arbitrary time window, returning one window with one value per aggregation across the whole time range requested.
  {% endupdate %}

{% update date="2026-01-22" tags="new-feature" %}

## `mapsTo` field now includes id domainIds

* The `mapsTo` field in the response to `/metadata` now contains the `id` domainIds.
  {% endupdate %}

{% update date="2026-01-15" tags="bug-fix" %}

## Trailing comma fix in measurements and statistics responses

* This update fixes a bug where in some cases `/measurements` and `/statistics` returned a trailing comma after the last JSON element within the array, which can cause errors in JSON parsing.
  {% endupdate %}

{% update date="2025-12-10" tags="bug-fix" %}

## Accept both aggregation and aggregations query parameter

* Both `aggregations` and `aggregation` are accepted as query parameter name for the `/statistics` endpoint. Previously, only `aggregations` was accepted, and using for example `aggregation=count` would not be recognised as the query parameter, and the API would return the default aggregation of `avg` instead.
  {% endupdate %}

{% update date="2025-12-09" tags="new-feature" %}

## Verbose domainIds query parameter

* All three endpoints `/metadata`, `/measurements` and `/statistics` now accept a `verbose` query parameter. This is a flag to toggle on verbose domainIds. Some domains offer more human-friendly, verbose domainIds; if `verbose` is set to `true`, the domainIds in the response will be verbose. Below is an example of a verbose versus regular domainId within the `vis-3-8a` domain:
  * regular: `IMO1234567/411.1/C101.31-6/meta/qty-temperature/cnt-coolant/pos-outlet`
  * verbose: `IMO1234567/411.1/C101.31-6/~propulsion.engine/~cylinder.6/meta/qty-temperature/cnt-coolant/pos-outlet`
    {% endupdate %}

{% update date="2025-11-19" tags="new-feature" %}

## VIS 3-8a domain support

* Added general support for the `vis-3-8a` domain. Queries can now be done using `vis-3-8a` domainIds, see the relevant section in the docs here: [vis 3-8a domain](/2025-10-31/docs/supported-domains#the-vis-3-8a-domain). Note that the availability of domainIds is subject to time series contextualization, which happens independent of the API development.
  {% endupdate %}

{% update date="2025-11-17" tags="bug-fix,new-feature" %}

## JSMEA POST improvements and Content-Type error handling

* `POST` requests for `/measurements` and `/statistics` using the JSMEA domain now support tags with empty hierarchy levels within the `POST` body.
* `POST` requests without the `Content-Type` header now return a more appropriate `Unsupported Media Type` error message. `application/json` is the only allowed value for the header.
  {% endupdate %}

{% update date="2025-11-06" tags="new-feature" %}

## `mapsTo` field now includes maker domainIds

* The `mapsTo` field in the response to `/metadata` now contains `maker` domainIds.
  {% endupdate %}
  {% endupdates %}


# Limitations

Raa Labs limits the number of REST API calls that can be made within a specific amount of time. This limit helps prevent abuse and denial-of-service attacks, and ensures that the API remains available for all users.

Contact your administrator for details about your environment.

## Rate Limits

The rate limit applies per IP address and is not tied to the client credentials used. If the limit is exceeded, the API returns a `429` status code.

## Concurrent Requests

The maximum allowed number of concurrent requests applies per client credentials, but is independent of the IP address used to make the request. If the maximum is exceeded, the API returns a `429` status code.


# API Endpoints

## Overview

The API is organized into three main categories of endpoints, each serving a different purpose:

* **Measurements (Time Series) Endpoints:** Retrieve actual time series data points (raw sensor measurements over time).
* **Metadata Endpoints:** Retrieve descriptive information (metadata) about time series tags (e.g. name, description, units, source of a sensor).
* **Statistics Endpoints:** Retrieve aggregated statistics (computed metrics) on time series data over specified time windows.

All endpoint URLs include a **domain** and an **expression** as path parameters, which together specify what data you are querying. The *domain* represents a context or naming schema (for example, a standard for tag names), and the *expression* is a query string identifying one or many time series within that domain.

### Common Headers

There are two common headers:

* the `Authorization` header is required for all endpoints
* the `x-api-version` header is optional, but it is strongly encouraged to use this header in production scenarios and specify what API version should be used. Read about versioning [here](/getting-started/versioning).

### Common Path Parameters <a href="#common-path-parameters-domain-and-expression" id="common-path-parameters-domain-and-expression"></a>

Every endpoint path includes `{domain}/{expression}`:

* **`domain`:** The contextual domain or naming scheme for the data. For example: `jsmea` (Japan Ship Machinery and Equipment Association standard for ISO 19848), `raalabs` (a flat naming scheme used by Raa Labs), `id` (the raw UUID of a time series), etc. The domain determines how the API interprets the expression part.
* **`expression`:** A query string that identifies the hierarchy or path of the data requested within the chosen domain. This could be a full specific path to a tag or include wildcard characters (`*`) to match multiple items.

**Wildcard Support:** You can use `*` in expressions to match individual levels of the hierarchy (matching multiple hierarchy levels is not supported), allowing broad queries. This is similar to wildcards in MQTT topics. For example:

* **Hierarchical Path Example:** `IMO1234567/411.1/C101.61/S203/meta/qty-mass.flow.rate` – a full path locating a specific measurement in the hierarchy (for a given IMO number and subsystem).
* **Wildcard Example:** `IMO1234567/*/qty-mass.flow.rate` – uses `*` to match any value in the second level, returning all measurements ending in `qty-mass.flow.rate` for the vessel `IMO1234567`.

When you query data, the domain and expression together determine which time series are returned. You can retrieve a single tag's data or use wildcards to retrieve multiple related tags in one query.

All examples in this documentation use curl with the base URL: `https://portal.raalabs.io/{ENVIRONMENT}`. Replace `{ENVIRONMENT}` with your tenant name.


# Measurements Endpoint

The **Measurements** endpoint provides access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time series data.

The Measurements API supports both `GET` and `POST` methods for fetching data:

* `GET` is used to retrieve data for a given domain/expression directly via the URL path.

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/{domain}/{expression}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/{domain}/{expression}"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

* `POST` can be used to retrieve data for multiple queries in one request (you provide a list of domain/expression queries in the JSON body).

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/measurements/query" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2026-09-10" \
  -H "Content-Type: application/json" \
  -d '[
    "domain/expression",
    "domain/expression"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/query"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10",
    "Content-Type": "application/json",
}
data = [
    "domain/expression",
    "domain/expression",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Use the `GET` method for most queries. Use the `POST /measurements/query` if you need to **cherry-pick multiple specific time series** in one request (by providing an array of queries as shown above). Only `POST` requests support domainIds with empty hierarchy levels.

## Request

### Path Parameters

| Parameter    | Description                                                                                                                                                                                          |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `domain`     | The contextualization domain (e.g. `jsmea`, `raalabs`, `id`, etc.) defining the naming scheme.                                                                                                       |
| `expression` | The query expression within that domain to specify the desired time series. This can be a full path or include wildcards (`*`). In many domains, the expression begins with the vessel’s IMO number. |

### Query Parameters

All query parameters below are **optional**. They let you refine the time range and format of the data returned. If no time range is specified, the default is the last **1 hour** of data up to the current time.

* **Time Range Parameters:** You can specify the time window for data using any of the following:
  * `from` / `to` (string): Start and end timestamps in ISO 8601 format (e.g., `2025-06-12T07:00:00Z`).
  * `from_epoch` / `to_epoch` (integer): Start and end time as Unix epoch timestamps in seconds.
  * `since` (string): A relative time duration string (e.g., `1day`, `6hours`) indicating how far back from now to retrieve data. For example, `since=1day` returns data from 24 hours ago *until now*. Valid values are `minute(s)`, `hour(s)`, `day(s)`, `month(s)`.
  * `last` (string): Similar to `since`, but anchored to the last full period of time. For example, `last=1day` returns data for the last full day (yesterday), as opposed to the past 24 hours.
  * `year`, `month`, `day` (numbers): Specify a calendar year, month, or day to retrieve data for those specific periods. These can be combined (e.g., `year=2024&month=3` for March 2024, or add `day` for a specific date).
* **Format Parameters:**
  * `format` (string): Desired output format of the data. Options are:
    * `json` (default): JSON array response (see format details below).
    * `ndjson`: Newline-delimited JSON, suitable for streaming large results.
    * `parquet`: Apache Parquet binary format. (Set the `Accept` header to the corresponding MIME type if using this.)
* **Verbose Parameter:**
  * `verbose` (boolean): Some domains offer more human-readable, verbose domainIds. This parameter allows you to toggle verbose domainIds on and off. If the domain does not distinguish between verbose and regular IDs, this parameter does not have any effect.
    * `false` (default): response includes regular domainIds
    * `true`: response includes verbose domainIds
* **Output Domain Parameter:**
  * `output_domain` (string): Returns the `id` of each matched time series in this domain, instead of the domain that was queried. For example, query the `raalabs` domain with `output_domain=jsmea` to get JSMEA ids back. Defaults to the queried domain. A time series that has no name in the requested output domain is left out of the response, so a query can return fewer time series with `output_domain` set than without it. The `id` domain is defined for every time series, so `output_domain=id` never leaves anything out.

You can also specify the response format via the **HTTP Accept Header** instead of the `format` query param:

* For JSON: `Accept: application/json` (default if unspecified).
* For NDJSON: `Accept: application/x-ndjson`.
* For Parquet: `Accept: application/vnd.apache.parquet`.

## Response

If the request is valid and the domain/expression matches one or more time series, the API returns all data points for those time series within the requested time range. The structure of the response depends on the format requested. In all cases, each data point is associated with an `id`, a `timestamp`, and a `value`.

{% hint style="info" %}
**Note:** The `id` field in each data point will use the same domain naming scheme as your query. For example, if you query the `raalabs` domain, each `id` in the response will be in the Raa Labs naming format; if you query `jsmea`, the `id` will be in the JSMEA hierarchical format. Set the `output_domain` query parameter to get the `id` in a different domain than the one you queried.
{% endhint %}

{% hint style="info" %}
**Note:** A domain does not always name every time series individually, so several time series can share one `id`. Their data points are then returned as separate rows carrying that same `id`, and rows from different time series cannot be told apart. Query with `output_domain=id` to keep them separate, since the `id` domain names every time series individually.
{% endhint %}

### Formats

{% tabs %}
{% tab title="JSON" %}
For `format=json` (the default), the result is a JSON **array** of data point objects. Each object has the following fields:

* `timestamp` – ISO 8601 timestamp of the measurement (UTC)
* `id` - The identifier of the measurement in the domain you specified.
* `value` – The sensor reading value at that timestamp (numeric or JSON, depending on the data type).

Example JSON response:

```json
[
  {
    "timestamp": "2025-06-12T07:42:03.361000Z",
    "id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/",
    "value": 120.225685
  },
  {
    "timestamp": "2025-06-12T07:42:03.361000Z",
    "id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Latitude/",
    "value": 36.008892
  }
]

```

{% endtab %}

{% tab title="NDJSON" %}
For `format=ndjson` (Newline-Delimited JSON), the response contains one JSON object per line, instead of a single array. This format is useful for streaming large datasets where each line can be processed independently. The content of each JSON object is the same as in the regular JSON format (with `timestamp`, `id` and `value` fields).

Example NDJSON response (two lines, each a separate JSON object):

```ndjson
{"timestamp": "2025-06-12T07:42:03.361000Z","id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Longitude/","value": 120.225685}
{"timestamp": "2025-06-12T07:42:03.361000Z","id": "IMO1000003/jsmea_nav/PositioningSystem/GPS///Latitude/","value": 36.008892}
```

{% endtab %}

{% tab title="Parquet" %}
For `format=parquet`, the response will be a binary **Parquet file** download. The Parquet schema includes the following columns for the time series data (each data point will populate one of the value columns depending on its type):

| Column Name    | Data Type | Description                                                                                          |
| -------------- | --------- | ---------------------------------------------------------------------------------------------------- |
| `timestamp`    | int64     | Timestamp of the data point in Unix epoch milliseconds.                                              |
| `id`           | string    | The identifier of the time series in the domain you specified, or in the `output_domain` if set.     |
| `long_value`   | int64     | If the data point value is an integer (64-bit), it will appear here. Otherwise null for this column. |
| `double_value` | float64   | If the data point value is a floating-point number, it will appear here. Otherwise null.             |
| `json_value`   | string    | If the data point value is a JSON string or non-numeric, it will appear here. Otherwise null.        |

Each row in the Parquet file represents one data point. **Only one** of `long_value`, `double_value`, or `json_value` will be populated for each row, depending on the data type of the measurement.

{% hint style="info" %}
**Note:** Use tools or libraries that support Apache Parquet to read the returned file. Parquet format is useful for efficient storage and transfer of large datasets.
{% endhint %}
{% endtab %}
{% endtabs %}

***

## Examples

### Retrieve Recent Measurements (last 1 hour, all tags)

Get all measurements for the last 1 hour using the `raalabs` domain (this will fetch the most recent hour of data for all available time series in the Raa Labs naming scheme):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/*?last=1hour" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/*?last=1hour"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This returns one hour of recent data points for all tags. The `*` wildcard in place of the IMO number means all vessels/data in the `raalabs` domain.

### **Query Multiple Specific Measurements (last 1 hour, fleet-wide)**

Retrieve specific measurements (speed through water, shaft power, and fuel mass flow) for all vessels over the last hour. Here we use the JSMEA domain with a POST query to select multiple expressions:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?last=1hour" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10" \
  -H "Content-Type: application/json" \
  -d '[
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?last=1hour"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
    "Content-Type": "application/json",
}
data = [
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In the JSON body, we provided three query expressions:

* `SpeedThroughWater` for all vessels (`*` in the IMO position),
* `Main Engine Shaft Power` for all vessels,
* `Main Engine Fuel Oil Mass Flow Rate` for all vessels.

Each of these will return the last hour of data for the matching time series across the fleet.

### **Measurements for a Specific Tag and Vessel (exact match)**

Retrieve **Mass Flow Meter** measurements from the Main Engine Fuel Oil Line for a specific vessel (IMO 1234567) over a given date range (March 1–10, 2025). This example shows two ways to query the same data using different domains:

JSMEA domain (hierarchical tag):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/\
IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate\
?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Raa Labs domain (flat tag, spaces in tag names must be URL-encoded using `%20`):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs\
/IMO1234567/ME%20FuelMassFlow\
?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/IMO1234567/ME%20FuelMassFlow?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Both requests will return the fuel mass flow rate measurements for vessel 1234567 in the specified date range. The first uses the JSMEA standard path; the second uses the Raa Labs short tag (`ME FuelMassFlow`).

### Using Wildcards in Measurements Query

Retrieve all **Mass Flow Rate** measurements for vessel IMO 1234567 over a date range, without specifying the exact sub-paths:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea\
/IMO1234567/*/*/MassFlowRate\
?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/IMO1234567/*/*/MassFlowRate?from=2025-03-01T12:00:00Z&to=2025-03-10T12:00:00Z"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here, the wildcards (`*/*`) in the middle of the path will match any system and sub-system. This query will return any time series that end with `MassFlowRate` for the given vessel (for example, it could match fuel mass flow, if that is the naming, or any other "MassFlowRate" under different systems).

### **Fleet-Wide Query by Month**

Retrieve Main Engine fuel mass flow measurements for **all vessels in the fleet** for a specific month (March 2024):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea\
/*/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate\
?year=2024&month=03" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/jsmea/*/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate?year=2024&month=03"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

By using `year` and `month` parameters along with wildcards for IMO and sub-components, this request fetches all Main Engine fuel mass flow data across the fleet for March 2024.

### Selecting Multiple Time Series in One Request

You can query multiple specific time series in one call using a POST request. For example, to fetch **Speed Through Water**, **Shaft Power**, and **Fuel Mass Flow** for the entire fleet (each for the last hour):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?since=1hour" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10" \
  -H "Content-Type: application/json" \
  -d '[
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/measurements/query?since=1hour"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
    "Content-Type": "application/json",
}
data = [
    "jsmea/*/jsmea_nav/SpeedandDistanceMeasurementSystem/DopplerLOG/*/*/SpeedThroughWater/",
    "jsmea/*/jsmea_mac/MainEngine/DrivingShaft/Output/*/Power/",
    "jsmea/*/jsmea_mac/MainEngine/FuelOilLine/FuelOil//MassFlowRate/",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This is similar to the earlier POST example, but using `since=1hour` (which also retrieves the last 1 hour of data). The response will intermix data from the three requested time series, each identified by its `id` in the output.


# Metadata Endpoint

The **Metadata** endpoint returns descriptive information about time series tags. This may include identifiers across multiple naming domains, data source, unit of measure, scale factor, value ranges, associated vessel, and other contextual attributes. Use this endpoint to understand what a particular time series represents.

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/{domain}/{expression}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/{domain}/{expression}"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Request

### Path Parameters

| Parameter    | Description                                                                                                                                                                                                                                      |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `domain`     | The contextual domain for the metadata query (e.g. `jsmea`, `raalabs`, `id`, `vis-3-8a`).                                                                                                                                                        |
| `expression` | The domain-specific query expression used to select which time series metadata to return. This can be a specific path or include wildcards (`*`) to retrieve multiple tags. In many domains, the expression begins with the vessel’s IMO number. |

### Query Parameters

* **Format Parameter:**
  * `format` (string): Desired output format of the metadata. Options are:
    * `json` (default): JSON array response.
    * `ndjson`: Newline-delimited JSON, suitable for streaming large results.
* **Verbose Parameter:**
  * `verbose` (boolean): Some domains offer more human-readable, verbose domainIds. This parameter allows you to toggle verbose domainIds on and off. If the domain does not distinguish between verbose and regular IDs, this parameter does not have any effect.
    * `false` (default): response includes regular domainIds
    * `true`: response includes verbose domainIds

## Response

If the query is valid, the response will include metadata for all matching time series. The response structure depends on the chosen format. Common among all of them is the individual metadata object, which contains the following attributes:

**Top-level fields:**

* `id` – The ID for the time series, according to the chosen domain in the request.
* `timeSeriesId` – A unique UUID for the time series (internal identifier).
* `source` – Source system or origin of the data (e.g., sensor or system name).
* `dataProvider` – The data provider responsible for the time series.
* `unitOfMeasure` – Unit of measure for the values (e.g., `bar` for pressure, `°C` for temperature).
* `scale` – Scaling factor applied to the raw data.

**Vessel object:**

* `vessel.name` – The name of the vessel the data is associated with.
* `vessel.imo` – The IMO number of the vessel.

**Domains object:**

The domains object maps the time series across different naming domains. Each key represents a domain and contains the ID of the time series within that domain, along with domain-specific metadata.

Available domains (a time series may not include all):

* `id` – Uses the unique timeSeriesId as identifier.
* `jsmea` – JSMEA domain naming with the hierarchical levels as separate fields: `namingRule`, `category`, `subcategory`, `content`, `position`, `item`, and `suffix`.
* `maker` – Includes `name`, `description`, `rangeHigh`, and `rangeLow`, specified by the maker of the machinery or equipment. The range fields specify the expected minimum and maximum values for the measurement.
* `raalabs` – Raa Labs naming convention with `id` and `shortName`.
* `vis-3-8a` – VIS 3-8a domain naming with `codes` of the `primaryItem` and `secondaryItem`. Each code can have a location specified by: `number`, `side`, `vertical`, `transverse`, `longitudinal`. It also includes `type` and `category` for the `metadata` tags.

Note that not all fields are present for every time series.

{% hint style="info" %}
**Important:** Note that not all domains necessarily return unique ids. There could be multiple time series having the same id, within a domain.
{% endhint %}

### Formats

{% tabs %}
{% tab title="JSON" %}
For `format=json` (the default), the response is an array of metadata objects.

**Example JSON metadata response**:

```json
[
    {
        "id": "IMO1234567/411.1/C101.31-9/meta/qty-temperature/cnt-scavenge.air",
        "timeSeriesId": "a8009573-5013-4d18-8e63-1ae2f91d6b80",
        "source": "Aconis",
        "dataProvider": "Raa Labs",
        "unitOfMeasure": "°C",
        "scale": 1.0,
        "vessel": {
            "name": "Flying Dutchman",
            "imo": "1234567"
        },
        "domains": {
            "id": {
                "id": "a8009573-5013-4d18-8e63-1ae2f91d6b80"
            },
            "jsmea": {
                "category": "MainEngine",
                "content": "ScavAir",
                "id": "IMO1234567/jsmea_mac/MainEngine/Cylinder9/ScavAir//Temp/",
                "item": "Temp",
                "namingRule": "jsmea_mac",
                "subcategory": "Cylinder9"
            },
            "maker": {
                "description": "M/E CYL.#9 SCAV. AIR BOX FIRE",
                "id": "IMO1234567/Aconis/0517",
                "name": "0517",
                "rangeHigh": 200.0,
                "rangeLow": 0.0
            },
            "raalabs": {
                "id": "IMO1234567/ME CylinderScavAirTemp_9",
                "shortName": "ME CylinderScavAirTemp_9"
            },
            "vis-3-8a": {
                "id": "IMO1234567/411.1/C101.31-9/meta/qty-temperature/cnt-scavenge.air",
                "metadata": [
                    {
                        "category": "qty",
                        "type": "temperature"
                    },
                    {
                        "category": "cnt",
                        "type": "scavenge.air"
                    }
                ],
                "primaryItem": [
                    {
                        "code": "411.1"
                    },
                    {
                        "code": "C101.31",
                        "number": 9
                    }
                ]
            }
        }
    },
    {
        "id": "IMO1234567/511.11/C101.61/S203/meta/qty-mass.flow.rate/cnt-fuel.oil",
        "timeSeriesId": "a3c8954b-a3fc-4c56-8c82-63972bc9dea5",
        "source": "Aconis",
        "dataProvider": "Raa Labs",
        "unitOfMeasure": "l/h",
        "scale": 1.0,
        "vessel": {
            "name": "Flying Dutchman",
            "imo": "1234567"
        },
        "domains": {
            "id": {
                "id": "a3c8954b-a3fc-4c56-8c82-63972bc9dea5"
            },
            "jsmea": {
                "category": "GeneratorEngine",
                "content": "FuelOil",
                "id": "IMO1234567/jsmea_mac/GeneratorEngine/FuelOilLine/FuelOil//MassFlowRate/",
                "item": "MassFlowRate",
                "namingRule": "jsmea_mac",
                "subcategory": "FuelOilLine"
            },
            "maker": {
                "description": "G/E F.O FLOW RATE",
                "id": "IMO1234567/Aconis/FO002",
                "name": "FO002",
                "rangeHigh": 120.0,
                "rangeLow": 0.0
            },
            "raalabs": {
                "id": "IMO1234567/AE FuelMassFlow_total",
                "shortName": "AE FuelMassFlow_total"
            },
            "vis-3-8a": {
                "id": "IMO1234567/511.11/C101.61/S203/meta/qty-mass.flow.rate/cnt-fuel.oil",
                "metadata": [
                    {
                        "category": "qty",
                        "type": "mass.flow.rate"
                    },
                    {
                        "category": "cnt",
                        "type": "fuel.oil"
                    }
                ],
                "primaryItem": [
                    {
                        "code": "511.11"
                    },
                    {
                        "code": "C101.61"
                    },
                    {
                        "code": "S203"
                    }
                ]
            }
        }
    }
]
```

{% endtab %}

{% tab title="NDJSON" %}
For `format=ndjson` (Newline-Delimited JSON), the response contains one JSON metadata object per line, instead of a single array. This format is useful for streaming large result sets where each line can be processed independently. The content of each JSON object is identical to the regular JSON format.

Example NDJSON response (two lines, each a separate JSON metadata object):

```ndjson
{"id":"IMO1234567/411.1/C101.31-9/meta/qty-temperature/cnt-scavenge.air","timeSeriesId":"a8009573-5013-4d18-8e63-1ae2f91d6b80","source":"Aconis","dataProvider":"Raa Labs","unitOfMeasure":"°C","scale":1.0,"vessel":{"name":"Flying Dutchman","imo":"1234567"},"domains":{"id":{"id":"a8009573-5013-4d18-8e63-1ae2f91d6b80"},"jsmea":{"category":"MainEngine","content":"ScavAir","id":"IMO1234567/jsmea_mac/MainEngine/Cylinder9/ScavAir//Temp/","item":"Temp","namingRule":"jsmea_mac","subcategory":"Cylinder9"},"maker":{"description":"M/E CYL.#9 SCAV. AIR BOX FIRE","id":"IMO1234567/Aconis/0517","name":"0517","rangeHigh":200.0,"rangeLow":0.0},"raalabs":{"id":"IMO1234567/ME CylinderScavAirTemp_9","shortName":"ME CylinderScavAirTemp_9"}}}
{"id":"IMO1234567/511.11/C101.61/S203/meta/qty-mass.flow.rate/cnt-fuel.oil","timeSeriesId":"a3c8954b-a3fc-4c56-8c82-63972bc9dea5","source":"Aconis","dataProvider":"Raa Labs","unitOfMeasure":"l/h","scale":1.0,"vessel":{"name":"Flying Dutchman","imo":"1234567"},"domains":{"id":{"id":"a3c8954b-a3fc-4c56-8c82-63972bc9dea5"},"jsmea":{"category":"GeneratorEngine","content":"FuelOil","id":"IMO1234567/jsmea_mac/GeneratorEngine/FuelOilLine/FuelOil//MassFlowRate/","item":"MassFlowRate","namingRule":"jsmea_mac","subcategory":"FuelOilLine"},"maker":{"description":"G/E F.O FLOW RATE","id":"IMO1234567/Aconis/FO002","name":"FO002","rangeHigh":120.0,"rangeLow":0.0},"raalabs":{"id":"IMO1234567/AE FuelMassFlow_total","shortName":"AE FuelMassFlow_total"}}}
```

{% endtab %}
{% endtabs %}

## Examples

### **Retrieve Metadata for a Single Vessel**

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/IMO1234567/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/IMO1234567/*"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This request returns a JSON array containing metadata objects for all time series associated with vessel IMO 1234567, using the Raa Labs domain naming convention. The trailing wildcard (\*) selects all tags for the vessel.

### **Retrieve Metadata for Entire Fleet**

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/raalabs/*"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This request returns metadata for all accessible time series across all vessels, using the Raa Labs domain naming convention. This is achieved by using a wildcard (`*`) in the IMO position of the expression.

### **Filtered Metadata Query (e.g., main engine tags)**

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/metadata/jsmea/*/jsmea_mac/MainEngine/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/metadata/jsmea/*/jsmea_mac/MainEngine/*"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

This request returns metadata for all Main Engine-related time series across all vessels, using the JSMEA domain naming convention. The wildcard in the IMO position selects all vessels, while the remaining expression path filters results to time series under the `jsmea_mac` engine and machinery naming rule with the `MainEngine` category.


# Statistics Endpoint

The **Statistics** endpoint provides **aggregated metrics** calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.

You can query one or multiple time series for statistics using `GET` requests:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/{domain}/{expression}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/{domain}/{expression}"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

...or `POST` requests:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl -X POST "https://portal.raalabs.io/{ENVIRONMENT}/statistics/query" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-version: 2026-09-10" \
  -H "Content-Type: application/json" \
  -d '[
    "domain/expression",
    "domain/expression"
  ]'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/query"
headers = {
    "Authorization": f"Bearer {TOKEN}",
    "x-api-version": "2026-09-10",
    "Content-Type": "application/json",
}
data = [
    "domain/expression",
    "domain/expression",
]

response = requests.post(url, headers=headers, json=data)
```

{% endcode %}
{% endtab %}
{% endtabs %}

As with measurements, use `GET` for a single domain/expression query and the `POST /statistics/query` (with a JSON array in the body) to retrieve multiple series in one call. Only `POST` requests support domainIds with empty hierarchy levels.

## Request

### Path Parameters

| Parameter    | Description                                                                                                                                                                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `domain`     | The contextual domain for the query (e.g. `jsmea`, `raalabs`, `id`, `vis-3-8a`).                                                                                                                                                                        |
| `expression` | The domain-specific query expression used to select which time series to compute statistics for. This can be a specific path or include wildcards (`*`) to retrieve multiple tags. In many domains, the expression begins with the vessel’s IMO number. |

### Query Parameters

Like the measurements endpoint, the statistics query supports time range parameters and format selection. If a requested window doesn't both start and end in the past, the statistics for it are calculated using the available data, which may be incomplete.

It also introduces parameters to specify the type of aggregation and the window size for aggregation. If no time range is given, the default is `from = -1h` to `to = now` (last one hour). If no aggregation is specified, the default is `mean`. If no window is specified, the default window is **1 minute** (`1m`).

* **Time Range:** Use `from`, `to`, `from_epoch`, `to_epoch`, `since`, `last`, `year`, `month`, `day` as described in the Measurements section to define the time range of data over which statistics are computed.
* **Aggregation Functions:** `aggregations` (string) – A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. If this parameter is omitted, the API uses `mean` by default.
* **Window Size:** `window` (string) – The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.<br>

  Windows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\
  \
  Set `window = none` to disable windowing. A single aggregation covering the full query range is returned.<br>

  A window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:

  ```
  [#####] = time window returned by the API
  11:30                                   16:30
    ↓ 12:00   13:00   14:00   15:00   16:00 ↓
    ¦   |       |       |       |       |   ¦
    ¦    [#####] [#####] [#####] [#####] [#####]
  ```

  Set `window_edges = partial` to use the query range exactly as given instead.
* **Window Edges:** `window_edges` (string) – Controls what happens to the windows at each end of the query time range when the range does not align with the time windows. This parameter is ignored when `window = none`, which always uses the query range as given.
  * `whole` (default): the range is expanded to whole windows, so the leading partial window is dropped and the trailing window is returned in full — meaning it can include data from after `to`.
  * `partial`: the range is used exactly as given, and the windows at each end are returned as partial windows covering only the part that falls inside the query range.<br>

    A partial window aggregates less data than a whole one, so compare values across windows with care. The first window carries the exact query start as its `timestamp` when it is partial. All other windows are aligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:

    ```
    [#####] = whole time window
    [###..] = partial time window, including data from the beginning of the window
    [..###] = partial time window, including data at the end of the window

      11:30                                   16:30
        ↓ 12:00   13:00   14:00   15:00   16:00 ↓
        ¦   |       |       |       |       |   ¦
        ¦   [#####] [#####] [#####] [#####] [#####]   window_edges = whole
     [..###][#####] [#####] [#####] [#####] [###..]   window_edges = partial
    ```

    With `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00` and `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.
* **Format:** `format` – Output format: `json` (default), `ndjson`, `csv`, or `html`.
* **Verbose Parameter:**
  * `verbose` (boolean): Some domains offer more human-readable, verbose domainIds. This parameter allows you to toggle verbose domainIds on and off. If the domain does not distinguish between verbose and regular IDs, this parameter does not have any effect.
    * `false` (default): response includes regular domainIds
    * `true`: response includes verbose domainIds
* **Output Domain Parameter:**
  * `output_domain` (string): Returns the `id` of each matched time series in this domain, instead of the domain that was queried. For example, query the `raalabs` domain with `output_domain=jsmea` to get JSMEA ids back. Defaults to the queried domain. A time series that has no name in the requested output domain is left out of the response, so a query can return fewer time series with `output_domain` set than without it. The `id` domain is defined for every time series, so `output_domain=id` never leaves anything out.

#### Supported Aggregations

The following aggregation functions are available to use in the `aggregations` parameter:

| `min`        | Minimum value in the window                                                                                                                                         |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max`        | Maximum value in the window                                                                                                                                         |
| `mean`       | Mean value in the window (if there is exactly one value, `mean` returns that value)                                                                                 |
| `avg`        | Average (time-weighted) value in the window (if there is exactly one value, `avg` returns `null`)                                                                   |
| `count`      | Count of data points (sample count)                                                                                                                                 |
| `sum`        | Sum of all values                                                                                                                                                   |
| `stddev`     | Standard deviation of values                                                                                                                                        |
| `var`        | Variance of values                                                                                                                                                  |
| `skewness`   | Statistical skewness of the values distribution                                                                                                                     |
| `kurtosis`   | Statistical kurtosis of the values distribution                                                                                                                     |
| `first_val`  | The first value in the time window                                                                                                                                  |
| `last_val`   | The last value in the time window                                                                                                                                   |
| `integral`   | Integral (area under the curve) over the window                                                                                                                     |
| `first_time` | Timestamp of the first data point in the window                                                                                                                     |
| `last_time`  | Timestamp of the last data point in the window                                                                                                                      |
| `delta`      | Difference between the last and first value in the window (`last_val - first_val`). Useful for calculating the change in a measurement over the aggregation window. |

You may combine multiple aggregations in one query by listing them separated with commas (e.g. `aggregations=avg,max,min,count`).

{% hint style="info" %}
**Note:** For time series with JSON data types, only a limited set of aggregations are available: `last_val`, `first_val`, `last_time`, `first_time`, and `count`. Other aggregations will return `null` for JSON data types.
{% endhint %}

## Response

If the request is valid, the API returns the computed statistics for each requested time series over the specified time range and window. The results are returned in time order, one object per time window and `id`, containing all requested aggregations.

{% hint style="info" %}
**Note:** Similar to the measurements endpoint, the `id` in the results will use the domain format you queried, unless you set the `output_domain` query parameter.
{% endhint %}

### Time Series Sharing an DomainId

A domain does not always name every time series individually, so several time series can resolve to the same `id`. Their statistics are then **merged** based on the `output_domain` parameter. The response holds one value per time window and `id` covering all of the data points of those time series, rather than one object per underlying time series.

Each aggregation is combined the way that aggregation requires:

* `count` and `sum` are added up
* `min` and `max` take the lowest and the highest value across the time series
* `mean`, `var`, `stddev`, `skewness` and `kurtosis` are recalculated for the combined set of data points
* `first_val`, `first_time`, `last_val` and `last_time` follow the earliest and the latest data point across the time series, and `delta` is the difference between those two values

`avg` and `integral` are time-weighted, and cannot be recalculated from values that are already aggregated. They are returned as `null` in a merged object. Request `mean` instead of `avg` for queries that can match time series sharing an `id`.

In the `csv` and `html` formats, merged time series likewise share a single `id/aggregation` column.

Query with `output_domain=id` to keep every time series separate, since the `id` domain names every time series individually.

### Formats

{% tabs %}
{% tab title="JSON" %}
For `format=json` (default), the response is a JSON **array** of objects. Each object represents the results of one time window and all requested aggregation types. Each object has the following fields:

* `timestamp` – ISO 8601 timestamp of the measurement (UTC), which marks the timestamp for that window's result. The timestamp corresponds to the start of the aggregation window. (For example, if window=1h, a timestamp of `2025-09-10T09:00:00Z` represents the window from 09:00 to 10:00.)
* `id` - The identifier of the measurement in the domain you specified.
* `aggregations` – an object containing the requested aggregations.

Example JSON response (for a query that requested `mean`, `max`, `min`, and `count` aggregations, and where a window size of 1 hour was chosen):

```json
[
  {
    "timestamp": "2025-09-10T09:00:00.000000Z",
    "id": "IMO1000001/ME ShaftPower",
    "aggregations": {
      "mean": 13.12,
      "max": 15.47,
      "min": 10.85,
      "count": 3600
    }
  },
  {
    "timestamp": "2025-09-10T10:00:00.000000Z",
    "id": "IMO1000001/ME ShaftPower",
    "aggregations": {
      "mean": 12.98,
      "max": 14.92,
      "min": 11.03,
      "count": 3600
    }
  }
]
```

(This example shows two 1-hour windows. Between 09:00 and 10:00 on Sept 10, 2025, the mean ShaftPower was 13.12, the max was 15.47, the min was 10.85, and there were 3600 data points. The next hour shows similar statistics with slightly different values.)
{% endtab %}

{% tab title="NDJSON" %}
For `format=ndjson`, the output is similar to the JSON format, but each object is written on a separate line (newline-delimited). This is helpful for streaming or incremental processing of large results.

Each line will be a JSON object identical in structure to the objects shown in the JSON example above.

Example NDJSON response:

```ndjson
{"timestamp": "2025-09-10T09:00:00.000000Z", "id": "IMO1000001/ME ShaftPower", "aggregations": {"mean": 13.12, "max": 15.47, "min": 10.85, "count": 3600}}
{"timestamp": "2025-09-10T10:00:00.000000Z", "id": "IMO1000001/ME ShaftPower", "aggregations": {"mean": 12.98, "max": 14.92, "min": 11.03, "count": 3600}}
```

{% endtab %}

{% tab title="CSV" %}
{% hint style="warning" %}
The CSV response format is an experimental feature, and is subject to change.
{% endhint %}

For `format=csv`, the response is returned as `text/csv` with a header row followed by data rows. Each row corresponds to one aggregation window timestamp, and columns represent the aggregated values for each time series.

Column names follow the pattern `id/aggregationName` (e.g., `IMO1000001/ME ShaftPower/mean`). Each `id` in the result gets its own set of columns, and time series that were merged into one `id` share a single set. Columns are sorted alphabetically.

Example CSV response (for a query that requested `mean`, `max`, `min`, and `count` aggregations with a 1-hour window):

```csv
timestamp,IMO1000001/ME ShaftPower/mean,IMO1000001/ME ShaftPower/count,IMO1000001/ME ShaftPower/max,IMO1000001/ME ShaftPower/min
2025-09-10T09:00:00.000000Z,13.12,3600,15.47,10.85
2025-09-10T10:00:00.000000Z,12.98,3600,14.92,11.03
```

{% endtab %}

{% tab title="HTML" %}
{% hint style="warning" %}
The HTML response format is an experimental feature, and is subject to change.
{% endhint %}

For `format=html`, the response is returned as `text/html; charset=utf-8` containing an HTML `<table>` element. The structure mirrors the CSV format, with each row representing one aggregation window.

Example HTML response:

```html
<table>
<thead>
<tr><th>timestamp</th><th>IMO1000001/ME ShaftPower/mean</th><th>IMO1000001/ME ShaftPower/count</th><th>IMO1000001/ME ShaftPower/max</th><th>IMO1000001/ME ShaftPower/min</th></tr>
</thead>
<tbody>
<tr><td>2025-09-10T09:00:00.000000Z</td><td>13.12</td><td>3600</td><td>15.47</td><td>10.85</td></tr>
<tr><td>2025-09-10T10:00:00.000000Z</td><td>12.98</td><td>3600</td><td>14.92</td><td>11.03</td></tr>
</tbody>
</table>
```

{% endtab %}
{% endtabs %}

## Examples

### **Aggregated Statistics (average & max over time)**

Retrieve the **average and maximum** Main Engine Shaft Power for all vessels, aggregated in 1-hour windows, over the last day (note that spaces in tag names must be URL-encoded using `%20`):

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/*/ME%20ShaftPower\
?aggregations=avg,max&window=1h&last=1day" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/*/ME%20ShaftPower?aggregations=avg,max&window=1h&last=1day"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this example:

* `raalabs/*/ME ShaftPower` targets the *ShaftPower* measurement for all vessels (`*` wildcard for IMO) in Raa Labs naming.
* `aggregations=avg,max` asks for Average and Maximum values.
* `window=1h` sets 1-hour aggregation windows.
* `last=1day` requests data for the last full day (24 hours).

The response will be a series of time-windowed results, each with an Avg and Max for that hour, along with a timestamp for the hour.

### **Statistics for a Specific Vessel (multiple metrics)**

Get the **mean** and **standard deviation** of fuel oil consumption (mass flow rate) for a specific vessel (IMO 1234567), calculated over daily windows for the last 30 days:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/jsmea\
/IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate\
?aggregations=mean,stddev&window=1d&last=30days" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/jsmea/IMO1234567/jsmea_mac/MainEngine/FuelOilLine/*/*/MassFlowRate?aggregations=mean,stddev&window=1d&last=30days"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this example:

* The domain/expression targets the MassFlowRate in the Main Engine Fuel Oil Line for vessel 1234567 (using wildcards for any sub-levels under FuelOilLine).
* `aggregations=mean,stddev` will return the daily average and daily standard deviation of that measurement.
* `window=1d` uses a one-day window for each data point (each result represents one day’s stats).
* `last=30days` means the last 30 full days (approximately the previous month).

The output will list one JSON object per day and aggregation type, each containing an aggregation name, and a `timestamp` (likely the start of the day).

### **Fleet-Wide Comparative Statistics**

Compare **minimum and maximum engine temperatures across the fleet** with 15-minute aggregation windows for the past week:

{% tabs %}
{% tab title="cURL" %}
{% code lineNumbers="true" %}

```bash
curl "https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs\
/*/ME*Temp*?aggregations=min,max&window=15m&since=7days" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import requests

TOKEN = "YOUR_TOKEN"
ENVIRONMENT = "YOUR_ENVIRONMENT"

url = f"https://portal.raalabs.io/{ENVIRONMENT}/statistics/raalabs/*/ME*Temp*?aggregations=min,max&window=15m&since=7days"
headers = {
    "Authorization": f"Bearer {YOUR_TOKEN}",
    "x-api-version": "2026-09-10",
}

response = requests.get(url, headers=headers)
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this example:

* The expression `raalabs/*/ME*Temp*` uses wildcards to match any Raa Labs tag that contains "ME" and "Temp" (e.g., it could match tags like "ME ExhaustTemp" or "ME CoolingTemp" depending on naming). This effectively tries to fetch engine temperature-related metrics for all vessels.
* `aggregations=min,max` will produce two values per window: the minimum and maximum temperature observed in each 15-minute interval.
* `window=15m` sets the aggregation interval to 15 minutes.
* `since=7days` retrieves data from one week ago up until now.

The result will show 15-minute snapshots of the lowest and highest recorded values among those temperature measurements, across the fleet, for the last week.


# Supported Domains

Raa Labs supports multiple **domains** for organizing and querying time series data. A domain defines how a time series is named and structured. Knowing the domains helps you form expressions for queries. The current supported domains include:

| Domain         | Description                                                                                                                                                                                                                   | Example DomainId                                              |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| **id**         | The raw time series UUID. This is a unique identifier assigned by Raa Labs for each signal. Use this for a direct lookup if you know the UUID.                                                                                | `62e23903-9db5-43cc-949d-78452ffb81bc`                        |
| **jsmea**      | JSMEA (Japan Ship Machinery and Equipment Association) naming standard, based on ISO 19848 for ship sensor data. This is a hierarchical naming scheme with multiple levels (e.g., system, subsystem, component, parameter).   | `IMO1234567/jsmea_mac/MainEngine/DrivingShaft/Output//Power/` |
| **raalabs**    | Raa Labs flat tag schema. A simpler, flattened naming convention that usually includes just the IMO number and a short descriptive tag.                                                                                       | `IMO1234567/ME ShaftPower`                                    |
| **parameters** | Parameter grouping schema. It includes the IMO number, a parameter group name, and a short name. This groups related signals under a category.                                                                                | `IMO1234567/ME Power/ME ShaftPower`                           |
| **maker**      | The name given to a tag by the maker of the machinery or equipment. Includes the maker name and a tag name.                                                                                                                   | `IMO1234567/Maker/12345`                                      |
| **vis-3-8a**   | The VIS (Vessel Information Structures) naming convention from DNV is a standardized way to uniquely identify vessel functions and onboard equipment. It uses a hierarchical structure based on GMOD (Generic Product Model). | `IMO1234567/913/S130.1-1/S121/sec/406i/H233/meta/qty-power`   |

{% hint style="info" %}
**Note:** For the `jsmea` and `vis-3-8a` domains, some signals may not yet have been mapped. If an expected signal is unavailable in these domains, use the `raalabs` domain. For specific requests, please contact us at <support@raalabs.com>.
{% endhint %}

## Querying Within Each Domain

This section describes the query expression structure for each supported domain. All domains support wildcard queries. Wildcards (`*`) can be used to match partial names or hierarchy levels, allowing you to retrieve data without specifying the full expression.

### The ID Domain

**Query Expression Format:**

`{timeseries_id}`

In this domain, the query expression consists solely of the UUID of the time series.

* `62e23903-9db5-43cc-949d-78452ffb81bc` Retrieves the time series with this exact UUID.
* `*` Retrieves all time series from *all vessels*

### The JSMEA Domain

**Query Expression Format:**

`{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}`

For a comprehensive explanation of the JSMEA naming scheme, refer to the [Description of Local ID Definitions (JSMEA Codebook)](https://www.jsmea.or.jp/ssap/topics/jsmea_codebook.html). In a hierarchical domain such as **`jsmea`**, wildcards may be applied at multiple levels:

* `IMO1234567/*/*/*/*/*/Power/` Retrieves all time series under vessel IMO1234567 whose final component is "Power" (regardless of which subsystem or component).
* `IMO1234567/jsmea_mac/MainEngine/*` Retrieves all Main Engine-related time series for vessel IMO1234567 (wildcard at the level below MainEngine to get everything under it).
* `IMO1234567/jsmea_mac/MainEngine/Fuel*` Retrieves all Main Engine with some kind of fuel component related time series for vessel IMO1234567 (wildcard at the level below MainEngine to get everything under it).

### The Raalabs Domain

**Query Expression Format:**

`{imo_number}/{tag_name}`

In a flat domain like **`raalabs`**, you can query multiple vessels or tags:

* `*/ME ShaftPower` Retrieves the **Main Engine ShaftPower** measurements for *all vessels* ( `*` in place of the IMO number matches every vessel).
* `raalabs/IMO1000002/ME* Shaft*` Retrieves all tags that start with ME and have the word Shaft in them.

### The Parameters Domain

**Query Expression Format:**

`{imo_number}/{parameter_group_name}`

A parameter represents a logical grouping of related time series, from one or more vessels. This enables analysis of specific operational topics (e.g., fuel consumption), either within a single vessel or across multiple vessels.

* `parameters/IMO1000001/AE Fuel Oil Consumption` Retrieves all time series in the AE Fuel Oil Consumption parameter group, for vessel IMO1000001.
* `parameters/{IMO1000001, IMO1000002}/AE Fuel Oil Consumption` Retrieves the same parameter group for multiple vessels.

### The Maker Domain

**Query Expression Format:**

`{imo_number}/{maker_name}{maker_tag_name}`

* `IMO1234567/Enamor/*` Retrieves all time series for vessel IMO1234567 originating from the Enamor system.
* `IMO1234567/Enamor/Water Depth` Retrieves the water depth time series for vessel IMO1234567 originating from the Enamor system.

### The VIS-3-8a Domain

**Query Expression Format:**

`{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}`

The **`vis-3-8a`** domain utilizes the hierarchical GMOD (Generic Product Model) framework to define VIS-paths for vessel items. DNV provides a comprehensive [learning tool](https://vista.dnv.com/learning-tool) for generating VIS-paths and metadata, along with detailed [naming rule documentation](https://docs.vista.dnv.com/docs/standards/dnv-naming-rule).

Wildcards (`*`) can be applied at multiple levels within VIS-paths, as well as at the metadata type and IMO number specifications:

* `*` Retrieves all time series from *all vessels*
* `IMO1234567/411.1` Retrieves all time series under vessel IMO1234567 with primary path root 411.1 ("Propulsion Driver") and arbitrary amounts of descendants in the VIS-path. The latter means that specifying {primary\_item\_vis\_path} = 411.1 is equal to specifying {primary\_item\_vis\_path} = 411.1/\*
* `IMO1234567/400a` Retrieves all time series under vessel IMO1234567 with primary path root being a code that lies within the group 400a ("Propulsion and steering arrangements") in the hierarchy tree, and arbitrary amounts of descendants in the VIS-path.
* `IMO1234567/*/S130-2AMOS` Retrieves all time series for vessel IMO1234567 with arbitrary primary path root and includes S130 (code for "fan unit") anywhere in the VIS-path of their primary item, except root position. Note that the code we require can be both exactly S130 or a code that is under it in the hierarchy, e.g. S130.2. In addition this code is required to have Location 2AMOS.
* `*/*/sec/*/H233` Retrieves all time series from *all vessels* with secondary item VIS-path including H233, and wildcard root.
* `IMO1234567/meta/qty-temperature` Retrieves all time series for vessel IMO1234567 which have the metadata element "qty-temperature".
* `IMO1234567/meta/qty-*` Retrieves all time series for vessel IMO1234567 which have the metadata category "qty" and any metadata type.

#### Notes

* Wildcards in primary or secondary VIS-paths match zero or more path segments, except at the root position where they match one or more segments to ensure a valid root is specified
* You can combine vessel, path, and meta wildcards: `*/400a/*/C663/sec/*/meta/qty-*`
* Use `{IMO1234567, IMO7654321}/*` to query multiple specific vessels

#### Verbose DomainIds

Add the query parameter `?verbose=true` to include human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. This optional parameter is disabled by default and is intended solely to improve readability of the output. Including verbose in the query expression will not affect the resolver behavior.

**Example (verbose=true):**

`IMO1234567/411.1-2P/C101.31-4/~propulsion.engine.2P/~cylinder.4/meta/qty-pressure`

In this example, the verbose segments (prefixed with \~) show that the time series represents the pressure of cylinder 4 in propulsion engine 2 on the port side. The verbose parameter is available across all endpoints and domains, but its use outside the vis-3-8a domain is only useful when querying metadata.

{% hint style="info" %}
**Note:** Wildcards are currently under development, not all functionality is available in all domains.
{% endhint %}

## Finding Domain Mappings

To determine how a time series is represented across different `domains`, query the metadata endpoint using the `id` domain. The response includes a `domains` object for each time series, listing its corresponding identifiers in other domains.

For example, using the following metadata query:

```sh
curl -X GET "https://portal.raalabs.io/{ENVIRONMENT}/metadata/id/*" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

returns metadata for all accessible time series. The response is a JSON array containing metadata objects such as:

```json
    {
        "id": "9830d38a-18f7-4549-8044-48455fe1cee4",
        "timeSeriesId": "9830d38a-18f7-4549-8044-48455fe1cee4",
        "source": "Aconis",
        "dataProvider": "Raa Labs",
        "unitOfMeasure": "bar",
        "scale": 0.01,
        "vessel": {
            "name": "Happy Wanderer",
            "imo": "1000005"
        },
        "domains": {
            "id": {
                "id": "9830d38a-18f7-4549-8044-48455fe1cee4"
            },
            "jsmea": {
                "category": "MainEngine",
                "content": "Compression",
                "id": "IMO1000005/jsmea_mac/MainEngine/Cylinder9/Compression//Press/",
                "item": "Press",
                "namingRule": "jsmea_mac",
                "subcategory": "Cylinder9"
            },
            "maker": {
                "description": "PCOMP CYL 9",
                "id": "IMO1000005/Aconis/CCS009",
                "name": "CCS009",
                "rangeHigh": 1000.0,
                "rangeLow": 0.0
            },
            "raalabs": {
                "id": "IMO1000005/ME CylinderCompressionPress_9",
                "shortName": "ME CylinderCompressionPress_9"
            },
            "vis-3-8a": {
                "id": "IMO1000005/411.1/C101.31-9/meta/qty-pressure/detail-compression",
                "metadata": [
                    {
                        "category": "detail",
                        "type": "compression"
                    },
                    {
                        "category": "qty",
                        "type": "pressure"
                    }
                ],
                "primaryItem": [
                    {
                        "code": "411.1"
                    },
                    {
                        "code": "C101.31",
                        "number": 9
                    }
                ]
            }
        }
    }
```

## Returning DomainIds in Another Domain

The `/measurements` and `/statistics` endpoints accept an `output_domain` query parameter. It sets the domain that the results are named in, independently of the domain that was queried. This lets you query in whichever naming scheme is most convenient, and get the results labelled in the scheme of your choice. Without the parameter, results are named in the domain that was queried.

For example, this query selects a time series by its flat `raalabs` tag name, but asks for JSMEA names in the response:

```sh
curl -X GET "https://portal.raalabs.io/{ENVIRONMENT}/measurements/raalabs/IMO1234567/ME%20ShaftPower?last=1hour&output_domain=jsmea" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "x-api-version: 2026-09-10"
```

It returns the same measurements as the query without `output_domain`, but each `id` is the JSMEA name of the time series instead of the `raalabs` one:

```json
[
  {
    "timestamp": "2025-06-12T07:42:03.361000Z",
    "id": "IMO1234567/jsmea_mac/MainEngine/DrivingShaft/Output//Power/",
    "value": 12351.4
  }
]
```

{% hint style="info" %}
**Note:** A time series that has no name in the requested output domain is left out of the response, so a query can return fewer time series with `output_domain` set than without it. The `id` domain is defined for every time series, so `output_domain=id` never leaves anything out.
{% endhint %}


# Response Codes and Error Handling

The API uses standard HTTP status codes for error reporting, along with a JSON error message in the response body to help diagnose issues.

* `2xx` response codes indicate success, and require no action from the user
* `4xx` response codes indicate a problem with the request, the user can resolve these problems with the help of the error message (see example below)
* `5xx` response codes indicate a problem with the service, and cannot be addressed by the user.

Common error responses include:

| HTTP Status Code               | Meaning                                                                                                                                          |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **200 OK**                     | The request was successful and the response contains the requested data.                                                                         |
| **400 Bad Request**            | The request was malformed or failed validation (e.g., missing required fields, invalid parameter format, etc.).                                  |
| **401 Unauthorized**           | Authentication failed – either the `Authorization` header is missing, or the Bearer token is invalid or expired.                                 |
| **403 Forbidden**              | The user is authorized, but does not have permission to access the requested content.                                                            |
| **404 Not Found**              | The requested resource does not exist. This could mean the endpoint is incorrect or the query is incorrect `{domain}/{expression}`               |
| **406 Not Acceptable**         | The server cannot produce a response matching the list of acceptable values defined in the request's headers (e.g., `Accept` header).            |
| **415 Unsupported Media Type** | Some endpoints expect a `Content-Type` header, see the API endpoints documentation for details.                                                  |
| **429 Too Many Requests**      | The user has sent too many requests in a given amount of time. Retry requests at a later time. This is configurable, contact your administrator. |
| **500 Internal Server Error**  | Internal server error. Please retry your request or contact support if the problem persists.                                                     |
| **503 Service Unavailable**    | The service is temporarily unavailable. Please retry your request after a short delay or contact support if the problem persists.                |

When an error occurs, the response body will typically include a JSON object with an `error` field describing what went wrong. For example:

```json
{
  "error": "Invalid 'to' timestamp: Invalid timestamp or format: 2025-05-25T09:41:00, valid formats are ISO8601 or YYYY-MM-DD"
}
```

In this example, the error message indicates that the `to` query parameter was not in an acceptable format. These messages can help you adjust your request accordingly.


# Examples

Below are some example use-cases and API requests.

{% hint style="info" %}
**Note:** In the example URLs, replace `{ENVIRONMENT}` with your target environment or customer identifier provided by Raa Labs.
{% endhint %}

A typical base URL is `https://portal.raalabs.io/{ENVIRONMENT}/...`. Be sure to include the Authorization header with your access token in each request (omitted in examples for brevity).

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Measurements Endpoint</strong></td><td>Example API requests for the Measurements Endpoint</td><td><a href="/docs/api-endpoints/measurements-endpoint#examples">Measurements Endpoint</a></td></tr><tr><td><strong>Metadata Endpoint</strong></td><td>Example API requests for the Metadata Endpoint</td><td><a href="/docs/api-endpoints/metadata-endpoint#examples">Metadata Endpoint</a></td></tr><tr><td><strong>Statistics Endpoint</strong></td><td>Example API requests for the Statistics Endpoint</td><td><a href="/docs/api-endpoints/statistics-endpoint#examples">Statistics Endpoint</a></td></tr></tbody></table>


# MCP Server

The Raa Labs **MCP server** exposes your fleet's time series data to AI agents over the [Model Context Protocol](https://modelcontextprotocol.io). Once connected, an agent can discover your vessels, look up which sensors are available, and pull aggregated statistics directly in a conversation, without you writing any API calls.

The server is available at:

```
https://mcp.raalabs.io/{ENVIRONMENT}/mcp
```

Replace `{ENVIRONMENT}` with your tenant name: the same one you use in the portal URL `portal.raalabs.io/{ENVIRONMENT}` and in the REST API base URL.

{% hint style="warning" %}
**Availability:** The MCP server is enabled per tenant, and connecting requires an OAuth Client ID issued by Raa Labs for your tenant. To have it enabled, contact <support@raalabs.com>.
{% endhint %}

## MCP or the REST API?

Both serve the same underlying data, and they suit different jobs.

| Use the MCP server when                                                                                      | Use the [REST API](/docs/api-endpoints) when                    |
| ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| You want to explore data conversationally: asking questions, following up, and iterating inside an AI agent. | You are building an application, dashboard, or scheduled job.   |
| You want an agent to find the right sensors for you rather than looking up tag names yourself.               | You need raw, un-aggregated measurements.                       |
| You want quick answers and visualisations without writing code.                                              | You need a specific response format, such as NDJSON or Parquet. |
| You are working ad hoc and do not want to manage tokens in a script.                                         | You need the specific naming standards as input/output.         |

The MCP server is a layer over this same API, so everything it returns follows the API version `2026-09-10` response shapes documented in this space.

## What you can ask

Once connected, the agent has tools for discovering vessels and sensors, querying aggregated statistics, reading time series metadata, and plotting vessel positions. You do not need to know tag names or query syntax in advance, the agent works that out itself. A few examples to start with:

* *"Which vessels do I have access to?"*
* *"What sensors are available on IMO1234567?"*
* *"What was the average main engine shaft power across the fleet last week?"*
* *"Show me where my fleet is right now."*
* *"Compare daily fuel consumption for IMO1234567 and IMO7654321 over the past month."*

{% hint style="info" %}
**Tip:** Narrow broad questions where you can. Asking for every sensor on every vessel over a long period returns a lot of data at once, and a more specific question gives a faster, better answer.
{% endhint %}

## Connecting a client

### What you'll need

| Item                | Description                                                                                                                                    |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Tenant name**     | Your tenant identifier, the same as in your portal URL `portal.raalabs.io/{ENVIRONMENT}`.                                                      |
| **MCP server URL**  | `https://mcp.raalabs.io/{ENVIRONMENT}/mcp`                                                                                                     |
| **OAuth Client ID** | Issued by Raa Labs for your tenant. Required for the Claude connector.                                                                         |
| **API credentials** | Your Client ID and Client Secret, as described under [Authentication](/getting-started/authentication). Required for the Claude Desktop route. |
| **Node.js 18+**     | Required for the Claude Desktop route only, to run `npx`.                                                                                      |

{% hint style="danger" %}
**Important:** Handle your **Client ID** and **Client Secret** with care. **Do not share or expose these credentials** in source code, public repositories, or unsecured locations. If you suspect that a Client Secret has been compromised, contact Raa Labs support immediately to regenerate it.
{% endhint %}

### Setup

{% tabs %}
{% tab title="Claude connector" %}
This is the recommended way to connect. An administrator adds the connector once for the whole organisation, and each team member then activates it with their own sign-in, so data access follows each person's own permissions.

{% hint style="info" %}
**Note:** Adding a connector for the whole organisation requires the **Owner** role on a Claude Team or Enterprise plan. Custom connectors are currently a Beta feature in Claude, so the dialog is labelled accordingly and its wording may change.
{% endhint %}

#### For administrators

1. In Claude, go to **Settings → Connectors**.
2. Click the **+** button at the top of the panel, then choose **Add custom connector**. Do not use **Browse connectors**: that opens Anthropic's pre-built catalogue, which does not include the Raa Labs MCP.
3. Enter a **Name** your team will recognise, for example `Raa Labs MCP`.
4. Enter the **URL** of your tenant's endpoint: `https://mcp.raalabs.io/{ENVIRONMENT}/mcp`
5. Expand **Advanced settings** and paste your **OAuth Client ID**. Leave the **OAuth Client Secret** field **blank**.
6. Under **How your team connects**, keep **Individual sign-in** selected. Do not select Managed authorization.
7. Click **Add**. The connector appears in the Connectors list, tagged `CUSTOM`.

#### For team members

Once an administrator has added the connector it is available to everyone in the organisation, but because it uses individual sign-in each person signs in once:

1. Go to **Settings → Connectors** and find **Raa Labs MCP** in the list.
2. Click **Connect** and complete the sign-in prompt with your Raa Labs account.
3. In a chat, open the tools menu next to the message box and make sure **Raa Labs MCP** is enabled for that conversation.

Claude remembers the connection for future chats. If the tools stop appearing, return to **Settings → Connectors** and reconnect.
{% endtab %}

{% tab title="Claude Desktop" %}
Claude Desktop connects through `mcp-remote`, a local bridge that forwards requests to the MCP server. This route uses your API credentials directly rather than an interactive sign-in.

1. In Claude Desktop, go to **Settings → Developer → Edit Config**. This opens `claude_desktop_config.json`:
   * **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
   * **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
2. Add an `mcpServers` entry. If the file already has other servers, add this entry inside the existing object.

{% code lineNumbers="true" %}

```json
{
  "mcpServers": {
    "raalabs": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.raalabs.io/{ENVIRONMENT}/mcp",
        "--header",
        "Authorization:Basic BASE64_CREDENTIALS"
      ]
    }
  }
}
```

{% endcode %}

Replace the placeholders:

| Placeholder          | What to use                              |
| -------------------- | ---------------------------------------- |
| `{ENVIRONMENT}`      | Your tenant name                         |
| `BASE64_CREDENTIALS` | Base64-encoded `client_id:client_secret` |

Generate the encoded credentials from a terminal:

{% code lineNumbers="true" %}

```bash
echo -n "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" | base64
```

{% endcode %}

3. Fully quit Claude Desktop (**Cmd+Q** on macOS, or right-click the system tray icon and choose **Quit** on Windows) and reopen it.
   {% endtab %}

{% tab title="Other MCP clients" %}
Any MCP-compatible client can connect. The details it needs:

| Property             | Value                                                                                               |
| -------------------- | --------------------------------------------------------------------------------------------------- |
| **Endpoint**         | `https://mcp.raalabs.io/{ENVIRONMENT}/mcp`                                                          |
| **Transport**        | JSON-RPC 2.0 over HTTP. `POST` only. `GET` and `DELETE` return `405 Method Not Allowed`.            |
| **Authentication**   | Standard `Authorization` header, forwarded on every request.                                        |
| **Server name**      | `raalabs-mcp`                                                                                       |
| **Protocol version** | `2026-07-28`. The earlier revisions `2025-11-25`, `2025-06-18`, and `2025-03-26` are also accepted. |

{% hint style="info" %}
**Note:** There is no stdio transport and no server-sent event stream. Clients that expect a stdio server should use a bridge such as `mcp-remote`, as shown in the Claude Desktop tab.
{% endhint %}
{% endtab %}
{% endtabs %}

### Verify the connection

Ask the agent to list your vessels. For example, *"List the vessels available in the Raa Labs MCP"*. It should return your fleet with vessel names and IMO numbers. If it does, you are connected.

### Troubleshooting

| Symptom                                 | What to check                                                                                                                                                                                                                                                           |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Tools don't appear after connecting** | For the connector, confirm it is enabled in the conversation's tools menu. For Claude Desktop, check **Settings → Developer** for error logs, typically a JSON syntax error or `npx` not being found.                                                                   |
| **`npx` not found (Claude Desktop)**    | Claude Desktop launches servers with a minimal `PATH`, so `npx` may not resolve even though it works in your terminal. Use its full path as the `command` value: run `which npx` on macOS or Linux, or point at `npx.cmd` in your Node.js install directory on Windows. |
| **Authentication errors**               | For the connector, confirm the OAuth Client ID is correct and that you completed the individual sign-in. For Claude Desktop, re-run the `base64` command to check your encoded credentials. If a Client ID has been reissued, update it.                                |
| **Errors mentioning a status code**     | The connection is working. This is an error from the underlying API surfaced as a tool result. The status codes and messages follow the [Response Codes and Error Handling](/docs/error-handling) page.                                                                 |
| **No data returned**                    | Confirm your tenant has vessels with data flowing, and that your credentials grant access to them. Ask for your vessel list first to check basic connectivity.                                                                                                          |


# API Reference


# Measurements

The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.

## GET /measurements/id/{ids}

> Get measurements from the id domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"IdTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"IdTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/measurements/id/{ids}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_id","parameters":[{"description":"A comma-separated list of one or more GUIDs","in":"path","name":"ids","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/IdTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/IdTimeseries"}}},"description":"All data points for the times series within the requested time range.","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get measurements from the id domain","tags":["Measurements"]}}}}
```

## GET /measurements/jsmea/{imo\_number}/{naming\_rule}/{category}/{sub\_category}/{content}/{position}/{item}/{modifier}

> Get measurement data from the jsmea domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"JsmeaTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"JsmeaTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/measurements/jsmea/{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_jsmea","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"The naming rule to use for retrieval","in":"path","name":"naming_rule","required":false,"schema":{"type":"string"}},{"description":"The category to retrieve from","in":"path","name":"category","required":false,"schema":{"type":"string"}},{"description":"The sub category to retrieve from","in":"path","name":"sub_category","required":false,"schema":{"type":"string"}},{"description":"The content to retrieve from","in":"path","name":"content","required":false,"schema":{"type":"string"}},{"description":"The position to retrieve from","in":"path","name":"position","required":false,"schema":{"type":"string"}},{"description":"The item to retrieve from","in":"path","name":"item","required":false,"schema":{"type":"string"}},{"description":"The modifier to retrieve from","in":"path","name":"modifier","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsmeaTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/JsmeaTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/JsmeaTimeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get measurement data from the jsmea domain","tags":["Measurements"]}}}}
```

## GET /measurements/maker/{imo\_number}/{maker}/{tag\_name}

> Get measurement data from the maker domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"MakerTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"MakerTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/measurements/maker/{imo_number}/{maker}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_maker","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Name of the maker (data source) that produced the time series","in":"path","name":"maker","required":false,"schema":{"type":"string"}},{"description":"Maker-specific tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakerTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/MakerTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/MakerTimeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get measurement data from the maker domain","tags":["Measurements"]}}}}
```

## Retrieve data from multiple queries

> Used to retrieve data for multiple queries in one request (you provide a list of domain/expression queries in the JSON body).

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"MeasurementBatchQueryRequest":{"description":"Array of domain/expression query strings","items":{"type":"string"},"minItems":1,"title":"MeasurementBatchQueryRequest","type":"array"},"MeasurementBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"MeasurementBatchQueryResponse","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/measurements/query":{"post":{"callbacks":{},"deprecated":false,"description":"Used to retrieve data for multiple queries in one request (you provide a list of domain/expression queries in the JSON body).","operationId":"get_measurements_post","parameters":[{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryRequest"}}},"description":"Batch measurement query request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryResponse"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryResponse"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/MeasurementBatchQueryResponse"}}},"description":"Batch measurement data","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Retrieve data from multiple queries","tags":["Measurements"]}}}}
```

## GET /measurements/raalabs/{imo\_number}/{tag\_name}

> Get measurement data from the raalabs domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"RaalabsTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"RaalabsTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/measurements/raalabs/{imo_number}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_raalabs","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Short descriptive tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaalabsTimeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/RaalabsTimeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/RaalabsTimeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get measurement data from the raalabs domain","tags":["Measurements"]}}}}
```

## GET /measurements/vis-3-8a/{imo\_number}/{primary\_item\_vis\_path}/sec/{secondary\_item\_vis\_path}/meta/{metadata}

> Get measurement data from the vis-3-8a domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Measurements endpoints provide access to raw sensor data (time series measurements) without any aggregation. Each measurement is an instantaneous reading from a sensor at a given timestamp. This is typically the most commonly used endpoint for retrieving time-series data.","name":"Measurements"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"Vis38Timeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"Vis38Timeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/measurements/vis-3-8a/{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_measurements_vis38","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Primary item VIS path","in":"path","name":"primary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Secondary item VIS path","in":"path","name":"secondary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Metadata","in":"path","name":"metadata","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson","parquet"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vis38Timeseries"}},"application/vnd.apache.parquet":{"schema":{"$ref":"#/components/schemas/Vis38Timeseries"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/Vis38Timeseries"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get measurement data from the vis-3-8a domain","tags":["Measurements"]}}}}
```


# Metadata

The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.

## GET /metadata/id/{ids}

> Get metadata from the id domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"IdMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"IdMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/metadata/id/{ids}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_id","parameters":[{"description":"A comma-separated list of one or more GUIDs","in":"path","name":"ids","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdMetadata"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/IdMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get metadata from the id domain","tags":["Metadata"]}}}}
```

## Get metadata from the jsmea domain

> Get metadata from the jsmea domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"JsmeaMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"JsmeaMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/metadata/jsmea/{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}":{"get":{"callbacks":{},"deprecated":false,"description":"Get metadata from the jsmea domain","operationId":"get_metadata_jsmea","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"The naming rule to use for retrieval","in":"path","name":"naming_rule","required":false,"schema":{"type":"string"}},{"description":"The category to retrieve from","in":"path","name":"category","required":false,"schema":{"type":"string"}},{"description":"The sub category to retrieve from","in":"path","name":"sub_category","required":false,"schema":{"type":"string"}},{"description":"The content to retrieve from","in":"path","name":"content","required":false,"schema":{"type":"string"}},{"description":"The position to retrieve from","in":"path","name":"position","required":false,"schema":{"type":"string"}},{"description":"The item to retrieve from","in":"path","name":"item","required":false,"schema":{"type":"string"}},{"description":"The modifier to retrieve from","in":"path","name":"modifier","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsmeaMetadata"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/JsmeaMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get metadata from the jsmea domain","tags":["Metadata"]}}}}
```

## GET /metadata/maker/{imo\_number}/{maker}/{tag\_name}

> Get metadata from the maker domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"MakerMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"MakerMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/metadata/maker/{imo_number}/{maker}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_maker","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Name of the maker (data source) that produced the time series","in":"path","name":"maker","required":false,"schema":{"type":"string"}},{"description":"Maker-specific tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakerMetadata"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/MakerMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get metadata from the maker domain","tags":["Metadata"]}}}}
```

## GET /metadata/raalabs/{imo\_number}/{tag\_name}

> Get metadata from the raalabs domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"RaalabsMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"RaalabsMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/metadata/raalabs/{imo_number}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_raalabs","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Short descriptive tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaalabsMetadata"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/RaalabsMetadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get metadata from the raalabs domain","tags":["Metadata"]}}}}
```

## GET /metadata/vis-3-8a/{imo\_number}/{primary\_item\_vis\_path}/sec/{secondary\_item\_vis\_path}/meta/{metadata}

> Get metadata from the vis-3-8a domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Metadata endpoint provides detailed metadata about time series tags. This includes information such as the human-readable name of the signal, description, units of measure, data source, scaling factor, valid range, and mappings to other naming domains. Use this endpoint to understand what a particular time series represents.","name":"Metadata"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"Vis38Metadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"Vis38Metadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/metadata/vis-3-8a/{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_metadata_vis38","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Primary item VIS path","in":"path","name":"primary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Secondary item VIS path","in":"path","name":"secondary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Metadata","in":"path","name":"metadata","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vis38Metadata"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/Vis38Metadata"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get metadata from the vis-3-8a domain","tags":["Metadata"]}}}}
```


# Statistics

The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.

## GET /statistics/id/{ids}

> Get statistics from the id domain

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"IdStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"IdStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/statistics/id/{ids}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_id","parameters":[{"description":"A comma-separated list of one or more GUIDs","in":"path","name":"ids","required":false,"schema":{"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/IdStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get statistics from the id domain","tags":["Statistics"]}}}}
```

## GET /statistics/jsmea/{imo\_number}/{naming\_rule}/{category}/{sub\_category}/{content}/{position}/{item}/{modifier}

> Get statistics from the jsmea domain

````json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"JsmeaStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"JsmeaStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/statistics/jsmea/{imo_number}/{naming_rule}/{category}/{sub_category}/{content}/{position}/{item}/{modifier}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_jsmea","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"The naming rule to use for retrieval","in":"path","name":"naming_rule","required":false,"schema":{"type":"string"}},{"description":"The category to retrieve from","in":"path","name":"category","required":false,"schema":{"type":"string"}},{"description":"The sub category to retrieve from","in":"path","name":"sub_category","required":false,"schema":{"type":"string"}},{"description":"The content to retrieve from","in":"path","name":"content","required":false,"schema":{"type":"string"}},{"description":"The position to retrieve from","in":"path","name":"position","required":false,"schema":{"type":"string"}},{"description":"The item to retrieve from","in":"path","name":"item","required":false,"schema":{"type":"string"}},{"description":"The modifier to retrieve from","in":"path","name":"modifier","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, delta, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.\n\n<br>\n\nWindows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\n\n<br>\n\nSet `window = none` to disable windowing. A single aggregation covering the full query range is returned.\n\n<br>\n\nNote that in the case where the boundaries of the requested query time range don't align with a time window\nthe result will contain every time window that starts inside the query time range. This means that data after\nthe query range can be included in aggregations if the time window starts inside the query range.\n\nA window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = time window returned by the API\n11:30                                   16:30\n  ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n  ¦   |       |       |       |       |   ¦\n  ¦                                       ¦\n  ¦    [#####] [#####] [#####] [#####] [#####]\n```\n\n<br>\n\nSet `window_edges = partial` to use the query range exactly as given instead. See the `window_edges` parameter.\n","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Controls what happens to the windows at each end of the query time range when the range does not align with\nthe time windows.\n\n<br>\n\nWith `whole` (the default) the range is expanded to whole windows, so the leading partial window is dropped\nand the trailing window is returned in full — meaning it can include data from after `to`.\n\n<br>\n\nWith `partial` the range is used exactly as given, and the windows at each end are returned as partial windows\ncovering only the part that falls inside the query range. A partial window aggregates less data than a whole\none, so compare values across windows with care.\n\nThe first window carries the exact query start as its `timestamp` when it is partial. All other windows are\naligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = whole time window\n[###..] = partial time window, including data from the beginning of the window\n[..###] = partial time window, including data at the end of the window\n\n  11:30                                   16:30\n    ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n    ¦   |       |       |       |       |   ¦\n    ¦                                       ¦\n    ¦    [#####] [#####] [#####] [#####] [#####]   window_edges = whole\n    ¦                                       ¦\n[..###] [#####] [#####] [#####] [#####] [###..]    window_edges = partial\n```\n\nWith `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00`\nand `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.\n\n<br>\n\nThis parameter is ignored when `window = none`, which always uses the query range as given.\n","in":"query","name":"window_edges","required":false,"schema":{"default":"whole","enum":["partial","whole"],"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsmeaStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/JsmeaStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get statistics from the jsmea domain","tags":["Statistics"]}}}}
````

## GET /statistics/maker/{imo\_number}/{maker}/{tag\_name}

> Get statistics from the maker domain

````json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"MakerStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"MakerStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/statistics/maker/{imo_number}/{maker}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_maker","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Name of the maker (data source) that produced the time series","in":"path","name":"maker","required":false,"schema":{"type":"string"}},{"description":"Maker-specific tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, delta, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.\n\n<br>\n\nWindows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\n\n<br>\n\nSet `window = none` to disable windowing. A single aggregation covering the full query range is returned.\n\n<br>\n\nNote that in the case where the boundaries of the requested query time range don't align with a time window\nthe result will contain every time window that starts inside the query time range. This means that data after\nthe query range can be included in aggregations if the time window starts inside the query range.\n\nA window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = time window returned by the API\n11:30                                   16:30\n  ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n  ¦   |       |       |       |       |   ¦\n  ¦                                       ¦\n  ¦    [#####] [#####] [#####] [#####] [#####]\n```\n\n<br>\n\nSet `window_edges = partial` to use the query range exactly as given instead. See the `window_edges` parameter.\n","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Controls what happens to the windows at each end of the query time range when the range does not align with\nthe time windows.\n\n<br>\n\nWith `whole` (the default) the range is expanded to whole windows, so the leading partial window is dropped\nand the trailing window is returned in full — meaning it can include data from after `to`.\n\n<br>\n\nWith `partial` the range is used exactly as given, and the windows at each end are returned as partial windows\ncovering only the part that falls inside the query range. A partial window aggregates less data than a whole\none, so compare values across windows with care.\n\nThe first window carries the exact query start as its `timestamp` when it is partial. All other windows are\naligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = whole time window\n[###..] = partial time window, including data from the beginning of the window\n[..###] = partial time window, including data at the end of the window\n\n  11:30                                   16:30\n    ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n    ¦   |       |       |       |       |   ¦\n    ¦                                       ¦\n    ¦    [#####] [#####] [#####] [#####] [#####]   window_edges = whole\n    ¦                                       ¦\n[..###] [#####] [#####] [#####] [#####] [###..]    window_edges = partial\n```\n\nWith `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00`\nand `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.\n\n<br>\n\nThis parameter is ignored when `window = none`, which always uses the query range as given.\n","in":"query","name":"window_edges","required":false,"schema":{"default":"whole","enum":["partial","whole"],"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakerStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/MakerStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get statistics from the maker domain","tags":["Statistics"]}}}}
````

## Retrieve multiple series

> Used to retrieve multiple series in one request (you provide a list of domain/expression queries in the JSON body).

````json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"StatisticsBatchQueryRequest":{"description":"Array of domain/expression query strings","items":{"type":"string"},"minItems":1,"title":"StatisticsBatchQueryRequest","type":"array"},"StatisticsBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"StatisticsBatchQueryResponse","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/statistics/query":{"post":{"callbacks":{},"deprecated":false,"description":"Used to retrieve multiple series in one request (you provide a list of domain/expression queries in the JSON body).","operationId":"get_statistics_post","parameters":[{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, delta, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.\n\n<br>\n\nWindows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\n\n<br>\n\nSet `window = none` to disable windowing. A single aggregation covering the full query range is returned.\n\n<br>\n\nNote that in the case where the boundaries of the requested query time range don't align with a time window\nthe result will contain every time window that starts inside the query time range. This means that data after\nthe query range can be included in aggregations if the time window starts inside the query range.\n\nA window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = time window returned by the API\n11:30                                   16:30\n  ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n  ¦   |       |       |       |       |   ¦\n  ¦                                       ¦\n  ¦    [#####] [#####] [#####] [#####] [#####]\n```\n\n<br>\n\nSet `window_edges = partial` to use the query range exactly as given instead. See the `window_edges` parameter.\n","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Controls what happens to the windows at each end of the query time range when the range does not align with\nthe time windows.\n\n<br>\n\nWith `whole` (the default) the range is expanded to whole windows, so the leading partial window is dropped\nand the trailing window is returned in full — meaning it can include data from after `to`.\n\n<br>\n\nWith `partial` the range is used exactly as given, and the windows at each end are returned as partial windows\ncovering only the part that falls inside the query range. A partial window aggregates less data than a whole\none, so compare values across windows with care.\n\nThe first window carries the exact query start as its `timestamp` when it is partial. All other windows are\naligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = whole time window\n[###..] = partial time window, including data from the beginning of the window\n[..###] = partial time window, including data at the end of the window\n\n  11:30                                   16:30\n    ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n    ¦   |       |       |       |       |   ¦\n    ¦                                       ¦\n    ¦    [#####] [#####] [#####] [#####] [#####]   window_edges = whole\n    ¦                                       ¦\n[..###] [#####] [#####] [#####] [#####] [###..]    window_edges = partial\n```\n\nWith `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00`\nand `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.\n\n<br>\n\nThis parameter is ignored when `window = none`, which always uses the query range as given.\n","in":"query","name":"window_edges","required":false,"schema":{"default":"whole","enum":["partial","whole"],"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatisticsBatchQueryRequest"}}},"description":"Batch statistics query request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatisticsBatchQueryResponse"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/StatisticsBatchQueryResponse"}}},"description":"Batch statistics data","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Retrieve multiple series","tags":["Statistics"]}}}}
````

## GET /statistics/raalabs/{imo\_number}/{tag\_name}

> Get statistics from the raalabs domain

````json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"RaalabsStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"RaalabsStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/statistics/raalabs/{imo_number}/{tag_name}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_raalabs","parameters":[{"description":"The IMO number of the vessel","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Short descriptive tag name","in":"path","name":"tag_name","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, delta, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.\n\n<br>\n\nWindows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\n\n<br>\n\nSet `window = none` to disable windowing. A single aggregation covering the full query range is returned.\n\n<br>\n\nNote that in the case where the boundaries of the requested query time range don't align with a time window\nthe result will contain every time window that starts inside the query time range. This means that data after\nthe query range can be included in aggregations if the time window starts inside the query range.\n\nA window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = time window returned by the API\n11:30                                   16:30\n  ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n  ¦   |       |       |       |       |   ¦\n  ¦                                       ¦\n  ¦    [#####] [#####] [#####] [#####] [#####]\n```\n\n<br>\n\nSet `window_edges = partial` to use the query range exactly as given instead. See the `window_edges` parameter.\n","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Controls what happens to the windows at each end of the query time range when the range does not align with\nthe time windows.\n\n<br>\n\nWith `whole` (the default) the range is expanded to whole windows, so the leading partial window is dropped\nand the trailing window is returned in full — meaning it can include data from after `to`.\n\n<br>\n\nWith `partial` the range is used exactly as given, and the windows at each end are returned as partial windows\ncovering only the part that falls inside the query range. A partial window aggregates less data than a whole\none, so compare values across windows with care.\n\nThe first window carries the exact query start as its `timestamp` when it is partial. All other windows are\naligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = whole time window\n[###..] = partial time window, including data from the beginning of the window\n[..###] = partial time window, including data at the end of the window\n\n  11:30                                   16:30\n    ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n    ¦   |       |       |       |       |   ¦\n    ¦                                       ¦\n    ¦    [#####] [#####] [#####] [#####] [#####]   window_edges = whole\n    ¦                                       ¦\n[..###] [#####] [#####] [#####] [#####] [###..]    window_edges = partial\n```\n\nWith `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00`\nand `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.\n\n<br>\n\nThis parameter is ignored when `window = none`, which always uses the query range as given.\n","in":"query","name":"window_edges","required":false,"schema":{"default":"whole","enum":["partial","whole"],"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaalabsStatistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/RaalabsStatistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get statistics from the raalabs domain","tags":["Statistics"]}}}}
````

## GET /statistics/vis-3-8a/{imo\_number}/{primary\_item\_vis\_path}/sec/{secondary\_item\_vis\_path}/meta/{metadata}

> Get statistics from the vis-3-8a domain

````json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"tags":[{"description":"The Statistics endpoint provides aggregated metrics calculated from time series data, over specified time windows. This allows you to query values such as averages, minima, maxima, counts, standard deviation, etc., instead of raw data points. It is useful for downsampling data or getting summary insights over time intervals.","name":"Statistics"}],"servers":[{"description":"API Server","url":"https://prism.raalabs.io/{ENVIRONMENT}","variables":{}}],"security":[{"authorization":[]}],"components":{"securitySchemes":{"authorization":{"scheme":"bearer","type":"http"}},"schemas":{"Vis38Statistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"Vis38Statistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"},"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"},"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"},"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}},"paths":{"/statistics/vis-3-8a/{imo_number}/{primary_item_vis_path}/sec/{secondary_item_vis_path}/meta/{metadata}":{"get":{"callbacks":{},"deprecated":false,"operationId":"get_statistics_vis38","parameters":[{"description":"The IMO number of the vessel to retrieve from","in":"path","name":"imo_number","required":false,"schema":{"type":"string"}},{"description":"Primary item VIS path","in":"path","name":"primary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Secondary item VIS path","in":"path","name":"secondary_item_vis_path","required":false,"schema":{"type":"string"}},{"description":"Metadata","in":"path","name":"metadata","required":false,"schema":{"type":"string"}},{"description":"From date-time","in":"query","name":"from","schema":{"format":"date-time","type":"string"}},{"description":"To date-time","in":"query","name":"to","schema":{"format":"date-time","type":"string"}},{"description":"From epoch. ex: 0","in":"query","name":"from_epoch","schema":{"type":"integer"}},{"description":"To epoch. ex: 100000","in":"query","name":"to_epoch","schema":{"type":"integer"}},{"description":"Since returns data going back. ex: '1day' from now","in":"query","name":"since","schema":{"type":"string"}},{"description":"Last returns data going back. ex: '1day' from the last full day","in":"query","name":"last","schema":{"type":"string"}},{"description":"Sets the year for which data should be retrieved. ex: 2025","in":"query","name":"year","schema":{"type":"number"}},{"description":"Sets the month for which data should be retrieved. ex: 8","in":"query","name":"month","schema":{"type":"number"}},{"description":"Sets the day for which data should be retrieved. ex: 1","in":"query","name":"day","schema":{"type":"number"}},{"description":"A comma-separated list of aggregation functions to compute for each time window. You can request multiple metrics at once. Valid values: avg, count, delta, first_time, first_val, integral, kurtosis, last_time, last_val, max, mean, min, skewness, stddev, sum, var","in":"query","name":"aggregations","schema":{"default":"avg","type":"string"}},{"description":"The duration of each aggregation window. This is given as a time interval literal, e.g. `1m` (1 minute), `2h` (2 hours), `2d` (2 days), etc. All data within each window is used to calculate the aggregations. The default window is `1m` if not specified.\n\n<br>\n\nWindows are aligned to a fixed global timeline, so boundaries are consistent across all queries. For example, with `window = 1h`, windows always start at whole hours (12:00, 13:00, …).\n\n<br>\n\nSet `window = none` to disable windowing. A single aggregation covering the full query range is returned.\n\n<br>\n\nNote that in the case where the boundaries of the requested query time range don't align with a time window\nthe result will contain every time window that starts inside the query time range. This means that data after\nthe query range can be included in aggregations if the time window starts inside the query range.\n\nA window is included if its **start time** is within the query range, even if it extends past the end. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = time window returned by the API\n11:30                                   16:30\n  ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n  ¦   |       |       |       |       |   ¦\n  ¦                                       ¦\n  ¦    [#####] [#####] [#####] [#####] [#####]\n```\n\n<br>\n\nSet `window_edges = partial` to use the query range exactly as given instead. See the `window_edges` parameter.\n","in":"query","name":"window","schema":{"default":"1month","type":"string"}},{"description":"Controls what happens to the windows at each end of the query time range when the range does not align with\nthe time windows.\n\n<br>\n\nWith `whole` (the default) the range is expanded to whole windows, so the leading partial window is dropped\nand the trailing window is returned in full — meaning it can include data from after `to`.\n\n<br>\n\nWith `partial` the range is used exactly as given, and the windows at each end are returned as partial windows\ncovering only the part that falls inside the query range. A partial window aggregates less data than a whole\none, so compare values across windows with care.\n\nThe first window carries the exact query start as its `timestamp` when it is partial. All other windows are\naligned to the usual global timeline. For instance if `window = 1h` and the query range is from `11:30` to `16:30`:\n\n```\n[#####] = whole time window\n[###..] = partial time window, including data from the beginning of the window\n[..###] = partial time window, including data at the end of the window\n\n  11:30                                   16:30\n    ↓ 12:00   13:00   14:00   15:00   16:00 ↓\n    ¦   |       |       |       |       |   ¦\n    ¦                                       ¦\n    ¦    [#####] [#####] [#####] [#####] [#####]   window_edges = whole\n    ¦                                       ¦\n[..###] [#####] [#####] [#####] [#####] [###..]    window_edges = partial\n```\n\nWith `window_edges = partial` the returned `timestamp` values are `11:30`, `12:00`, `13:00`, `14:00`, `15:00`\nand `16:00`, where the `11:30` window covers `11:30`–`12:00` and the `16:00` window covers `16:00`–`16:30`.\n\n<br>\n\nThis parameter is ignored when `window = none`, which always uses the query range as given.\n","in":"query","name":"window_edges","required":false,"schema":{"default":"whole","enum":["partial","whole"],"type":"string"}},{"description":"Desired output format of the data. You can also specify the response format via the HTTP Accept Header instead of the format query parameter.","in":"query","name":"format","schema":{"default":"json","enum":["json","ndjson"],"type":"string"}},{"description":"When true, includes human-readable names for the corresponding VIS path elements in vis-3-8a domainIds. If false (or omitted), regular domainIds are returned.","in":"query","name":"verbose","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Returns the ids of the matched time series in this naming domain instead of the domain that was queried. For\nexample, query the `raalabs` domain and get `vis-3-8a` ids back. Defaults to the queried domain.\n\n<br>\n\nA time series that has no name in the requested output domain is left out of the response, so a query can\nreturn fewer series with `output_domain` set than without it. The `id` domain is defined for every time series.\n","in":"query","name":"output_domain","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vis38Statistics"}},"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/Vis38Statistics"}}},"description":"Response from the route","headers":{"api-version":{"description":"API version","required":true,"schema":{"type":"string"},"style":"simple"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundErrorresponse"}}},"description":"Not Found"}},"summary":"Get statistics from the vis-3-8a domain","tags":["Statistics"]}}}}
````


# Models

## The BadRequestResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"BadRequestResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequestResponse","type":"object"}}}}
```

## The IdMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"IdMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"IdMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"}}}}
```

## The IdStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"IdStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"IdStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The IdTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"IdTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"IdTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The JsmeaMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"JsmeaMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"JsmeaMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"}}}}
```

## The JsmeaStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"JsmeaStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"JsmeaStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The JsmeaTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"JsmeaTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"JsmeaTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The MakerMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"MakerMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"MakerMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"}}}}
```

## The MakerStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"MakerStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"MakerStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The MakerTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"MakerTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"MakerTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The Measurement object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The MeasurementBatchQueryRequest object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"MeasurementBatchQueryRequest":{"description":"Array of domain/expression query strings","items":{"type":"string"},"minItems":1,"title":"MeasurementBatchQueryRequest","type":"array"}}}}
```

## The MeasurementBatchQueryResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"MeasurementBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"MeasurementBatchQueryResponse","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The MetadataItem object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"}}}}
```

## The NotFoundErrorresponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"NotFoundErrorresponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFoundErrorresponse","type":"object"}}}}
```

## The RaalabsMetadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"RaalabsMetadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"RaalabsMetadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"}}}}
```

## The RaalabsStatistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"RaalabsStatistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"RaalabsStatistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The RaalabsTimeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"RaalabsTimeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"RaalabsTimeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```

## The Statistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The StatisticsBatchQueryRequest object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"StatisticsBatchQueryRequest":{"description":"Array of domain/expression query strings","items":{"type":"string"},"minItems":1,"title":"StatisticsBatchQueryRequest","type":"array"}}}}
```

## The StatisticsBatchQueryResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"StatisticsBatchQueryResponse":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"StatisticsBatchQueryResponse","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The UnauthorizedErrorResponse object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"UnauthorizedErrorResponse":{"properties":{"error":{"description":"Error message","type":"string"}},"title":"UnauthorizedErrorResponse","type":"object"}}}}
```

## The Vis38Metadata object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"Vis38Metadata":{"items":{"$ref":"#/components/schemas/MetadataItem"},"title":"Vis38Metadata","type":"array"},"MetadataItem":{"properties":{"dataProvider":{"type":"string"},"domains":{"additionalProperties":true,"type":"object"},"id":{"type":"string"},"scale":{"type":"number"},"source":{"type":"string"},"timeSeriesId":{"format":"uuid","type":"string"},"unitOfMeasure":{"type":"string"},"vessel":{"properties":{"imo":{"type":"string"},"name":{"type":"string"}},"type":"object"}},"title":"MetadataItem","type":"object"}}}}
```

## The Vis38Statistics object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"Vis38Statistics":{"items":{"$ref":"#/components/schemas/Statistics"},"title":"Vis38Statistics","type":"array"},"Statistics":{"properties":{"aggregations":{"properties":{"avg":{"type":"number"},"max":{"type":"number"},"mean":{"type":"number"},"min":{"type":"number"}},"type":"object"},"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"}},"title":"Statistics","type":"object"}}}}
```

## The Vis38Timeseries object

```json
{"openapi":"3.0.0","info":{"title":"Raa Labs Time Series API","version":"2026-09-10"},"components":{"schemas":{"Vis38Timeseries":{"items":{"$ref":"#/components/schemas/Measurement"},"title":"Vis38Timeseries","type":"array"},"Measurement":{"properties":{"id":{"type":"string"},"timestamp":{"format":"date-time","type":"string"},"value":{"type":"number"}},"title":"Measurement","type":"object"}}}}
```


# Support

Get support for functionality or technical questions related to the API by contacting Raa Labs at <support@raalabs.com>.


