Rate limits

Three different budgets can each return 429, and every one sets Retry-After in seconds. They are counted on separate ledgers, so a response can be far from the request-rate limit and still be refused by one of the others. The code in the error body says which.

Rate limits
codeCounts
rate_limit_exceededRequests per minute, per credential. On /v1/public/, requests per minute per IP.
create_rate_limitedRun submissions per minute, per account.
too_many_streamsConcurrent live streams, per account.

Request rate limit

  • 1000 requests per minute per credential, burst allowance 50.
  • Over the limit: HTTP 429, code rate_limit_exceeded, Retry-After header in seconds.
  • Every credentialed /v1/ response, success or error, carries these headers:
Request rate limit
HeaderMeaning
X-RateLimit-LimitSustained requests-per-minute limit for this credential.
X-RateLimit-RemainingTokens remaining in this bucket: 0 on a rate_limit_exceeded 429; the other two 429s use separate budgets and may leave a positive balance here.
X-RateLimit-ResetUnix timestamp marking the end of the current rate-limit window.

The SDK retries 429 with backoff and honors Retry-After. Default: max 3 retries, exponential backoff (0.5 s, 1 s, 2 s) plus jitter. Configure with Client(max_retries=...).

Contact support for higher per-key limits on a specific integration.

Submission limits

Per account, per minute, counted separately by endpoint:

  • POST /v1/sweeps — 30 per minute.
  • POST /v1/dynamic-simulations — 30 per minute.
  • POST /v1/simulations — 120 per minute.

Over the limit: HTTP 429, code create_rate_limited, Retry-After header. The body carries retry_after_s, limit_per_minute, and kind.

The limit counts submissions, not running work. A submission that parks the run (launch_intent: "queue") counts the same as one that dispatches it.

Validating and reserving a large grid is real work on the request path, and an unpaced burst of large sweep submissions degrades latency for every endpoint on the account. A client that submits a queue of studies should pace itself rather than discover the wall.

One sweep carrying a component_axes axis replaces a loop of single-point submissions, and it is one request rather than hundreds. See Sweeps.

Catalog reads

The component catalog is open. There is no datasheet budget, no filter budget, and no lease. GET /v1/components, GET /v1/components/{id} and GET /v1/components/{id}/specs return the same datasheet on every tier, and they cost nothing beyond the per-credential request bucket above. Filter bounds take the value you give them on every tier. See Components.

The spec_rate_limited and filter_rate_limited codes, the X-Spec-Budget-Remaining and X-Spec-Budget-Reset headers, and the 402 on the free tier are all retired. Nothing returns them.

Public reads without an account

GET /v1/public/ reads — the open catalog and the anonymous run pages — take no credential, so they are counted per IP instead of per key.

  • 60 requests per IP per minute.
  • Over the limit: HTTP 429, code rate_limit_exceeded, Retry-After header. The body carries retry_after_s.
  • Anonymous solves (POST /v1/public/simulate) have their own window: 20 per IP per minute.

Simulation execution concurrency

HTTP request rate and simulation execution capacity are separate controls.

Free runs one simulation at a time. Pro runs up to five.

The execution limit is account-wide. Dashboard submissions and every API key owned by the account share it. Extra dispatched runs stay queued and start when a slot frees. They do not return a concurrency 429.

Service-wide overload or maintenance protection can still pause new submissions. That control is independent of the paid plan's unlimited usage policy and the account execution queue.

GET /v1/users/me returns concurrency: {limit, active, queued} for the account so a client can read the shared queue directly. limit is the account execution-slot ceiling, active is the number of slots in use, and queued is the number waiting.

Live streams

Server-Sent-Events progress streams (GET /v1/simulations/{id}/stream and the sweep and dynamic equivalents) share a limit of 20 concurrent open streams per account. Opening more returns HTTP 429 with code too_many_streams.

Each stream has a maximum lifetime of 30 minutes. If the run is still going when the stream reaches that limit, reconnect to keep watching. A stream always ends on its own when the run reaches a terminal state.

See Errors for the envelope and the per-code anchors, and Compute units for metering, which is a different thing from rate limiting.