Best Practices

Rate Limiting

Understand the OAuth token rate-limiting model, why it is designed this way, and what your integration should implement.

OAuth Token Rate Limiting

This guide explains how rate limiting works on the Billabex Public API (/api/public/v1/*) and how your client should behave to integrate reliably and predictably.

Overview

Rate limiting is enforced per OAuth Bearer access token.

This model is designed to provide clear and deterministic behavior for integrations:

  • One token = one shared quota across all public endpoints
  • Tokens are isolated from each other
  • Sliding window enforcement smooths short traffic bursts

If multiple systems or threads use the same token, they also share the same rate limit.

Rate-Limiting Model

AspectBehavior
ScopePer OAuth access token
Window typeSliding window
Window duration60 seconds
Limit300 requests per window
Applies toAll OAuth-protected public endpoints
Exceeded behavior429 Too Many Requests + Retry-After

Why Per-Token Limiting?

This design choice ensures:

  • Fairness between independent integrations
  • Isolation between tokens
  • Predictable capacity planning on the client side

What this means for you

  • Treat each access token as a shared, limited resource
  • Centralize outgoing requests per token
  • Avoid uncontrolled concurrency using the same token

Where Rate Limiting Applies

This model applies to all OAuth-protected endpoints under:

/api/public/v1/*

Examples:

  • GET /api/public/v1/accounts
  • GET /api/public/v1/invoices
  • GET /api/public/v1/organizations

Rate Limit Response Headers

Every rate-limited response includes the following headers. These headers are your source of truth.

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets

Example Response

HTTP/1.1 200 OK
 X-RateLimit-Limit: 300
 X-RateLimit-Remaining: 258
X-RateLimit-Reset: 1738859285

When the Limit Is Exceeded (429)

When the quota is exhausted, the API responds with:

  • HTTP status 429 Too Many Requests
  • A Retry-After header indicating when it is safe to retry

Example 429 Response

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1738859285
Retry-After: 12
Content-Type: application/json

{
  "statusCode": 429,
  "message": "Too many requests",
  "retryAfter": 12
}

Important

  • Always respect Retry-After
  • Do not retry immediately or use hardcoded delays
  • Repeated violations may lead to additional protections

To work reliably with the API, your client should:

  1. Centralize all outgoing requests per access token
  2. Monitor X-RateLimit-Remaining and slow down proactively
  3. On 429, wait for Retry-After before retrying
  4. Cap retries and surface errors when limits are repeatedly hit

Reference Retry Pattern (JavaScript)

async function fetchWithTokenRateLimit(url, options = {}, maxRetries = 3) {
  let attempt = 0;

  while (attempt <= maxRetries) {
    const response = await fetch(url, options);

    // Success or non-rate-limit error
    if (response.status !== 429) {
      return response;
    }

    const retryAfterSeconds = Number(response.headers.get('Retry-After') || '1');

    // Safety floor to avoid immediate retries
    const waitMs = Math.max(1000, retryAfterSeconds * 1000);

    if (attempt === maxRetries) {
      return response;
    }

    await new Promise((resolve) => setTimeout(resolve, waitMs));
    attempt += 1;
  }
}

Best Practices

  • Prefer pagination and batching to reduce request count
  • Use a per-token request queue or throttler
  • Keep concurrency controlled, especially for write-heavy flows
  • Avoid large parallel bursts using the same token
  • If you rotate tokens, remember that quotas are tracked per token

Next Steps

Support

Questions about OAuth token rate limiting?
Contact us via the website contact form.