rebase.energy

How to create a timeseries

Define timeseries types inline on first use, create them on objects or standalone, and link them to objects.

Timeseries are first-class on the platform — every series has an id, whether or not it's attached to an object. There are two ways to create one: directly on an object (the shortcut — create and link in one call), or standalone, linked to objects later or never.

  • POST /platform/v3/objects/sites/{siteId}/timeseries — define a new type inline and/or write points (shortcut: creates the series and links it to the object in one call).
  • POST /platform/v3/objects/sites/{siteId}/timeseries/{type} — append points to an existing series.
  • GET /platform/v3/objects/sites/{siteId}/timeseries — list series on an object.
  • POST /platform/v3/timeseries — create a standalone series, not attached to any object.
  • GET /platform/v3/timeseries — list every series in the org, standalone or linked.
  • POST /platform/v3/timeseries/{timeseriesId}/link — link a series to an object.

A timeseries typepower, temperature, power-forecast — is defined the first time you use it, by passing shape, unit, and (when it produces more than one value per timestamp) columns along with the first write. After that the type is known platform-wide and you reference it by name only. Types come in two shapes:

  • Flat — one value per valid_time. Measurements: power, temperature.
  • Multiindex — values also carry a knowledge_time (when the value was produced — for forecasts, when the workflow run that wrote it executed). Forecasts are the canonical case: a new run produces a fresh set of future timestamps each time.

By default each point has a single value. A series that produces several values per timestamp — like the quantiles of a forecast — declares them as columns, and each one appears as a key in every point.

Define a flat series on first use

The first write carries the type definition. Here power is created as a flat series in kW:

import requests

site_id = "4dbb3433-402f-4276-8a55-7f27753f70dc"
url = f"https://api.rebaseenergy.dev/platform/v3/objects/sites/{site_id}/timeseries"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "type": "power",
    "shape": "flat",
    "unit": "kW",
    "points": [
        {"valid_time": "2026-06-03T06:00:00Z", "value": 5400},
    ],
}

response = requests.post(url, headers=headers, json=body)
print(response.json())

Flat points carry only valid_time and value. A temperature type ("unit": "degC") is defined the same way.

Define a multiindex series on first use

Forecasts are the canonical multiindex case. power-forecast declares three output columns — the quantiles q10, q50, and q90:

import requests

site_id = "4dbb3433-402f-4276-8a55-7f27753f70dc"
url = f"https://api.rebaseenergy.dev/platform/v3/objects/sites/{site_id}/timeseries"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "type": "power-forecast",
    "shape": "multiindex",
    "unit": "kW",
    "columns": ["q10", "q50", "q90"],
    "points": [
        {"valid_time": "2026-06-03T07:00:00Z", "knowledge_time": "2026-06-03T06:00:00Z",
         "q10": 4200, "q50": 5100, "q90": 6300},
    ],
}

response = requests.post(url, headers=headers, json=body)
print(response.json())

Points are columnar: every key other than valid_time and knowledge_time is one of the declared columns, so a single row carries the whole quantile fan. Every multiindex point still requires a knowledge_time. In practice you rarely write forecast points by hand — they usually arrive as published workflow runs, where the run's knowledge_time is stamped onto every point for you (see create a workflow).

A second multiindex example: iceloss-forecast

A series can be multiindex with just a single value column — the knowledge_time axis alone is what makes it multiindex. Omit columns and each point carries a plain value:

import requests

site_id = "4dbb3433-402f-4276-8a55-7f27753f70dc"
url = f"https://api.rebaseenergy.dev/platform/v3/objects/sites/{site_id}/timeseries"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "type": "iceloss-forecast",
    "shape": "multiindex",
    "unit": "fraction",
    "points": [
        {"valid_time": "2026-06-03T07:00:00Z", "value": 0.08,
         "knowledge_time": "2026-06-03T06:00:00Z"},
    ],
}

response = requests.post(url, headers=headers, json=body)
print(response.json())

Reuse a type by name

Once defined anywhere, a type is referenced by name only — no shape, unit, or columns needed. Here temperature lands on a met-mast:

import requests

mast_id = "7a9e1c20-3b44-4f51-9c2a-1f0d8e6b4455"
url = f"https://api.rebaseenergy.dev/platform/v3/objects/met-masts/{mast_id}/timeseries"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "type": "temperature",
    "points": [
        {"valid_time": "2026-06-03T06:00:00Z", "value": -4.2},
    ],
}

response = requests.post(url, headers=headers, json=body)
print(response.json())

Append more points later

Once a series exists on an object, write to it directly via the /{type} route:

import requests

site_id = "4dbb3433-402f-4276-8a55-7f27753f70dc"
url = f"https://api.rebaseenergy.dev/platform/v3/objects/sites/{site_id}/timeseries/power"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "points": [
        {"valid_time": "2026-06-03T07:00:00Z", "value": 5650},
    ],
}

response = requests.post(url, headers=headers, json=body)
print(response.json())

A point's identity is its valid_time for flat series and (valid_time, knowledge_time) for multiindex. Writing the same key again replaces the row — an upsert, not a duplicate.

Create a standalone timeseries

Not every series belongs to a physical object. A market price belongs to a bidding zone, not to any one site:

import requests

url = "https://api.rebaseenergy.dev/platform/v3/timeseries"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "name": "Spot price SE2",
    "type": "spot-price",
    "shape": "flat",
    "unit": "EUR/MWh",
    "points": [
        {"valid_time": "2026-06-03T06:00:00Z", "value": 41.7},
    ],
}

response = requests.post(url, headers=headers, json=body)
print(response.json())
{
  "id": "c63d2f0a-8e15-4b7c-9d04-3a6e8f12b9c1",
  "name": "Spot price SE2",
  "type": "spot-price",
  "shape": "flat",
  "unit": "EUR/MWh",
  "linked_to": []
}

The series now exists in its own right. Read and write it at /platform/v3/timeseries/{timeseriesId} with the same parameters as the object routes — type definition inline on first use works here too.

Linking makes a standalone series appear on an object, under its type name, alongside everything created there directly:

import requests

ts_id = "c63d2f0a-8e15-4b7c-9d04-3a6e8f12b9c1"
url = f"https://api.rebaseenergy.dev/platform/v3/timeseries/{ts_id}/link"
headers = {"Authorization": "Bearer <your_api_key>"}

body = {
    "target_type": "site",
    "target_id": "4dbb3433-402f-4276-8a55-7f27753f70dc",
}

response = requests.post(url, headers=headers, json=body)
print(response.json())

From now on GET /platform/v3/objects/sites/{siteId}/timeseries/spot-price works like any other series on the site. Link the same series to as many objects as you like — a regional price or weather feed can serve a whole portfolio while its data lives in one place.

In fact, the object route used throughout this guide is exactly this, as a shortcut: POST /objects/sites/{siteId}/timeseries creates a standalone series and links it to the site in one call.

Open questions

Are a type's unit and shape frozen after first definition, or can a later write redefine them? Should re-writing the same key be an upsert, or append-and-version so old values stay auditable? What happens when a link collides with a series name already on the object — say a second spot-price feed: reject, or alias at link time ("as": "spot-price-dayahead")? And does /platform/v3/timeseries subsume the /data datasets endpoint for the timeseries kind, leaving /data to tables and scalars only?

On this page