SentX SI Blog Meet Victoria

Make your model API calls survive rate limits, timeouts, and partial failures

· 7 min read

SentX SI

Work disappears from model API integrations for one reason: the client treats every failure the same. A transient rate-limit error, a transient overload, a quota error that never clears on its own, and a stream that died halfway through are all "an exception" to code that catches and moves on — or that crashes and loses the item entirely. The fix is a decision method rather than more retries: classify each failure before acting, and make sure every request ends in a recorded state — succeeded, deferred, failed with a record, or partially completed with a record. Nothing is silently dropped when nothing is allowed to vanish without a trace. This guide follows OpenAI's documented rate-limit behavior; the shape of the method transfers, but other providers define their own codes and headers, and their documentation is where you get those values.

Tell the two transient errors apart

OpenAI's documentation distinguishes two transient conditions that look alike in a log but demand different recovery. Rapid traffic increases return HTTP 429 with the error code slow_down; temporary model overload returns HTTP 503 with server_is_overloaded. The client must branch on status plus error code, not status alone, because other errors reuse both statuses.

Source: Rate limits | OpenAI API

HTTP status Error code What it means Recovery
429 slow_down Your request rate increased too quickly Follow Retry-After when present, reduce your rate, then increase it gradually
503 server_is_overloaded The requested model is temporarily overloaded Follow Retry-After when present, then retry; widen the delay between attempts if it continues
429 or 503 anything else A different condition shares the status Read the error body before choosing a recovery action

One property of slow_down surprises people: it can fire even when you are inside your requests-per-minute and tokens-per-minute limits. It measures how fast your traffic rose, not whether you exhausted the limit — so the correct response is to slow the ramp, not just to wait.

Source: Rate limits | OpenAI API

In the official SDKs, these map to distinct exception types, so handlers can branch on exception as well as status: Python, TypeScript, and Ruby raise RateLimitError for 429 and InternalServerError for 503; Java raises RateLimitException and InternalServerException.

Source: Rate limits | OpenAI API

Treat Retry-After as a floor, then bound the fallback

When a valid Retry-After header is present, wait at least that long and add a small random delay so concurrent clients do not re-collide on the same second. When the header is missing or invalid, fall back to exponential backoff with jitter — start short, double after each failed attempt, add randomness — and cap both the number of attempts and the total wall-clock time spent retrying. Two further rules from the documentation keep this from misbehaving in production:

Source: Rate limits | OpenAI API

  1. Official SDKs already retry eligible 429 and 503 responses. Check the retry behavior of your installed version, and if you manage retries in your application, disable SDK retries or fold them into your limits so nested loops do not multiply requests.Source: Rate limits | OpenAI API
  2. If a valid server-supplied delay exceeds your configured maximum retry delay, stop retrying and defer the request rather than retrying sooner.Source: Rate limits | OpenAI API

A compact fallback loop, adapted from the manual example in the documentation:

Source: Rate limits | OpenAI API

import random
import time
from openai import OpenAI, RateLimitError, InternalServerError

client = OpenAI()

def with_backoff(func, initial_delay=1.0, base=2.0, max_retries=6,
                 errors=(RateLimitError, InternalServerError)):
    delay = initial_delay
    for attempt in range(max_retries):
        try:
            return func()
        except errors:
            if attempt == max_retries - 1:
                raise
            delay = delay * base * (1 + random.random())
            time.sleep(delay)

The documentation's own examples deliberately stop at this fallback path: they do not read Retry-After, so a production wrapper adds that check in front of the backoff and prefers the server's number whenever it is valid.

Source: Rate limits | OpenAI API

Never retry errors that need you

Quota, billing, and other user-action errors are not transient. The documentation is explicit that Retry-After does not mean such errors can be resolved by waiting, and it tells clients not to retry them. A hard spend limit, for instance, surfaces as a 429 that no amount of backing off will clear. These errors should propagate out of the retry layer immediately, land in a place an operator will see, and stop the affected workload — retrying them only burns budget against a wall that will not move.

Source: Rate limits | OpenAI API

Streaming fails in a different shape

For streaming requests, the HTTP error response applies before the stream starts. Once the stream is flowing, an error can arrive as a stream event, mid-output. The rule the documentation gives is blunt: do not automatically replay a request after consuming output. A correct handler keeps whatever it received, records the item as partially completed, and decides what to do with the remainder — reissue only the unfinished piece, or flag it for review — instead of firing the whole request again and doubling what already happened.

Source: Rate limits | OpenAI API

Timeouts are a separate axis

A per-attempt timeout is not a deadline for the whole operation, and a canceled request or expired deadline can stop retries without ever returning the HTTP error your handler is watching for. Handle cancellation and timeout explicitly as their own outcomes, with their own recorded states, or they become another silent hole.

Source: Rate limits | OpenAI API

Why tight loops make it worse

