---
path: /v1/authentication
title: Authentication and rate limits
summary: How the public audit API authenticates (it doesn't) and how it is rate limited.
group: Guides
updated: 2026-06-22
---

# Authentication and rate limits

## Authentication

The public audit endpoints (`POST /audit`, `GET /api/public/audit/{id}` and its
`history`/`diff` variants, and `GET /api/public/browse`) are **unauthenticated** — no API
key is required. AgentFit only fetches the public documentation site you name, so there is
no per-user data to protect on these routes.

The **MCP server** at `https://docs.agentfit.dev/mcp` uses OAuth 2.1 with anonymous auto-consent: the
authorization step approves with no login and issues a short-lived bearer token scoped to
`agentfit:audit`. See the [MCP guide](https://docs.agentfit.dev/v1/mcp).

## Rate limits

Limits protect the service and the sites it audits. When you exceed one, the response is
`429` with a `Retry-After` header (seconds). Back off and retry after that delay.

| Limit | Window | Applies to |
|---|---|---|
| Per-IP request rate | ~1 request / 30s (small burst) | `POST /audit` |
| Per-target rate | 10-minute window per audited URL | `POST /audit` |
| Per-site quota | 24 hours per `(client, base_url)` | `POST /audit` |
| Per-IP poll rate | generous | `GET /api/public/*` |

### Handling 429

```bash
curl -sD - -o /dev/null -X POST 'https://agentfit.dev/audit?async=true' \
  -H 'Content-Type: application/json' \
  -d '{"base_url":"https://docs.stripe.com"}' | grep -i retry-after
```

### Python

```python
import time, requests

def start_audit(base_url):
    while True:
        r = requests.post("https://agentfit.dev/audit", params={"async": "true"},
                          json={"base_url": base_url}, timeout=10)
        if r.status_code != 429:
            return r.json()
        time.sleep(int(r.headers.get("Retry-After", "30")))
```

Treat `429` and `503` as retryable; treat `400` as a permanent client error and fix the
request before retrying.

