Skip to content

Guides

Rate limits

Each API key can make up to 600 requests per minute. Every authenticated response tells you how much of that budget is left.

How the limit works

Limits apply per API key and use a token bucket. Each key has a bucket of 600 requests that refills continuously at 10 requests per second. You can burst up to the full bucket, then continue at the refill rate.

  • Separate integrations with separate keys don’t share a budget.
  • Every authenticated request counts, including requests that fail (for example, with 404) and conditional requests answered with 304.
  • Design for 600 requests per minute per key. Enforcement is approximate, so don’t depend on getting more.

Rate limit headers

Every authenticated response, successful or not, includes the headers below. Responses to requests that fail authentication (401, 403 account_inactive, or a 503 while the key is being checked) don’t include them, because no key’s budget was consulted.

HeaderMeaning
RateLimit-LimitSize of the bucket (600).
RateLimit-RemainingRequests you can make right now.
RateLimit-ResetSeconds until the bucket is completely full again.
Retry-AfterOn 429 responses, the number of seconds to wait before retrying. 503 service_unavailable responses include it too.
Response headers
HTTP/1.1 200 OK
Content-Type: application/json
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 2

When you exceed the limit

Requests over the limit fail with 429 Too Many Requests and code rate_limit_exceeded. Wait at least Retry-After seconds before trying again.

429 response
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 60
Retry-After: 1
X-Request-Id: req_k3j2h1g0f9e8d7c6b5a4z3y2

{
  "type": "https://weguard.ai/docs/errors#rate_limit_exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "code": "rate_limit_exceeded",
  "detail": "Rate limit of 600 requests per minute exceeded for this API key.",
  "instance": "/core/v1/devices",
  "requestId": "req_k3j2h1g0f9e8d7c6b5a4z3y2"
}

Retrying with backoff

Retry 429, 500 and 503 responses with exponential backoff and jitter, and honor Retry-After when it’s present. Jitter spreads out retries from parallel workers so they don’t all hit the API at once.

const RETRYABLE = new Set([429, 500, 503]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function getWithBackoff(url, maxAttempts = 5) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.WEGUARD_API_KEY}` },
    });
    if (!RETRYABLE.has(response.status)) return response;

    // Prefer the server's hint; otherwise exponential backoff with full jitter.
    const retryAfter = Number(response.headers.get("Retry-After"));
    const cap = Math.min(30_000, 1000 * 2 ** attempt);
    await sleep(retryAfter > 0 ? retryAfter * 1000 : Math.random() * cap);
  }
  throw new Error("Gave up after repeated 429/500/503 responses");
}

Staying under the limit

  • Use limit=100 (or the endpoint’s maximum) to fetch more per request. See Pagination.
  • Prefer one paginated pass over the list endpoint to fetching devices one by one.
  • Send If-None-Match with the ETag you received to skip downloading unchanged data. See Conditional requests.
  • Spread scheduled jobs out instead of starting them all on the hour.
  • Watch RateLimit-Remaining and slow down before it reaches zero.

We use cookies

We use cookies to ensure you get the best experience on our website. For more information on how we use cookies, please see our cookie policy.

By clicking "Accept", you agree to our use of cookies.

Learn more