Source: https://docs.debitura.com/developer-concepts/rate-limiting

# Rate Limiting

Debitura enforces rate limits on all external APIs to protect system stability. The limiter partitions requests by the literal `XApiKey` header across three time windows. Requests without that header — including Bearer-token requests — share the fallback partition.

<a id="default-limits"></a>

## Default Limits

| Window | Limit | Type |
| --- | --- | --- |
| Per minute | 2,000 requests | Sliding window |
| Per hour | 20,000 requests | Fixed window |
| Per day | 100,000 requests | Fixed 24h window |

A request is rejected if it exceeds **any** of the three limits. Each API key maintains independent counters.

<a id="rate-limit-response"></a>

## Rate Limit Response

When you exceed a limit, the API returns HTTP 429:

429Too Many Requests

Response

Content-Type: application/json

```json
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Please try again later or contact support if you need higher limits.",
  "retryAfter": 42.0
}
```

The `retryAfter` field in the JSON body indicates seconds until capacity becomes available. Debitura does not set a `Retry-After` response header — read `retryAfter` from the body instead.

<a id="handling-rate-limits"></a>

## Handling Rate Limits

1.  Check for HTTP 429 status code
2.  Read `retryAfter` from the response body
3.  Wait the specified duration before retrying
4.  Implement exponential backoff if retries continue to fail

Example: Retry with backoff

```javascript
async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const { retryAfter } = await response.json();
      const delay = (retryAfter || Math.pow(2, attempt)) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }

    return response;
  }
  throw new Error('Max retries exceeded');
}
```

<a id="key-behaviors"></a>

## Key Behaviors

**Sliding vs fixed windows**: The per-minute limit uses a sliding window divided into 10-second segments, providing smooth limiting. The per-hour limit uses a fixed 1-hour window anchored to the partition's first request — once started it advances in 1-hour chunks tied to that anchor, and does not reset at the top of each clock hour. The per-day limit works the same way: a fixed 24-hour window aligned to the partition's first request that advances in 24-hour chunks, and does not reset at midnight UTC.

**No queuing**: Requests exceeding the limit are immediately rejected. Debitura does not queue requests waiting for capacity.

**Requests without `XApiKey`**: Requests with no `XApiKey` header are tracked under a shared fallback partition and subject to the same limits. This includes unauthenticated requests and requests authenticated with a Bearer token. A nonempty but invalid `XApiKey` value has its own rate-limit partition even though authentication later rejects it.

<a id="requesting-higher-limits"></a>

## Requesting Higher Limits

If your integration requires higher throughput, contact [contact@debitura.com](mailto:contact@debitura.com) with:

-   Your use case and expected request volume
-   Which API and endpoints you're calling
-   Whether you need burst capacity or sustained throughput
