Data warehouse sync
Page through all units and their current values and write one JSON line per unit, ready to load into a data warehouse.
The script pages through all units with listBatteries, reads the
current values of each one with listBatteryValues and writes
units.jsonl: one line per unit with synced_at, the unit and its values. Most warehouses load JSON Lines
directly. It needs a key with the scope read.
valuesholds the current value of every unit-level field that has (or had) a value, with its provenance:revision,source,review_status,recorded_atand, for condition data,measured_at. A cleared field hasvalue: null.- Model-level data (nominal voltage, rated capacity and so on) belongs to the model version, not the unit: read it once
per version with
listModelVersionValues. - The script makes one request per unit plus one per 200 units. With 10 000 units that is about 17 minutes at the
shared limit of 600 requests per minute; it waits when it gets
429.
#!/usr/bin/env bash
# warehouse-sync.sh: every unit with its current values as one JSON line (needs curl and jq)
set -euo pipefail
auth="Authorization: Bearer $BATTERYPASS_API_KEY"
synced_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
cursor=""
: > units.jsonl
while :; do
page=$(curl -sS --fail --retry 5 -G "$BATTERYPASS_URL/api/v1/batteries" -H "$auth" \
-d limit=200 ${cursor:+--data-urlencode "cursor=$cursor"})
jq -c '.items[]' <<<"$page" | while IFS= read -r unit; do
id=$(jq -r .id <<<"$unit")
curl -sS --fail --retry 5 -H "$auth" "$BATTERYPASS_URL/api/v1/batteries/$id/values" |
jq -c --argjson unit "$unit" --arg at "$synced_at" '{synced_at: $at, unit: $unit, values: .items}' >> units.jsonl
done
cursor=$(jq -r '.next_cursor // empty' <<<"$page")
[ -n "$cursor" ] || break
done
wc -l < units.jsonl{"synced_at":"2026-10-24T02:00:00Z",
"unit":{"id":"0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f","serial_number":"SV2027-000184","passport_status":"published","row_version":4,"updated_at":"2026-07-02T10:15:00Z"},
"values":[{"field_key":"condition.state_of_charge","value":81.5,"unit":"%","revision":9,"source":"api","review_status":"approved","measured_at":"2026-10-24T06:00:00Z","recorded_at":"2026-10-24T06:00:03Z","registry_version":"0.5.0","checks":[]}]}Load the file into a staging table and merge on unit.id. To read less on later runs, compare row_version,
passport_status and the values' revision with what you stored, as described in
Sync units. For a complete copy of everything your organisation holds, an admin can
also download the full organisation export in the portal.