> For the complete documentation index, see [llms.txt](https://docs.raalabs.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.raalabs.io/docs/api-endpoints/measurements-endpoint.md).

# 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

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.
{% 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.
