# Limits

Every limit CLItrail applies: what each plan allows, and the service limits that keep it predictable for everyone. The same numbers are served at `GET /api/limits`.

## Plan limits

| Per organisation | Free | Standard | Enterprise |
| --- | --- | --- | --- |
| Install paths | 1 | 1 | 10 |
| Websites | 1 | 3 | 25 |
| Domains per website, primary included | 5 | 10 | 25 |
| Members, pending invites included | 1 | 3 | 20 |
| Destinations per website | 0 | 10 | 20 |
| Log streams (S3 or webhook) | 0 | 0 | 10 |
| Log retention, days (at most) | 7 | 30 | 90 |
| Tracked events a month (UTC) | 10,000 | 1,000,000 | 10,000,000 |

Past a count limit, creating answers `402` `plan_limit` (or `plan_required` where the plan allows none). Past the monthly quota, Free is counted only and paid plans are never cut off. See [Plans and limits](https://clitrail.com/docs/plans) for what happens at each limit and after a downgrade.

## Rate limits

| Requests | Limit | Counted per |
| --- | --- | --- |
| All collection endpoints | 240 a minute | Client address |
| `POST /v1/visits` | 6,000 a minute | Website |
| `POST /v1/events` | 1,200 a minute | Website |
| `POST /v1/events` | 30 a minute and 500 a day | Website and client address |
| Dashboard writes | 30 a minute | Client address |
| Dashboard reads | 600 a minute | Session |
| Invitations | 20 a day | Organisation |
| Support requests | 5 a day, and 20 a day | Person, and organisation |
| Checkout and billing portal sessions | 10 an hour | Organisation |
| Log exports | 10 an hour | Organisation |
| Log stream tests | 10 an hour | Organisation |

Rate limits use fixed windows aligned to the clock. A refused request answers `429` with `{"error":"rate_limited","retryAfter":<seconds>}` and a `Retry-After` header. Reaching a limit is recorded in the organisation’s activity and in [Logs](https://clitrail.com/docs/logs). Client addresses are counted as keyed hashes, never stored in the clear.

## Sizes and counts

- **Collection request body**: `16 KB`; larger answers `413` `body_too_large`
- **Organisations per person**: Own `5`, belong to `20`
- **Registered pages per website**: `30`
- **Receipts per install report**: `64`
- **GA4 measurement IDs per visit**: `10`
- **Custom adapters**: `10` per visit, `10` fields each, `256` characters per value
- **UTM value**: `150` characters
- **Google session attributes**: `6,000` characters
- **Handoff install command**: `65,536` characters
- **Names of websites, destinations, install paths, organisations**: `80` characters
- **Support request**: Message `4,000` characters, diagnostics `16 KB`
- **Logs per page**: `200` records
- **Log export**: `100,000` records or `25 MB`
- **Bodies kept in Logs**: Inbound `16 KB`; outbound request and response `64 KB` each
- **Log stream backlog**: `1,000,000` records or `7` days
- **Install events CSV export**: `100,000` most recent events
- **Activity per request**: `200` entries (default `50`)

## Time

- **Receipt matches for**: `30` days after the visit
- **Handoff token**: The lifetime of its visit
- **Dashboard session**: `7` days
- **Invitation link**: `7` days, single use
- **Hook scan**: About `4` seconds or `20,000` files
- **Hook request**: curl: `3` s to connect, `5` s total; wget: `5` s network timeout, one attempt
- **Reconstruction window**: `24`, `72` (default) or `168` hours in the dashboard; `1` to `168` by API
- **Website data retention**: `30` to `1,095` days; default `395`
- **Log retention**: `7` days up to the plan’s maximum; default `30`
- **Webhook request**: `10` seconds
- **Stripe webhook signature**: At most `5` minutes old
