# Rate Limiting

Some sensitive routes are rate-limited. There is no global limit, and there are no plans or tiers.

## Overview

Limits are configured per route, and most routes have none. A limited route counts requests per client IP address within its time window; counters are kept in memory by each API instance. Agent Payments API keys have their own per-key limit, and the password-reset endpoints have their own throttling.

## Limits

| Route | Limit |
| --- | --- |
| POST /v1/auth/register POST /v1/auth/login POST /v1/auth/google POST /v1/auth/apple | 20 per minute each |
| POST /v1/auth/refresh | 60 per minute |
| POST /v1/transactions/{id}/broadcast | 20 per minute |
| POST /v1/transactions/quote | 60 per minute |
| POST /v1/swap/broadcast POST /v1/swap/broadcast-approve POST /v1/swap/broadcast-gasless | 10 per 10 seconds each |
| POST /v1/dapps/broadcast | 10 per 10 seconds |
| GET /v1/dapps/nonce | 30 per minute |
| POST /v1/dapps/sign-personal-message | 20 per minute |
| POST /v1/aave/supply, /withdraw, /prepare-supply, /prepare-withdraw, /broadcast | 5 per minute each |
| GET /v1/portfolio/performance | 20 per minute |
| Requests authenticated with an Agent Payments API key | 120 per minute per key |
| POST /v1/auth/forgot-password | Throttled per email address and per IP |

## Headers

Responses from the routes in the table (except the API-key and password-reset limits) carry:

| Header | Description |
| --- | --- |
| x-ratelimit-limit | Requests allowed in the window |
| x-ratelimit-remaining | Requests left in the current window |
| x-ratelimit-reset | Seconds until the window resets |
| retry-after | On 429 only: seconds to wait before retrying |

## Exceeding a Limit

A route-level limit answers `429`; the wait in the message depends on the route's window:

```json
{
  "statusCode": 429,
  "error": "Error",
  "message": "Rate limit exceeded, retry in 1 minute"
}
```

- The Agent Payments API-key limit answers `403` with the message `Rate limit exceeded`.
- The password-reset endpoints answer `429` with `{ "error": "Too many requests. Please try again later." }`.

## Best Practices

- On `429`, wait for `retry-after` seconds before retrying.
- Sign tenant-signed requests again for each retry: a signature is accepted only once.
- Don't retry sign-in in a loop; show the error to the user instead.

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(url, options);
    if (res.status !== 429 || attempt === maxRetries) return res;
    const retryAfter = Number(res.headers.get('retry-after')) || 2 ** attempt;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }
}
```

---

Web version: https://docs.aureahub.com/#rate-limiting
