Drex reference
Limits
Per-request size limits and per-account rate limits, with the exact rules the limiter applies.
Drex applies two kinds of limits. Request limits bound one call to POST /v1/systemone. Rate limits bound how many calls an account can make. Exceeding a request limit returns 422 invalid_request_error. Exceeding a rate limit returns 429 rate_limit_error. The messages are listed in Errors.
Request limits
| Limit | Value |
|---|---|
| Max questions per request | 512 |
| Max serialized body (state + questions) | 4,194,304 bytes |
drex-max-finance whole packed request limit (tokens) | 262,144 |
drex-max-v1.1 whole packed request limit (tokens) | 262,144 |
drex-v2.0 state limit (tokens) | 131,072 |
drex-v2.0 row limit (state + longest question, tokens) | 262,144 |
| Upstream evaluate timeout | 55s |
| Route maxDuration | 60s |
| Max active API keys | 3 |
Max questions per request. The number of keys in questions. Over the limit, the 422 issue has path questions and message at most 512 questions allowed. The minimum is one question. Each question is an object with a type of noul, choice or score; the Questions guide shows each shape. Drex Max has the same limit.
Max serialized body. Drex serializes state, questions and, on Drex Max, a nonempty images array together as JSON and measures the result in UTF-8 bytes. Other top-level fields such as model are not counted. An uploaded image counts only as its short workspace_file reference; an inline image counts as its base64 text. The check runs only after every other validation check has passed, and it is the only issue in the response: path empty, message request body must be at most 4194304 bytes. A 131,072-token state is about 580 KB for ordinary text.
State limit (Drex 2.0). The state alone, counted in the model's tokenizer the way the model reads it. Over the limit, the 422 issue has path state and message exceeds the 131,072-token state limit, with the model's own number.
Row limit (Drex 2.0). The state plus the longest question, counted the same way. Other questions don't add to it. Over the limit, the 422 issue has path state and message exceeds the 262,144-token limit, with the model's own number. A body can be under the byte limit and still over a token limit. The example in Errors has a question that exceeds the row limit on drex-v2.0.
Drex Max limits. Both served Max models apply their input limit to the whole packed request, including all questions and images. Retired ids follow the successor's limits. See Drex Max limits for how each version counts its input.
Upstream evaluate timeout. How long Drex waits for the model service to answer one request. When the wait runs out, the response is 529 overloaded with the message Drex is temporarily unavailable. Retry shortly. and retry-after-ms: 2000.
Route maxDuration. The total time the platform allows one request, including authentication, the rate limiter, and the model call. The evaluate timeout is set below it so the 529 is still delivered.
Max active API keys. Per account. Creating a key at the limit fails on the API Keys page. Revoked keys do not count, so revoke one to make room for another.
Request encoding
The JSON routes accept a compressed body when the request names its encoding in Content-Encoding. The TypeSafe SDKs, @typesafe-ai/sdk on npm and typesafe-sdk on PyPI, send zstd for any body over 1 KiB, so they work against Drex without a change. That needs Node.js 22.15 or newer, or Python 3.14 or newer (earlier Python needs typesafe-sdk[zstd]). Drex's own SDKs, nace-sdk for TypeScript and Python, send uncompressed JSON.
Content-Encoding | Body |
|---|---|
absent or identity | Plain JSON. |
zstd | A Zstandard frame. |
gzip | A gzip stream. |
deflate | The zlib format that HTTP defines for deflate, not raw deflate. |
Send one encoding per request. A list such as gzip, zstd and any other value return 422 invalid_request_error naming the supported encodings. A body that is not valid in its encoding returns 422 with body could not be decoded as zstd, naming the encoding sent.
Drex does not accept br. The platform in front of Drex answers any request whose Content-Encoding includes br, alone or in a list, with 415 Unsupported Media Type before Drex reads it, so that response has no request_id.
The size limit applies twice, to the bytes as sent and to the bytes after decoding, with the same message as the serialized body limit: request body must be at most 4194304 bytes. Drex stops decoding at the limit, so a small body that expands past it is refused. The platform also refuses a body above about 4.5 MB as sent, with 413 FUNCTION_PAYLOAD_TOO_LARGE and no request_id, before Drex reads it. So a body between 4 MiB and about 4.5 MB gets the 422 above, and a larger one gets the 413.
Rate limits
| Tier | RPM | In-flight |
|---|---|---|
| free | 120 | 8 |
| paid | 600 | 16 |
Both columns apply at the same time. A request must pass the RPM check and the in-flight check to run.
Document API
The document API has three independent sliding windows per account, separate from inference and without Drex in-flight caps:
| Window | Free requests/min | Paid requests/min | Account RPM override |
|---|---|---|---|
| Creates and other document requests | 120 | 600 | 1x |
| Job reads: get, list, events, request echoes and parse preview metadata | 600 | 3000 | 5x |
| Upload setup: grants and upload sessions | 240 | 1200 | 2x |
An RPM override replaces the base tier RPM before multiplying; for example, an override of 100 allows 100 creates, 500 reads and 200 upload-setup calls per minute. An unset RPM override keeps the tier base. Unlimited accounts and internal accounts without a custom limit remain uncapped. Internal accounts with a custom limit use the paid base for unset values. All API keys on an account share each window. File downloads and individual preview parts are exempt. Uploads and polls cannot consume create capacity, but each window can return its own 429. NDI has its own limits and may also return 429; follow Retry-After in seconds. See batch guidance.
Tiers
An account is paid when it has completed a top-up, has auto-pay on, or has paid a usage invoice. A top-up or a paid invoice keeps it paid even after that balance is spent or expired. Every other account is free, and an account that only had auto-pay on returns to free when auto-pay is turned off. The signup balance, the monthly allowance, and the card check at sign-up do not make an account paid. Drex caches the tier with the API key check for up to 5 seconds, so the new limits apply within that time after a change.
Custom limits
The tier's limits are the default. An account can carry its own limits instead, set by Nace. An account can be unlimited, or can have its own requests per minute, its own in-flight limit, or both. A limit left unset keeps the tier's value. An unlimited account skips the RPM and in-flight checks, the limit of 50 unfinished document jobs (browser runs keep it) and the per-account Workspaces windows on searches, QA turns, locates and reads. Custom limits apply within the same 5 seconds as a tier change.
Requests per minute
The RPM limit is a sliding window, not a calendar minute. Drex records the admission time of every admitted request. A new request is refused when the account already has RPM admissions in the previous 60,000 milliseconds. The refusal is 429 with message Requests per minute exceeded for this account. and retry-after-ms set to the time until the oldest admission leaves the window, never below 1000.
What counts toward RPM on POST /v1/systemone:
- Every request that passes the API key and funding checks, including requests that then fail body validation, exceed the token limit, or fail at the model service.
What does not count:
- Requests refused with
401or402, which never reach the limiter. - Requests refused by any rate limit. A refused request is not recorded anywhere.
GET /v1/models, which does not use the limiter.
In flight
A request holds one in-flight slot from admission until Drex has sent its response, success or error, and released the slot. If a slot is never released, for example because the function died, the limiter reclaims it 90 seconds after admission. A request that arrives while the account holds In-flight slots is refused with 429, message Too many concurrent requests for this account., and retry-after-ms: 1000. Free accounts also share a fixed part of each model pool, so that paid traffic always has room. When free accounts together hold all of it, a free request is refused with 429, message The free tier is at capacity. Retry shortly., and retry-after-ms: 2000; paid accounts never get this refusal.
Scope
Limits are per account. Every API key on the account draws from the same RPM counter and the same in-flight counter, and so does the dashboard Playground. The Playground runs the same validation, the same limiter call, and the same per-input-token debit as the API. Both validate the body and count its tokens before they call the limiter, so a request that fails validation, or is over the token limit, does not count toward RPM or take an in-flight slot.
Order of checks
The limiter evaluates account RPM, then account in-flight, in one atomic step. A request is recorded in both counters only when both checks pass. When either check fails, nothing is recorded.