rebase.energy

Conventions

Pagination, filtering, naming, PATCH semantics, errors, permissions, and other rules that apply across the API.

The API is small enough that a few conventions cover most of it. They hold for every object type, built in or custom.

Pagination

List endpoints return up to limit records (default 50, max 200) in items.

r = requests.get(f"{BASE}/objects/sites", headers=HEADERS, params={"limit": 200})
r.raise_for_status()
sites = r.json()["items"]

When a workspace holds more records than one page, the response's next_cursor is set; pass it back as ?cursor= to fetch the next page, until it comes back null. For totals that do not depend on paging, GET /objects/{object_name}/stats answers in one call, and GET /objects/{object_name}/export streams the whole type. See Bulk record reads.

Narrowing a list

  • ?filter[<attribute>]=value keeps only records whose attribute has that value. Repeat it for several values of one attribute (OR-ed) or several attributes (AND-ed). Filters work on custom types, whose stored fields are exactly their attribute slugs; on the built-in types a filter is a 400. An object attribute filters by the target's id, and a between attribute by either end or one position: see Linking records.
  • ?fields=a,b trims values to the named attributes. The envelope always rides along.
  • ?parent_id=<id> lists the parts of one owner.

Slugs and field names

For slugs you choose, such as a timeseries type, a custom object type name, or an attribute slug, prefer kebab-case: power-production, not power_production.

Field names in request and response bodies (valid_time, capacity_kw, next_cursor) are fixed by the API and are snake_case.

Record names

name is required on every record, with one exception: a record of a type with a between attribute is identified by the two records it runs between, and a name only tells parallel records apart. Names may not contain / and must be unique among siblings: parts under one owner, and top-level records (sites, custom records) within the workspace. Creating a record whose name conflicts returns 409.

PATCH semantics

Only fields present in the body are applied, values included: send the attributes you change and leave the rest out.

Two attribute kinds behave differently, and it pays to know which is which:

  • Part lists are additive. On a site, values.assets in a PATCH creates a new asset for each entry and never removes one, so a forgotten field can never delete a part. Modify or delete individual assets through the asset routes.
  • Reference lists are replaced. A PATCH on an objects attribute with relation: reference writes the whole set; what it leaves out is unlinked. The records on the far end are untouched either way.

Errors

StatusWhen
400A filter on a built-in type, an aggregation parameter on a history read, or an operation the attribute kind does not allow (unlinking a part).
401No key, or an invalid one.
403Missing permission. Also returned when trying to modify or delete a built-in object type.
404Not found. Records in another workspace simply do not resolve, so they look the same as records that do not exist.
409Conflict: a duplicate name, a link cycle, a second record between the same pair of ends, or deleting an object type that still has records or is still targeted by another type.
422Validation error in the body or query parameters.

Error bodies have the shape {"detail": "..."}.

Stats

Every record carries a stats object with numbers the platform computes; you cannot set them. On a site, stats.total_capacity_kw is the sum of its assets' capacity_kw and updates as assets change. Whole-type totals live at GET /objects/{object_name}/stats.

Timestamps

Timestamps out are ISO 8601 in UTC (2026-09-22T10:00:00Z). Timestamps in may carry any offset; a naive timestamp is read as UTC. Timeseries windows (start, end) are inclusive on both ends.

Compression

Responses over a kilobyte are gzip-compressed when the request says Accept-Encoding: gzip. Python requests does this by default and decompresses transparently. Timeseries payloads shrink about tenfold, so leave it on.

Permissions

A key restricted at creation needs the permission matching the call, and sees 403 otherwise.

PermissionAllows
objects:readListing and reading records, object types, links, stats, exports.
objects:writeCreating and updating records and object types, linking and unlinking.
objects:deleteDeleting records and object types.
timeseries:readReading series, single or batch.
timeseries:writeUploading points, and deleting them (a deletion appends a retraction, so it is a write).

Retry safety

Several operations are designed so that a retried request is harmless:

  • Timeseries uploads dedup by default. Re-sending points the store already holds writes nothing (they count as points_skipped). A changed value becomes the current one, and the previous value stays in the series' history.
  • Batch writes land whole or not at all. If any entry of POST /timeseries/write fails validation, nothing is written.
  • Deleting points is a retraction. Points that are already gone count as points_missing and change nothing.
  • Linking is idempotent. PUT .../{attribute}/{target_id} on a record that is already linked returns 200 with the unchanged record.
  • Creating a record with nested parts is all or nothing. If any part fails, no record is created.

On this page