> 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/statistics-endpoint.md).

# 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

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