DynVoltDYNVOLT · developers

Pagination & time ranges

The list envelope, limit/offset pagination, and UTC time windows.

The list envelope

Every list endpoint returns the same envelope:

{
  "data": [ ... ],
  "meta": {
    "limit": 50,
    "offset": 0,
    "count": 137
  }
}
  • data — the items for this page.
  • meta.count — always present: the number of items matching the query.
  • meta.limit / meta.offset — present on paginated (O&M) lists, echoing the effective values.

Offset pagination (O&M lists)

The O&M lists — /work-orders and /defects — take limit and offset query parameters:

curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/work-orders?limit=50&offset=100" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY"

Page until offset + len(data) >= meta.count:

items, offset, limit = [], 0, 50
while True:
    page = client.get(
        "/v1/sites/pv-ljubas/work-orders",
        params={"limit": limit, "offset": offset},
    ).json()
    items += page["data"]
    offset += len(page["data"])
    if offset >= page["meta"]["count"] or not page["data"]:
        break

Time ranges (time-series endpoints)

Time-series endpoints — telemetry history, energy matrices, hourly earnings, predictions, BESS logs, telemetry gaps — take a time window instead of an offset:

  • start / end — timestamps, interpreted as UTC.
  • limit — caps the number of returned rows.
curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/energy/hourly?start=2026-08-01T00:00:00Z&end=2026-08-08T00:00:00Z" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY"

For long backfills, walk the window in chunks (e.g. one week at a time) rather than requesting months in a single call — smaller windows respond faster and retry more cheaply.

start/end as timestamps vs dates

Most windows want RFC3339 UTC timestamps (2026-08-01T00:00:00Z). A few take plain dates (YYYY-MM-DD):

  • Timestamps — telemetry/history, energy/hourly, predictions, BESS logs, gaps, I-V series.
  • Datesearnings/* (start/end are days) and the daily endpoints, which take a single date (forecast, plant/summary) or target_date (markets prices, BESS schedules/deviations).

limit caps by area

limit is clamped to a per-area maximum — request more and you get the cap, not an error:

AreaEndpointsCap
Telemetry historyloggers / inverters / strings / sensors / BESS history, dispatch & control logs, BESS alarm history10,000
Weather / EMIweather/logger/{id}/history2,000
I-V expectediv/strings/{…}/expected5,000
Telemetry gapsgaps, gaps/logger/{id}500
O&M listswork-orders, defects, om/notifications200

Everything is UTC

All timestamps in the API — request parameters and response fields — are UTC ISO-8601 (2026-08-11T06:00:00Z). The plants operate in local European time zones, but the API never returns local time. Convert at the display layer, not in storage:

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

utc = datetime.fromisoformat("2026-08-11T06:00:00+00:00")
local = utc.astimezone(ZoneInfo("Europe/Skopje"))

Send Z-suffixed (or +00:00) timestamps in queries; naive timestamps are rejected with 422 validation_error.