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

# Rate limiting on the InfraAudit REST API

> InfraAudit enforces per-user rate limits on all API endpoints. Learn the limit headers, what a 429 response contains, and how to build a safe retry strategy.

The InfraAudit API applies rate limits per authenticated user to prevent abuse and ensure availability for all accounts. Rate limit status is returned in response headers on every request.

## Rate limit headers

Every API response includes the following headers:

| Header                  | Description                                              |
| ----------------------- | -------------------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum number of requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests remaining before you hit the limit              |
| `X-RateLimit-Reset`     | Unix timestamp (seconds) when the current window resets  |

Example response headers:

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1712345678
```

## When you exceed the limit

When your request count exceeds the limit, the API returns `429 Too Many Requests`:

```json theme={null}
{
  "error": "rate limit exceeded",
  "retryAfter": 30
}
```

<ResponseField name="retryAfter" type="integer">
  The number of seconds to wait before retrying your request.
</ResponseField>

## Handling 429 responses

Wait for the number of seconds in `retryAfter` before retrying. Do not immediately retry — doing so consumes more of your remaining quota.

<CodeGroup>
  ```bash curl theme={null}
  response_code=$(curl -s -o /dev/null -w "%{http_code}" \
    https://api.infraaudit.dev/v1/resources \
    -H "Authorization: Bearer $TOKEN")

  if [ "$response_code" = "429" ]; then
    retry_after=$(curl -s https://api.infraaudit.dev/v1/resources \
      -H "Authorization: Bearer $TOKEN" | jq '.retryAfter')
    echo "Rate limited. Waiting ${retry_after}s..."
    sleep "$retry_after"
    # Retry the request
  fi
  ```

  ```javascript JavaScript theme={null}
  async function fetchWithRetry(url, options, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
      const response = await fetch(url, options)

      if (response.status === 429) {
        const body = await response.json()
        const wait = (body.retryAfter || 30) * 1000
        console.log(`Rate limited. Waiting ${body.retryAfter}s...`)
        await new Promise(resolve => setTimeout(resolve, wait))
        continue
      }

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

  ```python Python theme={null}
  import time
  import requests

  def fetch_with_retry(url, headers, max_retries=3):
      for attempt in range(max_retries):
          resp = requests.get(url, headers=headers)

          if resp.status_code == 429:
              retry_after = resp.json().get('retryAfter', 30)
              print(f'Rate limited. Waiting {retry_after}s...')
              time.sleep(retry_after)
              continue

          return resp

      raise Exception('Max retries exceeded')
  ```
</CodeGroup>

<Tip>
  Check `X-RateLimit-Remaining` before making bursts of requests. If the value is low, add a short delay between calls to avoid hitting the limit.
</Tip>

<Note>
  Self-hosted deployments can adjust rate limit defaults via environment variables. See the [configuration reference](/self-hosting/configuration) for available options.
</Note>
