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

# Rate Limits

> Learn about API rate limits and best practices for staying within them.

Rate limits protect the Entri API from abuse and ensure consistent performance for all users.

<Warning>
  **Rate limiting is not yet enforced on most endpoints.** The `X-RateLimit-*` headers and `429` responses described below are planned features and are not currently returned by the API. This page documents the intended behavior for future reference.

  Some AI-related endpoints (batch translation) do apply throttling using the [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting) module.
</Warning>

## Planned Rate Limit Headers

When rate limiting is fully implemented, every API response will include:

| Header                  | Description                                                       |
| ----------------------- | ----------------------------------------------------------------- |
| `X-RateLimit-Limit`     | The maximum number of requests allowed in the current window.     |
| `X-RateLimit-Remaining` | The number of requests remaining in the current window.           |
| `X-RateLimit-Reset`     | Unix timestamp (seconds) when the current window resets.          |
| `Retry-After`           | Present only on `429` responses. Seconds to wait before retrying. |

## When the Limit is Exceeded

When a rate limit is exceeded, the API will return `429 Too Many Requests`:

```json theme={null}
{
  "statusCode": 429,
  "error": "Too Many Requests",
  "message": "Too many requests. Please retry after 30 seconds.",
  "timestamp": "2025-03-02T14:30:00.000Z",
  "path": "/api/projects/proj_abc123/keys"
}
```

## Exponential Backoff

When you receive a `429` response, wait the number of seconds indicated in `Retry-After` before retrying. For repeated failures, use exponential backoff with jitter to avoid thundering herd problems:

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    async function requestWithRetry(path, options = {}, maxRetries = 5) {
      for (let attempt = 0; attempt < maxRetries; attempt++) {
        const response = await fetch(`https://app.nt3.io/api${path}`, {
          ...options,
          headers: {
            "X-API-Key": process.env.ENTRI_API_KEY,
            ...options.headers,
          },
        });

        if (response.status !== 429) {
          return response;
        }

        const retryAfter = parseInt(response.headers.get("Retry-After") ?? "1", 10);
        const jitter = Math.random() * 1000;
        const delay = retryAfter * 1000 * Math.pow(2, attempt) + jitter;

        console.warn(`Rate limited. Retrying in ${Math.round(delay / 1000)}s...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }

      throw new Error("Max retries exceeded");
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import time
    import random
    import httpx

    def request_with_retry(path: str, max_retries: int = 5, **kwargs):
        headers = {"X-API-Key": "entri_your_token_here"}

        with httpx.Client(base_url="https://app.nt3.io/api") as client:
            for attempt in range(max_retries):
                response = client.request(
                    kwargs.pop("method", "GET"),
                    path,
                    headers=headers,
                    **kwargs,
                )

                if response.status_code != 429:
                    return response

                retry_after = int(response.headers.get("Retry-After", 1))
                delay = retry_after * (2 ** attempt) + random.random()
                print(f"Rate limited. Retrying in {delay:.1f}s...")
                time.sleep(delay)

        raise RuntimeError("Max retries exceeded")
    ```
  </Tab>
</Tabs>

## Best Practices

**Monitor remaining capacity.** Read the `X-RateLimit-Remaining` header on each response. If the value is low, slow down your request rate before hitting the limit.

**Batch operations.** Use bulk endpoints where available rather than making individual requests. For example, use `POST /api/projects/:projectId/keys/bulk` to create many keys in one request instead of looping over single-key creates.

**Cache responses.** If your integration reads the same data frequently, cache the results locally for a short period rather than polling the API on every render or event.

**Use the CLI for large syncs.** The Entri CLI handles rate limit retries automatically and supports `--json` output for use in CI/CD pipelines:

```bash theme={null}
entri push --json
entri pull --json
```

**Avoid polling.** Instead of polling the API to detect changes, use [webhooks](/api-reference/endpoints/webhooks) to receive real-time notifications when events occur.

<Tip>
  If you routinely hit rate limits, review whether your integration can be restructured to use batch endpoints, reduce polling frequency, or react to webhooks instead.
</Tip>
