> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.prividium.com/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

Rate limiting protects your Prividium™ deployment by restricting how many requests a client can make within a time
window. This prevents abuse, ensures fair resource allocation, and maintains service availability.

## Overview

When enabled, rate limiting applies per-client limits based on either user ID (for authenticated requests) or IP address
(for unauthenticated requests). Clients that exceed their limit receive an HTTP 429 response and must wait before making
additional requests.

## Limits Before Authentication

`RATE_LIMIT_IP_MAX` limits each client IP to `5000` requests per window by default. The check runs before authentication,
body parsing, and schema validation. It applies separately to `/rpc` (including wallet and service RPC), authenticated routes under `/api/organizations`, `/api/m2m-app-queries`, and `/api/tenant-actions` when the legacy tenant feature is enabled.

All callers count toward this limit, including privileged users and trusted services. Clients sharing one IP share this
budget. Increase it when many clients use the same outbound proxy. Configure `TRUST_X_FORWARDED_FOR` only for the addresses or CIDRs of your trusted
reverse proxies so the API uses the correct client IP. Leave it unset for direct connections.

The authenticated limits below still apply after this check. Limits are stored in memory independently on each API
replica. Setting `RATE_LIMIT_ENABLED=false` disables both layers.

## Configuration

Rate limiting is controlled via environment variables:

| Variable                | Default | Description                                              |
| ----------------------- | ------- | -------------------------------------------------------- |
| `RATE_LIMIT_ENABLED`    | `true`  | Enable or disable per-window request quotas              |
| `RATE_LIMIT_AUTH_MAX`   | `100`   | Max requests per window for authentication endpoints     |
| `RATE_LIMIT_PUBLIC_MAX` | `300`   | Max requests per window for public endpoints             |
| `RATE_LIMIT_USER_MAX`   | `300`   | Max requests per window for authenticated user routes    |
| `RATE_LIMIT_RPC_MAX`    | `1000`  | RPC quota per window; see RPC Endpoints below            |
| `RATE_LIMIT_M2M_MAX`    | `1000`  | M2M quota per window; see M2M Application Requests below |
| `RATE_LIMIT_WINDOW_MS`  | `60000` | Time window in milliseconds (default: 1 minute)          |

## Rate Limit Tiers

Different endpoint categories have different limits to match their expected usage patterns:

### Authentication Endpoints (`RATE_LIMIT_AUTH_MAX`)

Applies to login, token refresh, and session management routes. Lower limits help prevent credential stuffing and brute
force attacks.

### Public Endpoints (`RATE_LIMIT_PUBLIC_MAX`)

Applies to unauthenticated public routes. Limits are based on client IP address.

### User Endpoints (`RATE_LIMIT_USER_MAX`)

Applies to authenticated API routes. Limits are tracked per user ID when authenticated, falling back to IP address for
unauthenticated requests.

### RPC Endpoints (`RATE_LIMIT_RPC_MAX`)

Applies to JSON-RPC operations. Each batch item consumes one unit, sharing the same budget as individual calls.
For example, a batch of 20 operations consumes 20 units. Operations that return permission or method errors also count.

If the remaining quota cannot cover a batch, the proxy rejects the entire batch with HTTP 429 before executing any item.
Quota consumed while checking a rejected batch is not refunded. Wait for the quota window to reset before retrying.

### M2M Application Requests (`RATE_LIMIT_M2M_MAX`)

Applies to requests authenticated with an M2M application API key, both on the JSON-RPC proxy and on the
`/api/m2m-app-queries` routes. Limits are tracked **per API key** (not per IP), so one application's keys are isolated
from each other and a key cannot raise its quota by distributing calls across multiple source IPs.

Each RPC batch item consumes one unit. The RPC and REST route groups maintain separate counters.

## RPC Batch and Concurrency Caps

By default, the RPC proxy accepts at most **20 operations per batch**, for every method and caller. Larger batches return a
JSON-RPC invalid-request error (`-32600`) before any operation executes. Wallet RPC continues to accept only individual calls.

By default, the proxy allows at most **20 active operations per caller** and **1,000 active operations across all callers** per API replica.
Batch items and individual calls share these concurrency caps across RPC routes. Requests exceeding either cap receive
HTTP 429 with `RATE_LIMIT_ERROR` and `retry-after: 1`; no items from that request execute.

Concurrency follows the user ID, M2M API key, or legacy tenant/service ID. Anonymous requests, including wallet RPC,
share the source IP's allowance. A batch reserves one slot per item and holds every slot until all its items and their
upstream calls have settled, including failed calls. For example, a batch of 20 with 19 completed items and one pending
item still occupies 20 slots. A client disconnect does not release slots while upstream work continues.

