Reference
Limits & quotas
Rate limits
| Limit | Value | Scope |
|---|---|---|
| Sustained request rate | 30 per minute | Per credential |
| Burst | 3 per second | Per credential |
| Credential provisioning | 5 per hour | Per source address |
Exceeding a request limit returns 429 with Retry-After: 5:
HTTP/1.1 429 Too Many Requests
Retry-After: 5
{"error":"rate limited","retryAfter":5}
The rate limit is keyed on the credential query parameter, so separate credentials get separate
budgets. If you need more throughput for a single workload, do not rotate credentials to get around
it.
Thirty per minute is lower than it sounds — design for it
The API is a control plane, not a data path: object bytes never traverse it, so a well-built client makes very few calls. A 1 GiB upload is two calls plus the direct shard writes. A client that hits 30 per minute is usually making one call per small object rather than packing them into shared slabs — see Packing strategy.
Account & billing limits
The account API at https://tessera.storage/api/v1 applies its own limits, separate from the
storage rate limits above:
| Limit | Value |
|---|---|
| Public signup & auth endpoints | Rate limited at the edge by nginx zones. Any challenge, where enabled, is scoped to /api/v1/auth/* and never applied to the rest of the API. |
| New accounts | 3 per source IP (IPv6 /64) and 20 per /24 (IPv6 /48) per rolling 24 hours, agents and humans together; 429 signup_limit beyond that. Each signup also needs an ALTCHA proof of work (X-Altcha). |
| Top-up amount | $0.01–$10,000 per intent (Tessera's floor; x402 uses only this), quantized to whole cents. Crypto through the payment processor (deposit address or invoice) also needs the coin's processor minimum — about $0.38 for USDC on Base, more for most other coins; live values from GET /billing/topup/minimums or tessera topup --minimums. |
| Sign-in links | Single use, expire after 15 minutes. |
| Browser sessions | 30-day tessera_session cookie; state-changing requests must match the site origin. |
See Accounts & billing for the endpoints these apply to.
Storage caps
Each credential carries caps recorded at provisioning:
| Cap | Enforced |
|---|---|
| Maximum stored bytes | Yes — 402 on prepare-write, slabs, and objects |
| Daily read bytes | Recorded, not currently enforced |
| Daily write bytes | Recorded, not currently enforced |
| Monthly spend | Recorded, not currently enforced |
Cap accounting is in whole 4 MiB sectors:
| Endpoint | Accounted as |
|---|---|
POST /prepare-write | totalShards × 4 MiB |
POST /slabs | sectors × 4 MiB per slab |
POST /objects | 4 KiB per object |
Small objects in their own slabs are expensive
A 100 KiB object written as its own 10-of-30 slab is accounted at 30 whole sectors — 120 MiB, a 1200× overhead against its logical size. This is arithmetic, not a penalty: 30 providers each hold a sector, and a sector is the unit they deal in. Pack small objects into shared slabs and the overhead disappears.
Erasure coding constraints
| Constraint | Value |
|---|---|
Minimum redundancy (totalShards / minShards) | 1.5× |
| Maximum redundancy | 4.0× |
minShards | ≤ totalShards |
| Default geometry | 10 data + 20 parity = 30 |
encryptionKey length | exactly 32 bytes |
| Duplicate provider within a slab | rejected |
| Duplicate sector root within a slab | rejected |
| Sector size | 4 MiB |
Request constraints
| Constraint | Value |
|---|---|
| Object key | exactly 32 bytes, 64 hex characters, no / |
| Slab ID | exactly 32 bytes, 64 hex characters |
| Provider public key | ed25519: + 64 hex characters |
| Sector root | 64 hex characters |
credential / signature encoding | base64url with padding |
appKey encoding | base64url without padding |
| Byte-slice JSON fields | standard base64 with padding |
validUntil | unix seconds, decimal in the query, little-endian uint64 in the hash |
Pagination
| Endpoint | Mechanism | Default | Notes |
|---|---|---|---|
GET /slabs | limit + offset | 50 | Not a stable cursor; entries can be missed if the set changes between pages. |
GET /objects | limit + after timestamp | 50 | Preferred. Stable enough for synchronisation; make application idempotent for timestamp ties. |
For a complete enumeration, walk GET /objects and collect slab IDs from the records rather than
paging GET /slabs.
Signature expiry
validUntil is chosen entirely by you. There is no server-imposed maximum, which means the
discipline is yours:
- 60 seconds for interactive calls.
- Minutes, not days. A signed URL is a bearer capability until it expires; expiry is the only replay protection in the scheme.
- Never sign a
DELETEfar into the future. That signature is a deletion capability for as long as it remains valid, wherever the URL was logged.
No CORS
The API cannot be called from a browser
No Access-Control-Allow-Origin header is sent on any endpoint, so cross-origin browser requests are
blocked by the browser regardless of what you do client-side. Call the API from a server-side runtime,
or proxy it through your own backend — which is the better pattern anyway, since it keeps your
credential's private key off the client.
Availability expectations
| Endpoint | Behaviour |
|---|---|
prepare-write | May return 503 when provider availability dips. Always retry with backoff. |
| Manifest endpoints | Expected to be consistently available; 500 is a bug worth reporting. |
| Shard reads and writes | Depend on individual providers, which fail routinely. Your client should treat individual provider failure as normal and route around it. |
The service is provided without a service level agreement.