Limits & Quotas

Die xander API setzt drei Verbrauchs-Limits durch, zum Schutz der Service-Stabilität und für fairen Zugang aller Kunden. Diese Seite erklärt, was jedes Limit tut, wie es gescoped ist und wie die API Ihrer Anwendung mitteilt, dass Sie ein Limit erreichen oder überschritten haben.

Was wird limitiert

Limit type Purpose Scope
Rate limit Short-term burst protection. Caps how many requests you can issue in a sliding window of seconds and minutes. Per Service Principal (your Client ID).
Quota Long-term consumption budget. Caps how many requests you can issue per day. Per Service Principal (your Client ID).
Concurrency Caps how many requests you can have in flight at any given moment. Per Service Principal (your Client ID).

All three limits are tied to your Service Principal, specifically, to the azp / appid claim in your access token, which is also our billing pivot point. Two developers from the same customer organisation each have their own Service Principal and therefore their own independent limits.

Where are the actual numbers?

Concrete values for rate limit, quota and concurrency depend on your tier and your contractual agreement with XWare Pulse, and they change over time. We deliberately do not publish them on this page so that the documentation does not go stale. To find out your current limits, or to request an increase, reach out to your xander contact at XWare Pulse, or write to xander@xwr.ch if you don't have one to hand.

How the API tells you about your current state

Response headers on every successful call

Every successful response carries headers that let you reason about the rate limit window without having to make any extra API calls:

HeaderMeaning
RateLimit-Limit The maximum number of requests allowed in the current rate-limit window.
RateLimit-Remaining How many requests you have left in the current rate-limit window before being throttled.

Use these headers in your client to track headroom and slow yourself down preemptively when you're getting close to the limit, instead of letting the server throttle you.

What happens when you hit the rate limit

Rate-limited requests come back as HTTP 429 Too Many Requests with a Retry-After header indicating how many seconds to wait before issuing the next request:

HTTP/1.1 429 Too Many Requests
Retry-After: 7
RateLimit-Limit: ...
RateLimit-Remaining: 0
Content-Type: application/json

{ "message": "Rate limit is exceeded. Try again in 7 seconds." }

The correct client behaviour on a 429 is: read the Retry-After value, sleep that many seconds, then retry. Do not implement aggressive exponential backoff that ignores the header, the server already tells you exactly when it's safe to come back.

What happens when you hit the daily quota

Quota-exceeded requests come back as a 4xx response indicating that your daily budget is exhausted. Unlike the rate limit, the quota does not heal in seconds, it resets at the end of the daily window. Until then, your Service Principal cannot make any more billed calls. The right reaction is:

  1. Stop issuing requests for that Service Principal until the next window opens.
  2. Check whether your usage is unexpected (a runaway loop, a misconfigured cron job).
  3. If usage is legitimate and you need more quota, contact your xander contact at XWare Pulse to discuss an increase.

What happens when you hit the concurrency limit

Concurrency limits are evaluated per request. If you have N requests in flight against your Service Principal and try to start an N+1th, the new request is rejected at the gateway with a 4xx/5xx response. The correct client behaviour is to either:

  • Reduce the parallelism in your application (e.g. lower your worker pool size or your Promise.all batch size).
  • Or: front your calls with a semaphore that caps in-flight requests just below your agreed concurrency limit.

Example: a defensive HTTP client wrapper

Pseudo-code showing the minimum-viable behaviour for a production-ready xander client:

async function callXander(path, body) {
  while (true) {
    const response = await fetch(`${XANDER_BASE_URL}${path}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${await getAccessToken()}`,
        'Content-Type':  'application/json',
      },
      body: JSON.stringify(body),
    });

    // Track headroom
    const remaining = parseInt(response.headers.get('RateLimit-Remaining') ?? '0', 10);
    if (remaining < 5) {
      // Less than 5 requests left in the current window - slow ourselves down
      // before the server has to throttle us.
      await sleep(500);
    }

    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') ?? '5', 10);
      await sleep(retryAfter * 1000);
      continue;
    }

    if (response.status === 401) {
      // Token expired or compromised - force a token refresh
      forceTokenRefresh();
      continue;
    }

    return response;
  }
}

Tips for staying within your limits

  • Cache your access token. Each token is valid for ~60 minutes. Caching it avoids hitting the rate limit on the token endpoint and reduces latency on every call.
  • Bound parallelism explicitly. Use a semaphore in your client to cap concurrent in-flight requests at a value comfortably below your agreed concurrency limit.
  • Watch RateLimit-Remaining. If it's consistently low, either you're sized too tight for your real-world load, or your application is retrying more than necessary. Investigate before requesting a limit increase.
  • Don't multiply costs by retrying on 4xx. A 400 / 422 response means the server understood your request and rejected it for content reasons. Retrying it will get the same answer, and burn rate-limit budget. Only retry on 429 and 5xx.
  • Coordinate with us before high-volume bursts. If you're about to process a large historic backlog or run a one-off migration, talk to your xander contact in advance so we can confirm your limits are sized appropriately.