Limits reference
This page lists the main limits that Spanlight enforces. Each value comes from the code, so it is exact. Limits that belong to one feature are also on that feature’s page. A limit you cannot change is fixed in the code. A limit that has a setting names it.
Ingestion
Section titled “Ingestion”These limits apply to POST /v1/traces and POST /v1/otlp/traces. See How ingestion works for the full pipeline.
| Limit | Value | When you exceed it |
|---|---|---|
| Request body | 5 MB (5 MiB), measured after decompression | 413 PAYLOAD_TOO_LARGE |
| Spans in one batch | 1,000 spans | 413 PAYLOAD_TOO_LARGE |
| Input or output of a span | 1 MB (1 MiB) each, after redaction | The text is cut to its first 1 MB and the span is marked truncated. The span is stored. |
| Attributes of a span | 64 KB of JSON and 256 entries | The span is refused. Attributes are never truncated. |
| Age of a span | A span may not start more than 90 days ago | The span is rejected. |
| Clock skew | A span may not end more than 10 minutes in the future | The span is rejected. |
Web searches on a span (usage.server_tool_use.web_search_requests) |
0 to 100,000 | The span is rejected. |
| Trace metadata | 32 keys of 1 to 64 characters (a-z, 0-9, _, ., -), values of 1 to 256 characters |
Native JSON rejects the span. The SDK, OTLP and the gateway drop the entry. See Attribution. |
A rejected span does not fail the other spans of the batch. The response lists each rejection.
API request bodies
Section titled “API request bodies”Every /api/v1 request body is limited to 1 MiB. A larger body gets 413 PAYLOAD_TOO_LARGE. Ingestion has its own limit (5 MB, above). The dataset upload route and the billing source CSV upload route have a limit of 5 MB of file plus 64 KB of multipart framing.
Rate limits
Section titled “Rate limits”Most rate limits are token buckets. A bucket refills at a steady rate and holds a burst. A request takes one token. When the bucket is empty, the request gets 429 RATE_LIMITED and a Retry-After header in whole seconds. The buckets live in Postgres, so the limits hold across any number of API replicas. If the limiter cannot reach the database, the request is let through.
| What | Rate | Burst | Counted per |
|---|---|---|---|
| Ingestion, score ingestion and prompt fetches with an API key | 50 a second | 100 | API key |
| Reads with an API key or a personal access token | 20 a second | 40 | Credential |
| Starting a demo session | 10 an hour | 10 | Client address |
| Playground runs and replays, together | 30 a minute | 30 | Person |
| Alert rule previews | 30 a minute | 30 | Person |
| Test deliveries to an alert channel | 10 an hour | 10 | Channel |
| Billing source refreshes | 1 every 5 minutes | 2 | Billing source |
Three notes on that table:
- The first row is one bucket.
POST /v1/traces,POST /v1/otlp/traces,POST /v1/scoresandGET /v1/prompts/{name}all draw from the same bucket of the same key. - The second row applies to read requests that carry a bearer credential. Requests from the dashboard with a session cookie are not limited by it.
- A key that has no
ingest:writescope does not spend tokens on ingestion routes. - A billing source refresh refused because a fetch of the source is still running (
409 BILLING_FETCH_IN_PROGRESS) has already spent its token.
Gateway limits
Section titled “Gateway limits”The gateway has its own limits. A gateway key can carry a limit of requests a minute and a limit of tokens a minute. An instance can also set GATEWAY_ORG_RPM_CEILING (1 to 100,000) as a ceiling on requests a minute for each organization. The ceiling is checked first, then the key’s requests, then the key’s tokens.
- A requests limit is a token bucket that refills continuously. Its burst equals the limit.
- A tokens limit is a fixed window for each UTC clock minute. A call that crosses the limit is let through and its tokens count. The next call is refused until the minute ends.
A refused call gets 429 RATE_LIMITED in the gateway’s own error format. See Gateway errors.
Attempts counted in a time window
Section titled “Attempts counted in a time window”Some limits count attempts in a window instead of using a bucket. They also answer 429 RATE_LIMITED with Retry-After.
| What | Limit | Counted per |
|---|---|---|
| Failed sign-ins | 5 in 15 minutes | Email address |
| Failed sign-ins | 20 in 15 minutes | Client address |
| Sign-ups | 10 an hour | Client address |
| Verification emails | 3 an hour | Account |
| Mailed invitations | 20 an hour | Organization |
| Mailed invitations | 50 an hour | Person who invites |
| Password reset requests | 3 in 15 minutes | Email address |
| Password reset requests | 10 in 15 minutes | Client address |
| Attempts to turn off two-factor authentication | 5 in 15 minutes | Person |
| New versions of a prompt | 100 a day (UTC) | Prompt |
Link-only invitations are not counted. A prompt gets at most 100 new versions in a UTC day. The 101st gets 429 RATE_LIMITED with a Retry-After that lasts until midnight UTC. See Accounts and sign-in for what each limit means for a user. Set TRUSTED_PROXIES so that Spanlight sees the real client address; see Configuration.
Other caps
Section titled “Other caps”| Limit | Value | When you exceed it |
|---|---|---|
| Alert channels in an organization | 20 | 422 LIMIT_EXCEEDED |
| Alert rules in a project (budget rules do not count) | 100 | 422 LIMIT_EXCEEDED |
| Budgets in a project | 50 | 422 LIMIT_EXCEEDED |
| Promoted metadata keys in a project | 5 | 422 VALIDATION_ERROR |
| Score configs (distinct score names) in a project | 500 | Creating a config answers 422 VALIDATION_ERROR with a name field error. In POST /v1/scores, a score whose new name would pass the cap is listed in rejected and the rest of the batch is stored. |
| Alert channels on one rule | 10 | The rule is refused. |
Scores in one POST /v1/scores batch |
1,000 | 422 VALIDATION_ERROR |
| Configurations in one playground run | 3 | 422 VALIDATION_ERROR |
| Page size of a list | 200 | 422 VALIDATION_ERROR |
Payloads in one trace-detail response (GET …/traces/{trace_id}) |
8 MB (8 MiB) of stored input and output, in span start order | Later spans come back with input and output null and payload_omitted: true; read each with GET …/traces/{trace_id}/spans/{span_id}. |
Evaluation, playground and requests
Section titled “Evaluation, playground and requests”| Limit | Value | When you exceed it |
|---|---|---|
| Evaluators on one experiment | 20 | 422 VALIDATION_ERROR |
Cost ceiling of one experiment (max_cost_usd) |
Above 0, at most USD 1,000 | 422 VALIDATION_ERROR |
Output tokens of an LLM judge evaluator (max_tokens) |
4,096 | 422 VALIDATION_ERROR |
| Variables in one playground run | 100 | 422 VALIDATION_ERROR |
Output tokens of a playground configuration (max_tokens) |
4,096 | 422 VALIDATION_ERROR |
| Length of an insight mute | At most 90 days ahead | 422 INVALID_MUTE |
| Reason for an insight mute | 1 to 500 characters | 422 INVALID_MUTE |
Idempotency-Key header |
1 to 128 visible ASCII characters | 422 VALIDATION_ERROR |
| Output tokens of a replay | 4,096. A stored request that allows more is sent with 4,096; a max_tokens above 4,096 in the request is refused |
422 VALIDATION_ERROR |
| Edited request of a replay | 256 KB | 422 REPLAY_BODY_TOO_LARGE |
| Replay spend in a project | REPLAY_MONTHLY_BUDGET_USD a UTC month, 5 US dollars by default |
409 REPLAY_BUDGET_EXCEEDED; nothing is sent |
Daily review
Section titled “Daily review”| Limit | Value | When you exceed it |
|---|---|---|
| Review policies in a project | 1 | PUT /review-policy replaces the one there is |
Traces queued a day (daily_cap) |
1 to 500, 50 by default; a quarter, rounded up, is held for random picks | 422 VALIDATION_ERROR. Over the cap, candidates are left out and the day’s selection says the cap was reached |
Random share (random_rate) |
0 to 0.05, four decimals | 422 VALIDATION_ERROR |
| Environments in a policy | 20, each 1 to 64 characters, none of evals, playground, experiments or doctor |
422 VALIDATION_ERROR |
| Days a manual selection can cover | A finished UTC day, from yesterday back to 7 days | 422 DAY_NOT_COMPLETE, 422 DAY_TOO_OLD |
| Selections of one day | 1 per project | 409 ALREADY_SELECTED |
| Duration of a trace the policy can pick | 1 hour | A longer trace is never selected |
| Traces of a name needed for a cost outlier | 30 priced traces in the 7 UTC days | With fewer, no trace of that name is a cost outlier |
| Selection history page | 30 days | 422 VALIDATION_ERROR |
| Failure categories | 2 to 30, including none |
422 VALIDATION_ERROR |
The nightly selection runs from 01:00 UTC and reads the traces that ended during the previous UTC day. See Daily review.
Exports
Section titled “Exports”| Limit | Value |
|---|---|
| Time range of a trace export | At most 90 days. from and to are required. |
| Traces in one export | 100,000. More matching traces fail the export as too large. Narrow the range. |
| Events in one audit log download | 50,000 |
| Download link | Valid for 1 hour |
| File retention | 7 days after the export finishes |
See Exports for how to request one.
Datasets
Section titled “Datasets”| Limit | Value |
|---|---|
| Items in a dataset | 10,000 |
| Input or expected output of an item | 64 KB each |
| Metadata of an item | 8 KB |
| Dataset file upload | 5 MB |
An item that is over a size limit is refused with 422 VALIDATION_ERROR. Adding items that would pass 10,000 is refused with 422 DATASET_FULL, and the whole batch is refused. An upload over the limit gets 413 PAYLOAD_TOO_LARGE. See Datasets.
Billing sources
Section titled “Billing sources”A billing source connects a provider account to an organization, with an admin key or as CSV uploads. See Reconcile cost with your provider bill.
| Limit | Value | When you exceed it |
|---|---|---|
| Admin key | At most 512 characters after trimming, starting with sk-ant-admin (Anthropic) or sk-admin (OpenAI) |
422 VALIDATION_ERROR on admin_key |
| Name | 1 to 128 characters, unique in the organization | 422 VALIDATION_ERROR, or 409 BILLING_SOURCE_NAME_TAKEN |
| Provider scope | 1 to 128 characters | 422 VALIDATION_ERROR |
| Projects on one source | 500 | 422 VALIDATION_ERROR |
| Sources one project is compared with | One per provider | 409 BILLING_SOURCE_PROJECT_TAKEN |
| Key check when a source is added or its key replaced | One request to the provider, 10 seconds at most | The source is stored anyway, with status failing and the reason in last_error: an HTTP status line such as 401 Unauthorized, or a short phrase such as timeout. With BILLING_SOURCES_ENABLED off, no check runs and the status is never_fetched. |
| Key checks in one organization | Five, then one a minute (adding and replacing keys together) | 429 RATE_LIMITED with Retry-After; nothing is checked or stored |
| Manual refresh | One every 5 minutes per source, bursts of 2; one fetch of a source at a time | 429 RATE_LIMITED with Retry-After; 409 BILLING_FETCH_IN_PROGRESS while a fetch or upload of the source runs (a fetch still running after 15 minutes no longer counts) |
| Days one fetch reads | The last 7 whole UTC days, or 31 on a source’s first successful fetch | Not configurable. The daily fetch runs from 03:30 UTC. |
| Provider pages one fetch reads | 40 pages, 4 MB each | The fetch fails with too many pages or response too large, and nothing from it is stored. |
| Cost CSV upload | 5 MB, 100,000 data rows, UTF-8, a header with all ten columns, each day within the 400 days up to today (UTC) |
413 PAYLOAD_TOO_LARGE over 5 MB; 422 VALIDATION_ERROR with up to 20 reasons by row and column otherwise, and nothing is stored |
| Reconciliation report window | At most 31 whole UTC days, ending today at the latest; the last 14 days by default | 422 VALIDATION_ERROR on to |
| Provider cost lines kept | 400 days | Older lines are deleted by the retention job. |
Retention
Section titled “Retention”Each project keeps its spans for a number of days. The default is 30. You can set any whole number from 1 to 90 with retention_days on the project. The worker runs the retention job every hour and deletes the traces, and with them the spans, that are older than the project keeps. Ingestion refuses spans that started more than 90 days ago, so 90 days is the longest retention that makes sense.
Time windows in the trace and metrics API are limited to 90 days as well.
Per-user figures on the Users page are recomputed every 15 minutes for the last three UTC days and nightly for the last eight. A trace counts for the UTC day it started, and the last pass that covers that day is the nightly one seven days later, so a span that arrives after it, about a week after the trace’s day, is not counted in them. Send spans soon after the call. See Releases and users.
Related pages
Section titled “Related pages”- How ingestion works: what happens to a batch, and why a span is rejected.
- Data, limits and operations: how long-window metrics, backups and monitoring work.
- Roles and permissions: who can do what, and which API key scopes exist.
- Error codes: what each status and code means.
- Troubleshooting: fixes for rejected spans and
429responses.