> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rinne.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limits

> How Rinne's request rate limits work, which endpoints they apply to, and how to build integrations that stay within them.

Rinne applies rate limits to keep the platform fast and stable for everyone. Limits are enforced consistently, so you should design your integration to stay within them from the start.

## How the limits work

There are two rate limits, both measured in requests per second (req/s):

| Request type                                                                         | Limit      |
| ------------------------------------------------------------------------------------ | ---------- |
| `POST` requests for transaction creation and 3DS session creation and authentication | 1000 req/s |
| Every other request                                                                  | 50 req/s   |

Each limit is a **single shared pool**, not a per-endpoint allowance. The limiter counts the total number of requests you send across all endpoints in a bucket, then compares that sum against the limit. Sending requests to many different endpoints does not give you a higher combined throughput.

<Note>
  The two pools are independent of each other. Transaction-creating or 3DS-creating requests draw from the 1000 req/s pool, while all other requests draw from the 50 req/s pool.
</Note>

## Same across all environments

Rate limits behave identically in every environment, including sandbox. This means you can validate your integration against the real limits before going to production.

<Tip>
  Use sandbox to load-test your request patterns. Because the limits match production exactly, the behavior you observe in sandbox is the behavior you will get in production.
</Tip>

## When you exceed a limit

Exceeding a limit does not block your account or interrupt service. Requests that stay within the limit always succeed and the API keeps working normally. Only the excess requests above the limit are rejected.

Rejected requests return a `429 Too Many Requests` status code with **no response body**. The rate limit details are returned in the response headers:

```http theme={null}
HTTP/1.1 429 Too Many Requests
x-ratelimited: true
x-ratelimit-limit: 50, 50;w=1
x-ratelimit-remaining: 0
x-ratelimit-reset: 1
```

| Header                  | Description                                                                            |
| ----------------------- | -------------------------------------------------------------------------------------- |
| `x-ratelimited`         | Set to `true` when the request was rejected for exceeding the limit.                   |
| `x-ratelimit-limit`     | The limit for the applicable pool. `50, 50;w=1` means 50 requests per 1-second window. |
| `x-ratelimit-remaining` | Requests remaining in the current window. `0` when the limit has been reached.         |
| `x-ratelimit-reset`     | Seconds until the window resets and requests are accepted again.                       |

<Note>
  Unlike other API errors, a rate limit rejection has no JSON body. Detect it from the `429` status code and the `x-ratelimit-*` headers rather than parsing a response body.
</Note>

There is no penalty or cooldown beyond the rejected request itself. As soon as your request rate drops back within the limit, requests succeed again.

## Best practices

<Tip>
  Smooth out bursts on the client side. Spreading requests evenly across each second keeps you under the limit far more reliably than sending large bursts.
</Tip>

* **Throttle outbound requests** so your sustained rate stays within the applicable pool.
* **Retry rejected requests** with a short backoff. Because there is no penalty, a rejected request can be retried as soon as your rate is back within the limit.
* **Use idempotency for transaction creation.** Include a `request_id` when creating transactions so safe retries never produce duplicates. See [Error handling](/guides/error-handling#idempotency) for details.

```javascript theme={null}
// Assumes your HTTP client throws an error exposing the response status
// (e.g. error.status === 429). A 429 has no body, so detect it from the status.
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      // Only retry rate-limit rejections, and only while retries remain.
      // Anything else (or the last attempt) is rethrown to the caller.
      if (error.status === 429 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}

// Usage
const transaction = await retryWithBackoff(() => createTransaction(data));
```

## Next steps

<CardGroup cols={2}>
  <Card title="Error handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle 429 and other error responses
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Explore all endpoints and schemas
  </Card>
</CardGroup>
