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

PropertyValue
Limit120 requests per window, per authenticated client
Window60 seconds, fixed
Counted perThe subject (sub claim) of your access token
Applies toEvery 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 429 are 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:

HeaderExampleMeaning
RateLimitlimit=120, remaining=87, reset=41Your limit, requests left in the current window, and seconds until the window resets.
RateLimit-Policy120;w=60The policy: 120 requests per 60-second window.
Retry-After41Sent 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. Read remaining and reset on every v3 response and pause proactively when remaining reaches zero, instead of waiting for a 429.
  • Page with the largest page size. v3 list endpoints accept per-page up to 1000. A full topology sync at per-page=1000 needs 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 interval you 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.


Did this page help you?