Skip to content

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.

  • values holds the current value of every unit-level field that has (or had) a value, with its provenance: revision, source, review_status, recorded_at and, for condition data, measured_at. A cleared field has value: 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
units.jsonl (one line, wrapped here)
{"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.