Failed requests still consume your per-minute quota, so continuously resending a rejected request cannot work — every retry competes with your real traffic for the same budget. Two levers reduce pressure at the source: set max_tokens close to the response size you actually expect, since the limit calculation takes the larger of that value and the estimated input tokens, and move work that does not need an immediate answer to the Batch API, which runs large collections without drawing on your synchronous rate limits.

Source: Rate limits | OpenAI API

On ramping new traffic, the documentation offers a rule of thumb, flagged as approximate: once you reach 1 million input tokens per minute, grow by no more than 50% every 15 minutes, because the exact point at which ramp-rate limiting kicks in varies by model and conditions.

Source: Rate limits | OpenAI API

When the endpoint isn't OpenAI's

The method travels; the constants don't. Each provider defines its own statuses, error codes and headers, so a client built for one has to re-read the documentation of every other before its handler can trust a single number. Worth checking early is whether the provider publishes its current limits at all. SentX SI, the Dubai-based research and product company behind the Victoria model, does: its public model card states that availability, daily limits and media allowances depend on the current service and plan, and it points integrators to the current policies and API documentation before wiring anything up. That is the same habit this guide keeps repeating — the published page is the source of truth, and it will change under you.

Source: Victoria AI Model Card | Capabilities, Evidence and Limits | SentX SI

A failed request, traced end to end

This walkthrough is an illustration built from the documented behavior, not an observed result. A nightly enrichment job sends 300 prompts:

  1. The job launches at full concurrency. Within seconds, several calls return 429 / slow_down with Retry-After: 56. The client waits at least 56 seconds plus a random offset, cuts concurrency, and ramps back up gradually.Source: Rate limits | OpenAI API
  2. An hour in, a call returns 503 / server_is_overloaded with no usable Retry-After. The client falls back to capped exponential backoff with jitter; the attempt budget exhausts before the item succeeds, so the item is written to a dead-letter list with its error — visible, retryable tomorrow, not gone.Source: Rate limits | OpenAI API
  3. A different call fails with a billing error. It bypasses the retry layer entirely and halts the job with a message an operator sees.Source: Rate limits | OpenAI API
  4. A streaming call delivers part of its output, then a stream event reports the failure. The client keeps the partial output, marks the item partially completed, and reissues only the unfinished portion.Source: Rate limits | OpenAI API

Every one of the 300 items ends in a state someone or something can find. That invariant — no request may disappear without a record — is what separates a resilient client from one that quietly drops work, and it costs less than the first silent data loss would.

Provider-specific numbers in OpenAI's rate-limit documentation change over time, so check the current page before you encode any of them as constants into code.

Source: Rate limits | OpenAI API

Sources

  1. Rate limits | OpenAI API — OpenAI
    Supported claims and limits
    • Rapid traffic increases return HTTP 429 with error code slow_down; temporary model overload returns HTTP 503 with server_is_overloaded; clients must branch on status plus error.code because other errors reuse both statuses
    • slow_down can occur even when traffic is within its RPM and TPM limits; it reflects how quickly traffic increased, not whether the limits were exhausted
    • Python, TypeScript, and Ruby raise RateLimitError for 429 and InternalServerError for 503; Java raises RateLimitException and InternalServerException
    • Retry-After is a minimum wait; add a small random delay; when missing or invalid, fall back to exponential backoff with jitter and cap both attempt count and total retry time
    • Official SDKs automatically retry eligible 429 and 503 responses, subject to retry settings that vary by SDK version and configuration; application-managed retries must disable or account for SDK retries so nested loops do not multiply requests
    • If a valid server-supplied delay exceeds the configured maximum retry delay, stop retrying and defer the request rather than retrying sooner
    • The documentation's fallback backoff examples deliberately do not inspect Retry-After; production wrappers should add that check and prefer valid server hints
    • Quota, billing, and other user-action errors should not be retried; Retry-After does not mean such errors resolve by waiting; a hard spend limit makes affected requests return 429
    • For streaming requests, HTTP error responses apply before the stream starts; after streaming begins an error can arrive as a stream event, and requests should not be automatically replayed after consuming output
    • A timeout per attempt is not necessarily a deadline for the entire operation; a canceled request or expired deadline can stop retries without returning the HTTP error
    • Unsuccessful requests contribute to the per-minute limit, so continuously resending a rejected request will not work
    • Set max_tokens close to the expected response size; the rate limit is calculated as the maximum of max_tokens and the estimated token count of the request
    • The Batch API executes large collections of requests without impacting synchronous request rate limits
    • Rule of thumb: once traffic reaches 1 million input tokens per minute, increase by no more than 50% every 15 minutes; the exact ramp-rate limit point varies by model and traffic conditions
  2. Victoria AI Model Card | Capabilities, Evidence and Limits | SentX SI — SentX SI
    Supported claims and limits
    • The public model card states that availability, daily limits and media allowances depend on the current service and plan
    • The model card directs integrators to read the current policies and API documentation before an integration
Meet Victoria