HTTP API#
Overview#
The HTTP API allows you to interact with the different assets on a site in a request-response manner. You can query historical and live data, as well as send commands to control the assets. Data is exchanged using JSON format.
The data available over this API is cleaned, preprocessed and aggregated on our side to ensure consistency and reliability. As a result, the freshest data available here is typically 5-10 minutes old. Any steering commands are of course still realtime applied. If you have need for lower latency, you should consider using our MQTT API, which provides data with minimal delay.
We have two environments available to partners:
| Environment | Host |
|---|---|
Production | https://api.octave.energy |
Integration | https://api.integration.octave.energy |
The integration environment is used for testing and development purposes, allowing you to safely interact with the API without affecting production data.
Security#
The HTTP API is protected via mTLS and an API key mechanism. Clients must present a valid client certificate and include the API key in the request headers to authenticate and authorize access. See the Credentials setup section for more details.
There are rate limits in place to prevent abuse and ensure fair usage. In case you encounter problems related to rate limits, please contact us.
OpenAPI Specification#
The HTTP API is fully described by an OpenAPI specification, which can be found here.
Data conventions#
Data is available up to 1 year in the past.
Metrics exposed via this API are time series data, processed, sampled and aggregated by us to ensure consistency and reliability. Depending on which metric, they are available per asset/device and/or aggregated per plant.
Data can be queried either per-minute or per-quarter-hour. If you request data per minute, you can retrieve up to 1 day of data in a single request. If you request data per quarter-hour, you can retrieve up to 15 days of data in a single request.
Metrics which represent percentages (typically named with a _pct suffix) are expressed as values between 0 and 100.
We zero-fill missing data points to maintain a consistent response format. This means that if you request e.g. a timerange of 4 minutes of data and some of the minutes have no recorded values, an entry for those minutes will still appear in the response with a value of None. We do not interpolate any missing data.
Getting started#
Below are some example scripts demonstrating how to interact with the HTTP API using Python, in the typical order you would perform these actions. They cover asset discovery, reading metrics, and steering assets. You can run this locally via uv run <script_name.py>.
The scripts assume you have valid credentials, so you have an api key and the necessary client certificates available on your system. You'll need to adjust the script to point to the correct file paths for your API key and client certificates, or put them in the location where the script expects them to be.
Check the OpenAPI specification
The examples below demonstrate some endpoints and typical usage patterns, in order to help you get started with the API. They do not cover all possible endpoints, available metrics, parameters ... We show only one example of each type of endpoint. For a complete reference of all available endpoints, consult the OpenAPI specification.
Site information#
First you can use this endpoint to get a list of sites which your account has access to.
Example response (values are illustrative):
[
{
"site_id": "43a69a32-cc1a-449f-a80c-2f547a6cbef0",
"label": "Octave Office 3",
"latitude": 51.052548,
"longitude": 4.447758
},
{
"site_id": "b89e5b7c-8e73-4a80-bfc6-1b5e81a62cb6",
"label": "Octave Office 1",
"latitude": 51.050278,
"longitude": 4.444874
}
]
You will need the site_id values from this response to interact with other endpoints in the API.
Asset discovery#
In each of these response below, the bess_index/pv_index/meter_index uniquely identifies the respective asset within the site. This is used to reference a specific asset when querying or controlling it via the API.
In order to discover the different assets available at a site, each with some of their attributes:
BESS#
Example response (values are illustrative):
{
"site_id": "43a69a32-cc1a-449f-a80c-2f547a6cbef0",
"bess": [
{
"bess_index": 1,
"serial_number": null,
"vendor_model_reference": "OCTAVE_ONE_PLUS",
"label": null,
"max_charge_power_kw": 125.0,
"max_discharge_power_kw": 125.0,
"min_soc": 0.03,
"max_soc": 1.0,
"capacity_kwh": 261.0
}
]
}
Site Power Meter#
| Get site power meter info | |
|---|---|
Example response (values are illustrative):
{
"site_id": "4d15479f-8add-4691-bcff-e7bf3009a6cc",
"power_meters": [
{
"meter_index": 1,
"meter_type": "SITE",
"label": null,
"data_transfer_type": "modbus_tcp",
"vendor_model_reference": null,
"serial_number": null,
"network_host": "10.0.30.3",
"network_mac_address": "00:80:67:90:ee:34"
}
]
}
Solar/PV#
| Get Solar/PV info | |
|---|---|
Example response (values are illustrative):
{
"site_id": "4d15479f-8add-4691-bcff-e7bf3009a6cc",
"pvs": [
{
"pv_index": 1,
"label": null,
"data_transfer_type": "modbus_tcp",
"vendor_model_reference": "Huawei - Smart Logger 3000",
"serial_number": null,
"network_host": "10.0.30.99",
"network_mac_address": "a4:6d:a4:2d:66:83",
"is_controlled": false
}
]
}
Site configuration#
This endpoint returns the configuration of a site, such as its grid injection and offtake limits, and peak shaving target.
The response is a list of configuration change events, not the current state. To determine the configuration at any point in time, forward-fill the events: a configuration stays valid until the next event. To make this possible, the response always includes the last event before start_timestamp, so you know the configuration that was active at the start of your range. end_timestamp is optional.
Example response (values are illustrative):
{
"site_id": "97cae0aa-80f3-49d1-9d8e-395b59f5f721",
"config_change_events": [
{
"timestamp": "2026-08-01T10:15:00Z",
"site_injection_capacity_kw": 500.0,
"site_offtake_capacity_kw": 800.0,
"site_max_current_per_phase_a": null,
"site_peak_shaving_target_kw": null
},
{
"timestamp": "2026-09-10T08:00:00Z",
"site_injection_capacity_kw": 500.0,
"site_offtake_capacity_kw": 750.0,
"site_max_current_per_phase_a": 250.0,
"site_peak_shaving_target_kw": 600.0
}
]
}
Get alerts for site#
You can query the alerts for a specific site within a given time range using the following API call. We will return all alerts active during the specified time range, so you can get back alerts that started before the range but are still active within it (and same at the end).
Example response (values are illustrative):
{
"site_id": "97cae0aa-80f3-49d1-9d8e-395b59f5f721",
"alerts": [
{
"start_time": "2026-08-28T07:43:20Z",
"end_time": "2026-08-28T09:08:20Z",
"current_status": "resolved",
"severity": "ERROR",
"name": "BESS \u2014 PCS Alert",
"description": "This alert is triggered when the PCS is in fault, for any inverter in the BESS plant. As a result, the BESS may operate sub-optimally, at a reduced power rate, or become completely unavailable.\r\n\r\nThe alert is evaluated every 5 minutes.",
"documentation_link": "https://ems.octave.energy/alerts/alert-bess-pcs",
"bess_index": 0,
"sn_device": "120449"
},
{
"start_time": "2026-09-01T17:03:20Z",
"end_time": "2026-09-01T18:23:20Z",
"current_status": "resolved",
"severity": "ERROR",
"name": "BESS \u2014 PCS Alert",
"description": "This alert is triggered when the PCS is in fault, for any inverter in the BESS plant. As a result, the BESS may operate sub-optimally, at a reduced power rate, or become completely unavailable.\r\n\r\nThe alert is evaluated every 5 minutes.",
"documentation_link": "https://ems.octave.energy/alerts/alert-bess-pcs",
"bess_index": 0,
"sn_device": "120449"
}
]
}
Get metrics#
Most metrics endpoints work in a very similar way, we will illustrate this with an example below. Generally all endpoints ending in /metrics follow this pattern.
To query metrics you need to provide the query parameter period to indicate if you want to have the data per minute or per 15-minute interval. The period parameter is in seconds, so for a per-minute interval you would use 60, and for a per 15-minute interval you would use 900.
Example response (values are illustrative):
{
"site_id": "97cae0aa-80f3-49d1-9d8e-395b59f5f721",
"start_timestamp": "2026-09-22T23:20:00Z",
"end_timestamp": "2026-09-22T23:30:00Z",
"period_seconds": 60,
"metrics": [
{
"bess_index": 1,
"name": "active_power_in_kw",
"values": [
2.54,
0.11,
0.0,
0.0,
0.0,
1.92,
2.3,
0.61,
0.24,
0.77,
0.01
]
},
{
"bess_index": 1,
"name": "active_power_out_kw",
"values": [
0.0,
0.0,
-1.49,
-0.02,
-1.62,
-0.03,
0.0,
0.0,
0.0,
0.0,
-6.85
]
}
]
}
The values array contains the metric values for each period specified in the request. We do not repeat the period timestamp in the response, this can be deducted based on the start_timestamp, period choice, and index in the array.
Get status events#
Status endpoints provide information about the state changes of various components within the site. Depending on the endpoints, status events can be queried per device/asset or aggregated per site.
Any state change that occurred between the start_timestamp and end_timestamp specified in the request will be included in the response. As opposed to metrics data, the timestamp of the status events are not resampled. The last event that occurred before the requested from date is also included, as a convenience to enable determining the state at the beginning of the time period.
Example response (values are illustrative):
{
"site_id": "89ac3726-d1d0-4f9d-a6fc-1a5c68d639bb",
"operating_vendor_state_events": [],
"operating_octave_state_events": [
{
"bess_index": 1,
"timestamp": "2026-09-23T00:00:00Z",
"operating_octave_state": "OFF"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:46:57.756000Z",
"operating_octave_state": "UNKNOWN"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:47:17.009000Z",
"operating_octave_state": "ERROR"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:47:29.045000Z",
"operating_octave_state": "OFF"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:47:30.059000Z",
"operating_octave_state": "PRECHARGE"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:47:49.478000Z",
"operating_octave_state": "STANDBY"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:48:00.512000Z",
"operating_octave_state": "STARTING"
},
{
"bess_index": 1,
"timestamp": "2026-09-23T07:49:01.244000Z",
"operating_octave_state": "GRID_CONNECTED"
}
],
"vendor_fault_state_events": [],
"vendor_fault_code_events": []
}
Set peak shaving target#
You can set a peak shaving target for the site:
Example response (values are illustrative):
You can clear the peak shaving target using the same endpoint by setting the target to null:
Example response (values are illustrative):
You can get the peak shaving target using the site configuration endpoint mentioned above, for example:
After doing the requests above (setting a peak shaving target and then clearing it), this is a typical response:
{
"site_id": "b89e5b7c-8e73-4a80-bfc6-1b5e81a62cb6",
"config_change_events": [
{
"timestamp": "2026-10-08T16:56:39Z",
"site_injection_capacity_kw": 35.0,
"site_offtake_capacity_kw": 45.0,
"site_max_current_per_phase_a": 55.0,
"site_peak_shaving_target_kw": 46.0
},
{
"timestamp": "2026-10-08T20:46:09Z",
"site_injection_capacity_kw": 35.0,
"site_offtake_capacity_kw": 45.0,
"site_max_current_per_phase_a": 55.0,
"site_peak_shaving_target_kw": 48.0
},
{
"timestamp": "2026-10-08T20:49:53Z",
"site_injection_capacity_kw": 35.0,
"site_offtake_capacity_kw": 45.0,
"site_max_current_per_phase_a": 55.0,
"site_peak_shaving_target_kw": null
}
]
}