Rate limits, retries and latency in production
A decision model on the request path is a dependency with a rate limit, a latency and a failure mode, and each needs an answer before launch. TypeSafe publishes 1,200 requests per minute and 250,000 tokens per second, returns 429 when you exceed them and 529 when it is overloaded, and says to retry both with exponential backoff, which its client libraries do by default. TypeSafe reports 70 to 500 milliseconds end to end, and Openlayer measured a p50 of 244 and a p95 of 371 milliseconds on its own benchmark. This page turns those into a timeout, a safe default action, a retry budget and a batching plan.
Published September 22, 2026. Editorial.
Key takeaways
- TypeSafe's published limits are 1,200 requests per minute and 250,000 tokens per second, and it notes they can be adjusted.
- A 429 means the rate limit was exceeded and a 529 means TypeSafe is temporarily overloaded; retry both with a wait that doubles each time, which the SDKs do by default.
- The Python SDK retries twice after the first attempt, from 0.5 seconds up to 5 seconds with 25 percent jitter, inside a 30-second budget, and respects Retry-After.
- Set a timeout near the p95 and define a safe default per decision for when the model does not answer: the cheapest action to undo.
- Batch questions into one request: TypeSafe measured 13 questions in one call at $0.000497 and 0.27 seconds against $0.006090 and 2.71 seconds as 13 calls.
The first production incident with a decision model is almost never the model being wrong. It is a 429 at the start of the hour when a batch job and the live traffic share one key, or a timeout that nobody set, leaving a request waiting on a decision it could have made by default. This page is the operations checklist we run before a Jev integration goes live.
The published limits
TypeSafe's models page gives two limits for jev-1.13.0: 250,000 tokens per second and 1,200 requests per minute, with a note that they can be adjusted [1]. The same page gives the context limit: 64k tokens per request, of which 32k is the ceiling for the state plus the longest question [1].
Those two limits interact with request shape. At 1,200 requests per minute, a product doing one Jev request per user message can serve 20 messages per second on one key before it reaches the request limit; the token limit at that rate is 12,500 tokens per request, which a state of a few thousand tokens never approaches. A batch job that screens a backlog is the opposite: it will reach the request limit long before the token limit, so it needs its own rate limiter in code, below 1,200, and ideally its own key so it cannot use up the capacity the live traffic needs.
The errors and what each means
The API reference lists four error codes [2]. A 401 means the key is missing or invalid, and a retry will not help. A 422 means the request body failed validation: a question without instructions, a Choice with more than 255 options, a state over the limit. A retry will not help with that either, and it should fail the request loudly in development, because it is a bug. A 429 means "You have exceeded your rate limit". A 529 means "TypeSafe is temporarily overloaded".
For 429 and 529 the docs say to retry "with exponential backoff instead of retrying immediately" [2]. Exponential backoff is a wait that doubles after each failed attempt (half a second, then one, then two), usually with a random fraction subtracted so that many clients retrying at once do not all retry in the same instant. That random fraction is called jitter.
What the SDK does by default
TypeSafe's Python SDK, the client library that wraps the API (the web service your code calls), documents its RetryPolicy [3]. The defaults: max_retries of 2, so three attempts in total; retryable statuses 408, 429 and every code from 500 to 599, which covers 529; backoff_initial of 0.5 seconds, doubling each time; backoff_max of 5.0 seconds; backoff_jitter of 0.25, which subtracts up to 25 percent from each delay at random; and a timeout of 30.0 seconds as the total budget for all attempts and delays in one call. The policy respects a Retry-After header when the server sends one, and it retries connection errors and timeouts unless told not to. The SDK's constants page gives the default per-request HTTP timeout as 10.0 seconds and the default model as jev-latest [4].
Two of those defaults need changing for a request-path decision. A 30-second budget is right for a batch job and wrong for a user waiting on a route: the request path needs a short budget with fewer retries, and the batch path can keep the long one. And the default model alias, jev-latest, moves when TypeSafe releases a new version [1]; versions, drift and monitoring is the argument for pinning.
The JavaScript SDK, @typesafe-ai/sdk on Node 20 or newer, reads the same TYPESAFE_API_KEY [5]; we have read its retry defaults only in the Python documentation, so confirm the JavaScript values in its own reference before relying on them.
Latency, and what to set the timeout to
TypeSafe reports 70 to 500 milliseconds end to end [6]. Openlayer, running its jevals guardrails through a gateway, measured a p50 of 244 milliseconds and a p95 of 371 milliseconds per request on its own benchmark [7]. A p50 is the time that half of requests complete within; a p95 is the time 95 percent complete within, so one request in twenty takes longer. TypeSafe's own cookbooks report similar figures: 0.16 to 0.31 seconds for a wide Choice over 182 options and 0.09 to 0.12 seconds for a narrow re-check [8], 0.27 seconds for 13 questions against a 53,777-character document [9], and 111 milliseconds per call in the self-consistency cookbook [10]. Those are the vendor's measurements on the vendor's tasks, and your own p95 from your own region and request shape is the number to set a timeout against.
The timeout rule we use: measure your p95 for a week, set the request-path timeout at twice it, and set the retry policy on that path to one retry with a budget that fits inside the user-facing deadline. A route that has to answer inside a second cannot afford the SDK's default 30-second budget, and a timeout that expires should be treated the same as a 529: use the safe default.
Because questions run in parallel, adding questions to a request barely changes its time [11], so latency is decided by the state size and the round trip rather than by the number of decisions. That is the reason to batch.
The safe default when Jev does not answer
Every decision on the request path needs a written answer to "what happens if the model does not reply in time". The answer is the action that costs least to undo, chosen per decision and coded before launch.
For a route: send the request to the general queue and mark it unrouted, so a person or a later retry can place it. For an input screen: hold the message for review rather than passing it, if the product can tolerate the delay; pass it with a flag and screen the output if it cannot. For an output screen: hold the reply. For a tool-call gate: confirm, never run, because a gate that lets calls through when it cannot decide is no gate. For an extraction: leave the field empty and flag it. For an eval grade: mark the check as not run rather than passed.
The point is that the default is never "act as if the model said yes". TypeSafe's confidence page says of the low band, "Don't guess" [12], and an absent answer is the lowest confidence there is.
Record every use of the default with the reason (timeout, 429, 529, connection error), because a rise in that count is the earliest sign of a limit being reached or an outage starting.
Batching to cut requests
The request limit, the latency and the cost all improve when questions share a request, and TypeSafe's parallel questions cookbook measured it. Thirteen questions (eight Nouls, two Choices, three Scores) against the 53,777-character Wikipedia article on the GDPR, five repeats each way on jev-1.12: one batched call cost $0.000497 and took 0.27 seconds; thirteen single calls cost $0.006090 and took 2.71 seconds. TypeSafe reports that as 12.2 times cheaper and 10.0 times faster, and reports a standard deviation of 0.0 across the repeats for 11 of the 13 questions, with the other two under 0.01, so batching neither shifted the answers nor added variance [9]. The saving comes from sending the document once instead of thirteen times, because the document dominates every request.
For a request-path product the rule is one request per message with every question that message might need: the route, the screen, the score, asked together and filtered in code, which is TypeSafe's speculative fan-out pattern [13]. For a batch job the rule is one request per document with every question about that document. The limit is the 32k-token ceiling on state plus the longest question [1].
Batching has one cost: a 429 or a timeout on a batched request loses every answer in it, so the safe default has to be defined for the whole request.
A launch checklist
Separate keys, or at least separate limiters, for live traffic and batch jobs. A rate limiter in code below 1,200 requests per minute on every batch path. Retry on 429 and 529 only, with backoff and jitter, and never on 401 or 422. A per-path retry budget: short on the request path, long on batch. A timeout at twice the measured p95 on the request path. A safe default per decision, coded and logged. Batching on by default, with the state under 32k tokens. Alerts on the rate of 429s, 529s, timeouts and defaults used. A pinned model version rather than the alias.
Where Reveneau fits
Reveneau runs its own eval suite through Jev with the batch-side settings above (a long budget, a code rate limiter, one request per check group), which is part of why the suite runs ten times faster on our own suite than it did with a language model as the grader; Evals with Jev describes the setup. In the products we build, every request-path decision ships with its timeout, its safe default and its alert, written down beside the question. What it costs and how long it takes to release an AI agent covers where this operations work sits in the plan, and why observability matters more with generated code is the argument for the alerts.
Best for
- Any Jev integration on a request path where a user is waiting.
- Batch jobs that screen or grade a backlog and share a key with live traffic.
- Teams that need a written answer to what happens when the model does not reply.
Avoid if
- You plan to keep the SDK's 30-second default budget on a request that has to answer in a second.
- There is no safe default and a missing answer would be treated as a yes.
- Batch and live traffic share one limiter and one key with no separation.
Check before you decide
- 429 and 529 are retried with backoff and jitter; 401 and 422 are not retried.
- The request-path timeout is set from a measured p95 and the retry budget fits the user deadline.
- Each decision has a coded safe default and the count of defaults used is alerted on.
- Questions are batched per message or per document, under the 32k-token state limit.
Common questions
What are Jev's rate limits?
TypeSafe's models page publishes 1,200 requests per minute and 250,000 tokens per second for jev-1.13.0, with a note that the limits can be adjusted, and a context limit of 64k tokens per request with 32k for the state plus the longest question. At one request per user message that is 20 messages per second on one key. A batch job reaches the request limit long before the token limit, so it needs its own limiter in code and ideally its own key.
What do the 429 and 529 errors mean and how should code handle them?
A 429 means you have exceeded your rate limit; a 529 means TypeSafe is temporarily overloaded. The API reference says to retry both with exponential backoff instead of retrying immediately: wait, then double the wait after each failure, with a random fraction subtracted so many clients do not retry in the same instant. A 401 (bad key) and a 422 (request failed validation) are bugs and should fail loudly rather than be retried.
What is the SDK's default retry policy?
The Python SDK's RetryPolicy defaults to two retries after the first attempt, retrying on 408, 429 and every status from 500 to 599, with delays starting at 0.5 seconds, doubling to a cap of 5 seconds and up to 25 percent jitter subtracted, inside a 30-second total budget per call. It respects a Retry-After header and retries connection errors and timeouts by default. The per-request HTTP timeout defaults to 10 seconds and the default model is the jev-latest alias.
Why change the SDK defaults on a request path?
Because a 30-second budget is right for a batch job and wrong for a user waiting on a route. On the request path set one retry and a budget that fits inside the user-facing deadline, and treat a timeout the same as a 529 by using the safe default. Also replace the jev-latest default with a pinned version, because TypeSafe's models page says an alias moves when a new release ships and the answers behind it can change without a change on your side.
What latency should I plan for?
TypeSafe reports 70 to 500 milliseconds end to end. Openlayer measured a p50 of 244 milliseconds and a p95 of 371 milliseconds on its own benchmark, where half of requests beat the p50 and 95 percent beat the p95. TypeSafe's cookbooks report 0.09 to 0.31 seconds for routing stages and 0.27 seconds for 13 questions on a 53,777-character document. Measure your own p95 for a week from your own region and set the timeout at twice it.
What should happen when Jev does not answer in time?
The action that costs least to undo, chosen per decision and coded before launch. Route to the general queue; hold a message or a reply for review; confirm rather than run a tool call, since an undecided gate must never let a call through; leave an extracted field empty; mark an eval check as not run. Never act as if the model said yes. Log each use of the default with its reason, since a rise in that count is the earliest sign of an outage.
How much does batching questions save?
TypeSafe's cookbook measured 13 questions against the 53,777-character GDPR article: one batched call cost $0.000497 and took 0.27 seconds, against $0.006090 and 2.71 seconds for 13 single calls, which TypeSafe reports as 12.2 times cheaper and 10.0 times faster. Across five repeats, 11 of 13 questions had a standard deviation of 0.0, so batching changed neither the answers nor their variance. The saving is from sending the document once instead of 13 times.
What is the right batching unit?
One request per message on the request path, carrying every question that message might need (route, screen, score) and filtering the answers in code, which is TypeSafe's speculative fan-out pattern. One request per document on a batch path, with every question about that document. The limit is 32k tokens for the state plus the longest question. The one cost is that a failed batched request loses every answer in it, so define the safe default for the whole request.
Should live traffic and batch jobs share a key?
Not if you can avoid it. A backlog job that screens a million records will reach 1,200 requests per minute and, on one shared key, use up the capacity the routes and screens that users are waiting on need. Give the batch job its own key or at least its own limiter in code set below the published limit, keep the long retry budget there, and keep the short budget and the safe default on the live path. Alert on 429 rates separately for each.
What should be alerted on for a Jev integration?
The rate of 429s, 529s and timeouts, each separately; the count of safe defaults used per decision; the p95 latency against the timeout; and the token count per request against the 32k state limit. A rising default count with a flat error rate points to latency; a rising 429 rate with flat traffic points to a batch job sharing the key. Include the model version from each response, so a shift in any of these can be matched to a release.
How does Reveneau run Jev in its own eval suite?
With batch-side settings: a long retry budget, a rate limiter in code below the published limit, and one request per group of checks so the rubric is sent once. That is part of why our eval suite runs ten times faster on our own suite than it did with a language model as the grader. In the products we build, every request-path decision ships with its timeout, its safe default and its alert written beside the question, and the model version is pinned.
References
- [1] TypeSafe docs, Models: 250,000 tokens per second and 1,200 requests per minute (with dynamic adjustment noted); 64k tokens per request, 32k for state plus the longest question; an alias moves when a new release ships.
- [2] TypeSafe docs, API reference: 401 Missing or invalid API key; 422 Request body failed validation; 429 You have exceeded your rate limit; 529 TypeSafe is temporarily overloaded; retry 429 and 529 with exponential backoff instead of retrying immediately.
- [3] TypeSafe docs, Python SDK RetryPolicy: max_retries=2; retryable statuses 408, 429 and 500 to 599; backoff_initial=0.5, backoff_max=5.0, backoff_jitter=0.25; timeout=30.0 total budget; respects Retry-After; retries connection and timeout errors by default.
- [4] TypeSafe docs, Python SDK constants: default model jev-latest; default HTTP timeout 10.0 seconds; environment variables TYPESAFE_API_KEY, TYPESAFE_BASE_URL, TYPESAFE_DEFAULT_MODEL, TYPESAFE_LOG_LEVEL.
- [5] TypeSafe docs, JavaScript SDK: package @typesafe-ai/sdk, Node.js 20 or newer, reads TYPESAFE_API_KEY.
- [6] TypeSafe, Introducing System One models and Jev: 70 to 500 ms end to end.
- [7] Openlayer, jevals README: p50 244 ms, p95 371 ms per request on Openlayer's own benchmark through a gateway.
- [8] TypeSafe docs, Skill suggestion cookbook: stage one 0.16 to 0.31 s, stage two 0.09 to 0.12 s.
- [9] TypeSafe docs, Parallel questions cookbook: 13 questions against the 53,777-character GDPR article on jev-1.12, five repeats each way; batched $0.000497 and 0.27 s; single calls $0.006090 and 2.71 s; 12.2x cheaper, 10.0x faster; standard deviation 0.0 for 11 of 13 questions.
- [10] TypeSafe docs, Self-consistency Noul cookbook: jev-latest 111 ms and $0.000043 per call.
- [11] TypeSafe docs, Introduction: Adding questions barely changes the response time.
- [12] TypeSafe docs, Confidence: below 0.5 the model is genuinely unsure; Don't guess.
- [13] TypeSafe docs, Speculative fan-out pattern: send many questions in a single call and let code decide what is relevant.
Related reading
Why observability matters more when a machine wrote the code
When a person writes a system, someone carries a mental model of it. When a model writes it, nobody does, and production becomes the only place where you can see what the system actually does.
The real price of an LLM judge is per decision, and it multiplies
A grader's price per call looks small. Multiply it by every check on every change, or every message through a guardrail, and the public figures from LangChain, Openlayer and TypeSafe show where a cheaper decision model pays for its own migration and where it does not.
What belongs in a runbook nobody reads
Most runbooks are written for a calm reader who has time. The person who opens one is tired, frightened, and has about ninety seconds, and almost nothing in a normal runbook is useful to that person.