Rate limits and quotas
What the server enforces per account and per IP, and the size caps by plan.
Limits are enforced by the server. A request over a rate limit gets a 429; a request over a size or storage cap gets a 413. Plan-gated features return 402.
Rate limits
| Limit | Scope | Value | Applies to |
|---|---|---|---|
| Publishes | per account | 120 per hour | every publish_*, update_*, put_file, set_description, edit_file, apply_patch and restore_version tool call, POST /api/docs, /api/snippets, /api/files, /api/docs/{id}/files, PATCH with html, and streamed uploads |
| Access requests | per account | 10 per hour | request_access and POST /api/access-requests, shared across clients |
| Access-request status | per account | 120 per minute | GET /api/access-requests/status; poll every five seconds while pending |
Abuse-protection throttles also apply per IP to sign-in starts (/auth/login, /auth/sso/*), the SSO lookup (/auth/sso/lookup), token minting (POST /api/tokens), client registration (POST /oauth/register), invalid upload tickets, not-found responses on the content host (slug scanning protection on /d/*), and, per IP per document, to HTTP Basic failures on password documents. Their thresholds are not published and may change; a normal client will not reach them.
The publish limit is shared across every client and token on the account: an agent and a CI job under the same account share the same 120 calls. The error text from MCP is publish rate limit exceeded (120/hour); slow down and retry later.
There is no rate limit on read tools (list_docs, get_link, list_files, search_docs, grep_doc, get_file, list_versions, list_links, resolve_link, list_access, list_comments, list_audit, get_policy) or on settings tools (set_visibility, set_tags, set_retention, set_listing, set_alias, share_doc, delete_doc) beyond ordinary server capacity. Search has its own bounds instead: patterns up to 256 characters, at most 200 matching files per call, and a time budget of a few seconds after which results are truncated.
Size caps
These are the same on every plan.
| Cap | Value | Error |
|---|---|---|
| One file through a tool call or JSON request | 20 MiB decoded | 413 file 'x' is N bytes; limit is 20971520 |
| Request body on non-streaming routes | 32 MiB | 413 request body too large |
One HTTP upload (/u/<ticket>, PUT /api/upload) |
20 MiB for HTML, JS/CSS or an existing file replacement; 2 GiB for other new file shares | 413 before any bytes are read when Content-Length says so, or mid-stream |
| All files in one document | 20 MiB; a file share may hold one streamed upload instead | 413 doc would be N bytes; limit is ... |
| Documents per account | 5,000 live documents | 413 doc limit reached |
| Tags per document | 10, each 1 to 32 characters | 400 |
| Description on a file share | 16 KiB; 2,000 characters when passed through create_upload |
400 |
| Upload ticket lifetime | 60 minutes | 403 invalid or expired upload ticket |
list_docs page size |
500 | silently capped |
| Text file read, searched or edited as text | 1 MiB, valid UTF-8 | 400 naming the file |
| Comment body | 8,000 characters | 400 |
| Alias | 64 characters, lowercase slug | 400 |
| Event names per webhook | 50 | 400 |
| Audit log page | 500 events; CSV export 100,000 rows | silently capped |
Storage by plan
Storage counts current live documents, secure items and stored Agent transcripts. Soft-deleted documents and secure items do not count. Historical document versions and backups are not charged through this meter.
| Plan | Cap |
|---|---|
| Free | 1 GiB |
| Team | 100 GiB |
| Business | 1 TiB |
| Enterprise | 5 TiB included, or a negotiated allowance |
| Storage add-on | 50 GiB per unit, on any plan |
Without opted-in usage billing, storage growth above the cap fails with 413. Paid workspace owners can authorize time-based additional storage, subject to a monthly spending and capacity limit. Existing documents remain readable, and deleting or reducing document content remains possible at the limit.
Plan gates
| Feature | Requirement | Error |
|---|---|---|
| Custom domains | Team: 1, Business: 5, Enterprise: unlimited | 402 |
| Members | Free 2, Team 25, Business 50, Enterprise no enforced cap; pending invites count | new sign-ups get a personal workspace instead; invites 402; SSO sign-in and SCIM refused with the cap |
| Comments | Team plan or above | 403 naming the plan |
| Groups and group grants | Team plan or above; individual/workspace grants work on every plan | 403 naming the plan |
| Workspace policy, audit log, webhooks | Business plan or above | 402 naming the plan |
| SSO with your own identity provider | Business plan or above | 402 |
| Legal hold, IP allowlist, SCIM | Enterprise | 402 |
Other lifetimes
| Item | Lifetime |
|---|---|
| OAuth authorization code | 300 seconds, single use |
| OAuth access token | 7 days |
| OAuth refresh token | 365 days from the grant, rotated on use |
| Static token | chosen at mint; default 365 days; 0 for never |
| App session cookie | 90 days |
| Viewer cookie on the content host | 12 hours |
| Workspace sign-in handoff code | 60 seconds |
| Soft-deleted document | 7 days before purge |
| Member invite link | 14 days, one address |
| Document access request and email review link | 30 days from request, one decision; denial allows retry after 24 hours |
| Expiry warning email | 3 days before expiry; its keep link is bound to that expiry |
| Webhook delivery | 10-second timeout; retries after 1 m, 5 m, 30 m, 2 h, 12 h, then failed |
| Database backups | 14 days |
Retrying
Publishing is safe to retry: pass idempotency_key on publish_*, and rely on the fact that update_* and put_file with identical content are no-ops. Re-PUTting an upload ticket returns the document created by the first attempt. On 429, wait and retry; there is no Retry-After header today.
Activity bounds
Activity queries return at most 100 files or detailed events per page. Daily ranges are clamped to the plan: 90/365/730/1,095 days for Free/Team/Business/Enterprise. Detail is retained for 0/7/30/90 days respectively. Poll activity_status no faster than its returned poll_after (15 seconds).