Rate limits
Per-token limits, the rate-limit headers, and backoff advice.
Rate limits are applied per token:
| Limit | Value |
|---|---|
| Requests per minute | 120 |
| Requests per day | 5,000 |
| Write requests per minute | 30 |
Writes (O&M actions, alert acknowledge/resolve, report runs, curtailment, BESS dispatch approvals) count against both the write limit and the overall limits.
Authentication throttle
Repeated authentication failures are throttled separately — 30 failed attempts per minute per source IP — to blunt brute-force scanning of the dvk_live_ keyspace. This is independent of the per-token limits above (a failed request has no valid token to bill). If you are looping on 401 unauthorized, fix the key rather than retrying; hammering the endpoint will get the source IP throttled.
Headers
Every response includes the current rate-limit state:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1754899200X-RateLimit-Limit— the limit for the current window.X-RateLimit-Remaining— requests left in the window.X-RateLimit-Reset— Unix timestamp (seconds, UTC) when the window resets.
When you exceed a limit, the API returns 429 with error code rate_limited and a Retry-After header (seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 23Backing off
- Always honor
Retry-Afteron 429 — it is the exact wait, no guessing needed. - For proactive throttling, watch
X-RateLimit-Remainingand slow down before hitting zero. - For transient
5xx/upstream_unavailableerrors, use exponential backoff with jitter (e.g. 1s, 2s, 4s, ... capped at 60s).
import random, time
def request_with_backoff(fn, max_attempts=6):
for attempt in range(max_attempts):
resp = fn()
if resp.status_code == 429:
time.sleep(int(resp.headers.get("Retry-After", "5")))
continue
if resp.status_code >= 500:
time.sleep(min(2 ** attempt, 60) + random.random())
continue
return resp
resp.raise_for_status()Staying under the limits
- Poll at the data's cadence. Live telemetry updates on the order of tens of seconds — polling
/overviewevery 2 seconds burns budget for identical data. Once per minute is plenty for dashboards. - Prefer
/overviewover calling/loggers+/inverters+/sensors+/weatherseparately when you need the whole picture — one request instead of four. - Cache slow-moving data (site lists, report catalogs, markets metadata) for hours, not seconds.
- Need more headroom for a legitimate use case? Contact DynVolt with your key ID and expected volume.

