Rate limits
How many requests you get, and how to back off when you hit the ceiling.
Rate limits protect the Verdigris APIs from abuse and keep response times predictable for all customers. This page describes the limits that apply to your integration and how to handle them.
REST API v3
| Property | Value |
|---|---|
| Limit | 120 requests per window, per authenticated client |
| Window | 60 seconds, fixed |
| Counted per | The subject (sub claim) of your access token |
| Applies to | Every request to https://api.verdigris.co/v3, regardless of endpoint or method |
Notes:
- The count is per token subject, not per IP address and not per token string. Refreshing your token does not reset your counter, and multiple processes that use the same client credentials share one budget.
- The window is fixed: your counter starts at your first request and resets 60 seconds later.
- Requests rejected with
429are still counted against the window, so do not retry in a tight loop.
Response headers
Every authenticated response from the v3 API includes the IETF draft-7 rate limit headers:
| Header | Example | Meaning |
|---|---|---|
RateLimit | limit=120, remaining=87, reset=41 | Your limit, requests left in the current window, and seconds until the window resets. |
RateLimit-Policy | 120;w=60 | The policy: 120 requests per 60-second window. |
Retry-After | 41 | Sent on 429 responses only. Seconds to wait before retrying. |
Example values are illustrative.
The 429 response
When you exceed the limit, the API responds with HTTP 429 Too Many Requests. The body is the plain-text message Too many requests, please try again later. It is not the JSON error object used by other v3 errors, so check the status code before you try to parse JSON.
Data API v4
The Data API v4 (https://api.verdigris.co/data/v4) does not currently publish a request quota, and the RateLimit headers described above are a v3 feature. Do not assume that means unlimited use: apply the same client behavior (backoff, batching, caching) to v4, and honor a 429 or 503 response if you ever receive one.
Handle 429 with exponential backoff and jitter
Use the Retry-After header when present. For all other retryable failures (429, 502, 503), use exponential backoff with full jitter so many clients do not retry at the same instant.
import random
import time
import requests
RETRYABLE = {429, 502, 503}
def request_with_backoff(method, url, *, max_retries=6, base=1.0, cap=60.0, **kwargs):
for attempt in range(max_retries + 1):
resp = requests.request(method, url, timeout=30, **kwargs)
if resp.status_code not in RETRYABLE:
return resp
if attempt == max_retries:
return resp
retry_after = resp.headers.get("Retry-After")
if retry_after is not None:
delay = float(retry_after) + random.uniform(0, 1)
else:
delay = random.uniform(0, min(cap, base * 2 ** attempt))
time.sleep(delay)const RETRYABLE = new Set([429, 502, 503]);
export async function fetchWithBackoff(url: string, init: RequestInit = {}, maxRetries = 6, base = 1000, cap = 60000) {
for (let attempt = 0; ; attempt++) {
const res = await fetch(url, init);
if (!RETRYABLE.has(res.status) || attempt === maxRetries) return res;
const retryAfter = res.headers.get("Retry-After");
const delay = retryAfter
? Number(retryAfter) * 1000 + Math.random() * 1000
: Math.random() * Math.min(cap, base * 2 ** attempt);
await new Promise((r) => setTimeout(r, delay));
}
}Only retry idempotent requests automatically. All read endpoints (GET) are safe to retry.
Stay within the limits
- Watch
RateLimit. Readremainingandreseton every v3 response and pause proactively whenremainingreaches zero, instead of waiting for a429. - Page with the largest page size. v3 list endpoints accept
per-pageup to 1000. A full topology sync atper-page=1000needs far fewer requests than the default of 10. See Rebuild your building topology. - Use batch endpoints in Data API v4. Pass many IDs in one request (
ids=1,2,3) instead of calling the single-entity endpoint per ID. See Retrieve telemetry. - Cache slow-changing data. Topology changes rarely. Sync it on a schedule (for example daily) and store it, rather than re-reading it on every optimization cycle.
- Poll incrementally. Request only the time window since your last successful data point, at the coarsest
intervalyou need. - Reuse tokens. Cache your access token until it expires instead of requesting a new one for every call.
- Spread out work. Add small random delays to scheduled jobs so that many jobs do not all fire on the minute boundary.
Access tokens
The token endpoint (https://auth.verdigris.co/oauth/token) has its own limit: 20 token requests per hour, and at most 480 per 24 hours, on a rolling basis. Tokens last 1 hour, so cache and reuse them instead of requesting one per run or per call. For more token quota, contact our sales team.
Need a higher limit?
If your integration legitimately needs more than 120 requests per minute on the v3 API, contact [email protected]. Include your client ID, the endpoints involved, your expected request rate, and what you have already done to batch and cache requests.
Updated 27 days ago