Of the 1,000 slots, **100 are reserved for trusted callers**: the [exempt actors below](#exempt-actors). Other callers can occupy at most 900 slots. Trusted callers can also use shared capacity, but still obey the per-caller and total caps. The reservation protects against saturation by ordinary callers; it does not guarantee availability when trusted callers exhaust capacity.

Configure these caps independently of per-window quotas:

| Variable                          | Default | Description                                                                       |
| --------------------------------- | ------- | --------------------------------------------------------------------------------- |
| `RPC_MAX_BATCH_SIZE`              | `20`    | Maximum operations per batch                                                      |
| `RPC_MAX_CONCURRENT_PER_ACTOR`    | `20`    | Maximum active operations per caller                                              |
| `RPC_MAX_CONCURRENT_TOTAL`        | `1000`  | Maximum active operations per API replica                                         |
| `RPC_RESERVED_CONCURRENT_TRUSTED` | `100`   | Slots within the total reserved for trusted callers; `0` disables the reservation |

Startup rejects non-integer or invalid limits before database migrations. Batch size must fit the per-caller cap and the total minus the reservation. The per-caller cap cannot exceed the total.

To accept 100-item batches, set both `RPC_MAX_BATCH_SIZE=100` and `RPC_MAX_CONCURRENT_PER_ACTOR=100`. This permits five times more simultaneous work per caller; validate upstream capacity before raising these limits. The default remains 20.

Requests do not queue for capacity. The per-window quota is charged before concurrency admission, so a request rejected
by a concurrency cap can still consume quota. Quota charges are not refunded.

These safety caps also apply to exempt actors and remain active when `RATE_LIMIT_ENABLED=false`.
All quotas and concurrency counters are local to each API replica.

The proxy authorizes and processes each operation separately, including any required upstream calls.
These caps bound work at the proxy; the node does not receive the original request as one JSON-RPC batch.
Configure clients to match the server's batch cap, limit concurrent calls, and respect `retry-after`.

For ethers, set `batchMaxCount: 20` in `JsonRpcProvider` options when using the default server limits. This controls batch size; callers must also bound concurrent requests across batches.

The server expresses `retry-after` in seconds. ethers 6.17.0 [interprets this header as milliseconds](https://github.com/ethers-io/ethers.js/blob/v6.17.0/src.ts/utils/fetch.ts#L557-L562). Do not rely on its default 429 retries to wait long enough. Use bounded client retries with delays in seconds and backoff; keep the server header in seconds.

## Exempt Actors

The actors below bypass authenticated per-window request quotas. Other limits still apply.

### Tenants (legacy)

Consumers authenticated via tenant credentials are exempt from authenticated per-window request quotas. These are typically trusted backend services
with their own rate limiting. Tenants are a legacy feature, disabled by default; new integrations use
[M2M applications](/configuring/applications/m2m-applications), which follow the standard `RATE_LIMIT_M2M_MAX` tier.

### Services (legacy)

Service accounts bypass authenticated per-window request quotas. Service authentication is a legacy mechanism superseded by
[M2M applications](/configuring/applications/m2m-applications).

### Users with System Permissions

Users with any of the following [system permissions](/configuring/users/system-permissions) on a zone-scoped role are exempt:

* `full_sequencer_rpc_access`
* `full_read_access`
* `contract_deployment`
* `admin_read`

These permissions bypass authenticated per-window request quotas. Other limits still apply.

## Response Headers

When rate limiting is enabled, responses include headers for the last applied limit, or the limit that rejected the request:

| Header                  | Description                                        |
| ----------------------- | -------------------------------------------------- |
| `x-ratelimit-limit`     | Maximum quota units allowed in the current window  |
| `x-ratelimit-remaining` | Quota units remaining in the current window        |
| `x-ratelimit-reset`     | Seconds until the current window resets            |
| `retry-after`           | Seconds to wait before retrying (on 429 responses) |

## Error Response

When a client exceeds the rate limit, they receive:

* **HTTP Status:** `429 Too Many Requests`
* **Error Code:** `RATE_LIMIT_ERROR`
* **Message:** Includes the number of seconds to wait before retrying

Example response:

```json
{
  "error": {
    "code": "RATE_LIMIT_ERROR",
    "message": "Rate limit exceeded. Try again in 45 seconds."
  }
}
```

## API-Key Failure Audits

Each API replica samples API-key authentication failures in fixed 60-second windows using two independent allowances:

* Missing, malformed, inactive, or unknown keys: one event per IP, up to 10,000 distinct IPs per window.
* Recognized active keys: one event per verified key ID and failure reason, up to 10,000 combinations per window.

Recognized-key failures include blocked IPs, disallowed credential types, and deleted owner organizations. Their allowance
is shared across source IPs, so distributing attempts cannot increase the number of audit rows for one key and reason.
Junk requests cannot consume the recognized-key allowance.

An entry records only the sampled request. It does not count suppressed failures or collect their source IPs. Once an allowance is exhausted, new failures in that category produce no audit entry until the next window.

Further failures still fail authentication, but do not create more audit rows until the next window.
Both allowances reset together. Sampling remains active when request rate limiting is disabled.

## Best Practices

* **Start with defaults**: The default values are suitable for most deployments.
* **Monitor before adjusting**: Use metrics to understand actual traffic patterns before changing limits.
* **Use system permissions sparingly**: While system permissions bypass rate limits, they also grant elevated access.
  Only assign them when genuinely needed.
