Platform limits
This is the whole platform's limits table in one page: how big a request can be, what you may name things, what a new project is allowed to run, how long things wait before giving up, and how long credentials live. Every number here comes from the code that enforces it.
Two things to know before you read on:
- Most limits are defaults, not laws. Where a limit is backed by a setting (shown in the last column), your platform administrator can change it. Where a limit is a hard rule of the API, it says so.
- Check your own project's live limits with
GET /v1/projects/{projectID}/quotaor the console's Quota page — that reads the platform's own enforcement rather than this table. See Quotas and audit.
Request body and upload sizes
Exceeding these returns 400, or the request is refused before it is read. Two paths answer 413 instead: a direct POST to a deployed function (the language shims) and an upload through the console's proxy.
| What | Limit | Notes |
|---|---|---|
Most core API JSON bodies (workload-api) | 8 KiB | Users, orgs, projects, members, invitations, keys, service accounts |
| Agent or function code upload | 100 MiB | Plus 1 MiB of slack for the form fields. Setting: MAX_UPLOAD_BYTES |
| Stored source file (browser editor) | 1 MiB per file | 200 files per agent; an uploaded archive may expand to at most 64 MiB |
| Agent invoke request | 1 MiB | Applies to invoke, streaming invoke, and memorize |
| Agent invoke upstream response | 32 MiB | A larger response from the agent becomes a 502 |
| Function event body (the runtime shim) | 8 MiB | The cap on a direct POST to a deployed function |
| Per-agent secrets body | 1 MiB | PUT and PATCH on /v1/agents/{name}/secrets |
| Project secret create body | 128 KiB | The value itself is capped separately, below |
| Project secret value | 65536 bytes (64 KiB) | 400 — secret value is N bytes, limit is 65536 |
| Secret binding body | 8 KiB | PUT on a secret map |
| MCP server create body | 4 KiB | Only name and expose are read |
| MCP tool publish body | 1 MiB | It carries Python source |
| Serverless API body | 256 KiB | Every route on serverless-api |
| VectorDB body | 32 MiB | Setting: MAX_BODY_BYTES |
| VectorDB query filter | 64 KiB | 400 — filter is too large |
| MemoryStore create body | 16 KiB | — |
| Pub/Sub publish body | 4 MiB | Every other Pub/Sub body is capped at 1 MiB |
| Sandboxed tool call body | 1 MiB | The tool's own response is capped at 8 MiB |
Pagination and batch sizes
| What | Default | Maximum | Notes |
|---|---|---|---|
page_size on every list endpoint | 50 | 200 | limit is accepted as an alias. A larger value is rejected with 400 — page_size must be between 1 and 200 — never clamped |
page_token | — | — | Opaque, tamper-evident, and bound to the list that issued it. A token from another list is 400 — page_token is invalid or was issued for a different list; start from the first page |
| VectorDB points per upsert | — | 1000 | All-or-nothing: one bad point rejects the whole batch. Setting: MAX_POINTS_PER_REQUEST |
VectorDB top_k per query | 10 | 1000 | Setting: MAX_TOP_K |
| VectorDB scroll page | 50 | 1000 | The point browser |
| VectorDB ids per delete-points call | — | 1000 | — |
| VectorDB collections listed in a direct-access credential | — | 256 | Beyond that the response sets truncated: true |
| Pub/Sub messages per publish | — | 100 | 400 — a publish carries at most 100 messages (got N) |
| Pub/Sub messages per pull | 10 | 100 | Over-asks are silently capped, not rejected |
| Pub/Sub acknowledgement ids per call | — | 1000 | — |
| Agent log history lines | 500 | — | The limit query parameter on the history endpoint |
| Serverless log tail lines | 500 | 10000 | The tail query parameter; minimum 1. Setting: LOG_TAIL_LINES |
Name rules
Names are chosen once and cannot be changed — renaming would move the address other things depend on.
| Resource | Rule | Length |
|---|---|---|
| Agent or function | ^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$ — a lowercase DNS label | ≤ 63 |
| Serverless service | ^[a-z]([-a-z0-9]{0,50}[a-z0-9])?$ | 1–52 (shorter than 63 because revision names append -00001) |
| MCP server | ^[a-z]([a-z0-9-]{0,38}[a-z0-9])?$ | ≤ 40 |
| MCP tool | ^[a-z][a-z0-9_]{0,62}$ — a lowercase identifier, no leading underscore | ≤ 63 |
| VectorDB index | [a-z0-9]([a-z0-9-]*[a-z0-9])? — no uppercase, no underscore | ≤ 48 |
| MemoryStore instance | ^[a-z]([-a-z0-9]*[a-z0-9])?$ | 1–40 |
| Pub/Sub topic and subscription | ^[a-z]([a-z0-9-]*[a-z0-9])?$ | ≤ 63 |
| Project secret | ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$ | 1–63 |
| Environment variable / secret key | ^[A-Za-z_][A-Za-z0-9_]*$ | — |
| Service account | A lowercase DNS label. Reserved permanently, even after deletion, so a new principal can never inherit an old one's grants | — |
session_id | ^[A-Za-z0-9_.-]{1,128}$ | ≤ 128 |
user_id | ^[A-Za-z0-9_.:@-]{1,128}$ | ≤ 128 |
| A session id you choose yourself while unauthenticated | Must be at least 24 characters of unguessable randomness — or omit it and the platform generates one | ≥ 24 |
| Password | Minimum 12 characters, and 3 of 4 character types (upper, lower, number, symbol) | ≥ 12 |
Agents and functions share one flat name space per project: a function cannot take a name an agent already holds.
crusoe-cloud-credential is a reserved secret name. Nineteen environment variable names are reserved and cannot be bound to a secret — they are listed in the Secrets API.
Default project quotas
Written into every new project at creation. A hard value of 0 means that resource is unlimited. Nothing in the API raises a cap — ask your platform administrator.
| Resource | Default | Shown in the console as |
|---|---|---|
| Running instances | 50 | Running instances |
| Services | 30 | Services |
| CPU reserved (requests) | 10 | CPU (reserved) |
| Memory reserved (requests) | 20 GiB | Memory (reserved) |
| CPU maximum (limits) | 20 | CPU (max) |
| Memory maximum (limits) | 40 GiB | Memory (max) |
| Pub/Sub backlog storage | 1 GiB per project | (Pub/Sub quota page) |
| Pub/Sub topics | 100 per project | — |
| Pub/Sub producers and consumers | 100 each, per topic | — |
An instance's size sets its own container's limits; medium, the default, is 1 CPU and 1 GiB. The request proxy that runs beside every instance has its own small limits set by the platform, 500m CPU and 256 MiB. So a medium instance counts 1.5 CPU and 1.25 GiB against CPU maximum and Memory maximum: at the defaults, about thirteen medium instances at once. A redeploy counts the old instance until it has scaled away. An instance held back by the quota stays deploying, with the quota message in message, until room frees up.
Instance size
You pick one value, not four. A size sets the CPU and memory for every instance of that workload, and it applies to agents, functions, MCP servers and serverless services alike.
| Size | CPU | Memory |
|---|---|---|
small | 500m | 512 MiB |
medium (default) | 1 | 1 GiB |
large | 2 | 2 GiB |
xlarge | 4 | 4 GiB |
Two things worth knowing about what a size means:
- Memory is reserved, not just capped. The size's memory is both the request and the limit, so an instance always has the memory you picked. There is no quiet oversubscription and no surprise eviction under load - but the memory counts against your project's reserved-memory quota whether or not the workload is busy.
- CPU is a ceiling with a small reservation. Your instance may use up to the size's CPU, and reserves 10% of it (never less than 100m). This is what lets a scaled-to-zero workload start cheaply and still burst.
The live table is always GET /v1/platform/limits, or:
platformctl platform limits
Set a size when deploying, or change it later:
platformctl agents config set my-agent --size large
Every serverless revision permanently holds two entries against the Services quota (one public, one private), and every deploy or spec edit creates a revision. Once that quota is full, new revisions across the whole project stop becoming ready until you delete services you no longer need.
Pub/Sub budget is spent by claim, not by use. A topic reserves its max_bytes the moment it exists, so four empty 16 MiB topics have already spent 64 MiB. Consuming messages does not give budget back — delete a topic or lower its max_bytes.
If some older material quotes 60 Services, that is a fallback value used only when a project is created without the field; projects created by the platform's own control plane get 30. Your live number is always in GET /v1/projects/{projectID}/quota.
Timeouts
Three separate clocks govern one request, and they answer different questions. All three are yours to set.
| Clock | Default | Maximum | Setting | What it measures |
|---|---|---|---|---|
| Request timeout | 60 s | 900 s | timeout_seconds | The whole request, first byte to last. The one to raise for slow work. |
| Response start timeout | inherits the request timeout | 900 s | response_start_seconds | How long the platform waits for your first byte. Set it low to fail fast on a workload that is wedged, while still allowing a long total. |
| Idle timeout | off | — | not settable yet | Gap between bytes on a stream. |
Why the idle timeout is off, and not merely unset. A model that thinks for four minutes and then answers is indistinguishable, from outside, from a stream that has died - both are a connection sending nothing. Turning this on today would kill healthy work. It becomes settable once workloads emit heartbeats.
The platform's hard ceiling is 3600 s; the 900 s maximum above is policy and is the number that applies to you. A value above the maximum is clamped, not rejected, and the response tells you the value that was applied.
| What | Default | Maximum | Setting |
|---|---|---|---|
| Agent invoke (control plane waits this long for the agent) | 60 s | 900 s | timeout_seconds |
| MCP server request timeout | 60 s | 900 s | timeout_seconds |
run_python sandbox execution | 20 s | 60 s | timeout_s on the execute call; 400 — timeout_s must be between 1 and 60 |
| Sandboxed tool call | 30 s | 120 s | timeout_s; 400 — timeout_s must be between 1 and 120 |
| Waiting for a warm sandbox | 30 s | — | Then 503 — no sandbox is available right now - try again shortly |
| A tool sandbox becoming ready | 110 s | — | Tool calls have no warm pool, so they pay container start and image pull |
| Pub/Sub pull long-poll wait | 2000 ms | 20000 ms | timeout_ms |
| Pub/Sub acknowledgement deadline | 30 s | 600 s (minimum 1 s) | ack_deadline_seconds. On a push subscription this doubles as the per-request timeout — leave room for a cold start |
| MemoryStore API request | 30 s | — | REQUEST_TIMEOUT_SECONDS |
| MemoryStore live stats read | 3 s | — | — |
| Scale to zero after idle | about 90 s | — | Poll for zero instances with a generous timeout rather than sleeping a fixed time |
| A stale build claim is reclaimable after | 30 min | — | Until then a second deploy gets 409 — a build is already in progress for agent <name> |
platformctl deploy waiting for ready | polls every 2 s, up to 5 min | — | — |
platformctl invoke client timeout | 5 min | — | — |
Retention and history
| What | Retention | Notes |
|---|---|---|
| Persisted agent logs | 14 days | Survives scale-to-zero and revision rollouts. Setting: LOG_RETENTION |
| Live agent and serverless logs | none | Streamed from the newest running instance only; an idle, scaled-to-zero workload has no live logs (you get 200 and an explanation, not an error) |
| Audit log | kept, but with no export, no retention policy, and no date-range filter yet | See Break-glass and audit |
| Metrics history | none | The platform reports live instance counts and readiness; there are no historical charts, because no time-series database is deployed |
| Stored agent source | latest version only | No history, no version control — copy anything you want to keep |
| Pub/Sub messages | only while some subscription still owes an acknowledgement | Create subscriptions before publishing, or the message is reclaimed |
| VectorDB and MemoryStore data | until you delete it | No snapshots, no backups, no restore. Deleting an index or an instance is irreversible |
Credentials, tokens, and sign-in
| What | Value | Notes |
|---|---|---|
| Session token lifetime | 12 hours | HMAC-signed and stateless, so it is not revocable before it expires |
| API key expiry | 0 (never expires) or 1–3650 days | 400 — expires_in_days must be between 1 and 3650, or 0 for a key that does not expire |
| API key secret visibility | shown exactly once, at creation | Only a hash is stored. Lost it? Revoke the key and create another |
| Sign-in throttle | 10 attempts per IP and 50 per account, per 15 minutes | 429 with a Retry-After header |
| Password minimum | 12 characters, plus 3 of 4 character types | Same rule for new accounts, changes, and invitation acceptance |
| Break-glass grant | 4 hours by default, 24 hours maximum | Reason required, at least 8 characters, shown verbatim to the project's members |
Secret read token (:issue-token) | 300 s by default | Clamped to 3600 s. The store's own maximum lease may clamp it further — trust the returned ttl_seconds |
| Pub/Sub direct broker token | never expires | Revoking one means rotating the platform signing key for everyone |
| VectorDB direct-access token | non-expiring by default | Scoped to the exact collections that existed when it was minted, so refetch after creating an index |
| MCP server bearer token | minted once, reused across redeploys | Format: cai_mcp_ plus 64 hexadecimal characters |
| Invitation token | shown exactly once, and expires | Send the accept link immediately — it cannot be retrieved later |
Rate limits
| API | Limit | What you see |
|---|---|---|
workload-api | Per-principal token bucket, tuned by your administrator (RATELIMIT_RPS / RATELIMIT_BURST); anonymous callers are limited per IP | 429 |
serverless-api | 100 requests/second, burst 200, per principal per API replica; /healthz is exempt | 429 |
| VectorDB, MemoryStore, Pub/Sub | Per-principal token bucket on every route except health checks | 429 |
Service defaults worth knowing
These are not caps — they are what you get when you do not say otherwise.
| Service | Default |
|---|---|
| Serverless scaling | minimum 0 instances, maximum 10, 80 concurrent requests per instance |
| Serverless resources | 250m CPU / 512 MiB requested, 1 CPU / 512 MiB limit — applied only when you send no resources at all |
| Serverless port | 8080; exactly one port, named http1 or h2c |
| Serverless maximum instances | Sending max_scale: 0 on a serverless service behaves as 10, not unlimited. On an agent's config, max_scale: 0 does mean unbounded |
| Agent invoke authentication | open to anonymous callers; your administrator can close it. Writing to an agent's memory bank always requires a credential |
| Code sandbox warm pool | 3 sandboxes kept ready; each runs one snippet and is destroyed. Each is limited to 1 CPU / 512 MiB (requesting 50m / 128 MiB) |
| Sandboxed tool calls | 2 CPU / 1 GiB limit, 100m / 256 MiB request; no warm pool |
| VectorDB index | the platform embedding model's width, cosine distance, 1 shard, 1 replica, no quantization. Dimensions accept 1–65536 and replicas 1–8; only replicas and payload placement can be changed later |
| MemoryStore size classes | small 256 MiB container / 192 MiB usable, medium 1 GiB / 768 MiB, large 4 GiB / 3 GiB — usable memory is 75% of the container, on purpose |
| MemoryStore persistence | on, with a 2 GiB volume; eviction policy noeviction (writes fail at the limit rather than dropping keys) |
| Pub/Sub topic | 16 MiB max_bytes, no age limit, discard: old |
| Pub/Sub subscription | type shared, 30 s acknowledgement deadline, 5 delivery attempts, 1000 unacknowledged messages in flight, start_from: all, pull delivery |
Related pages
- API overview — pagination, error envelope, and rate limits in detail
- Quotas and audit — reading your project's live usage
- Core concepts — what a project, revision, and quota actually are
- Known issues — where platform limits bite today
- Data protection — encryption at rest and in transit
- Secrets API · MCP servers API · Platform API