---
title: "HTTP status codes, rate limits, retry semantics"
type: reference
tags: [errors, rate-limits, retries, exceptions, http-api]
created: 2026-09-17
updated: 2026-09-17
confidence: high
sources:
  - raw/docs/api.md
  - raw/docs/models.md
  - raw/docs/sdk__python__api__exceptions.md
  - raw/docs/sdk__python__api__retries.md
  - raw/docs/sdk__javascript__api__classes__APIError.md
  - raw/docs/sdk__javascript__api__interfaces__RetryPolicy.md
jev_version: "jev-1.13.0"
sdk_python: "0.6.0"
sdk_js: "0.6.0"
summary: "One table mapping every HTTP status to its meaning, Python exception, JS error class, retryability, and the recommended client action."
---

# HTTP status codes, rate limits, retry semantics

> **TL;DR** Retry `408`, `429`, and every `5xx` (including `529 Overloaded`) with exponential backoff, honoring `retry-after-ms` then `retry-after`. Do not retry `400`, `401`, `403`, `404`, or `422` — fix the request or the key. Both SDKs do this by default: 2 retries, 500 ms initial backoff doubling to a 5,000 ms cap, 25% jitter.

## The table

| HTTP status | Meaning | Python exception (`typesafe_sdk`) | JS error class (`@typesafe-ai/sdk`) | Retryable by default? | Recommended action |
|---|---|---|---|---|---|
| `400 Bad Request` | The request was invalid. Not listed in raw/docs/api.md; both SDKs map it. | `TypeSafeBadRequestError` | `BadRequestError` | No | Fix the request. Inspect `body` / `err.body`. |
| `401 Unauthorized` | "Missing or invalid API key. Check the `Authorization` header." | `TypeSafeAuthenticationError` | `AuthenticationError` | No | Check `TYPESAFE_API_KEY` and the `Bearer` prefix. Never retry — it will not succeed. |
| `403 Forbidden` | Access was denied. Not listed in raw/docs/api.md; both SDKs map it. | `TypeSafePermissionDeniedError` | `PermissionDeniedError` | No | The key is valid but lacks access (e.g. a model or feature not enabled). Contact sales@typesafe.ai. |
| `404 Not Found` | The resource was not found. Not listed in raw/docs/api.md; both SDKs map it. | `TypeSafeNotFoundError` | `NotFoundError` | No | Check the path (`/v1/systemone`, `/v1/models`) and `TYPESAFE_BASE_URL`. |
| `408 Request Timeout` | Not described in the docs; present only in the SDK retry defaults. | `TypeSafeAPIError` (no dedicated subclass; `TypeSafeInternalServerError` only applies at ≥ 500) | `APIError` (base; `fromResponse` has no 408 branch) | **Yes** | Retry with backoff. |
| `422 Unprocessable Entity` | "The request body failed validation — for example a missing required field or a malformed question. The body details the offending field." | `TypeSafeUnprocessableEntityError` | `UnprocessableEntityError` | No | Read `detail[]` (`loc`, `msg`, `type`) from the `HTTPValidationError` body and fix the field. |
| `429 Too Many Requests` | "You have exceeded your rate limit. Back off and retry after a short delay." Returned when either the 250,000 tok/s or the 1,200 rpm limit is exceeded. | `TypeSafeRateLimitError` (adds `retry_after_ms`) | `RateLimitError` (adds `retryAfterMs`) | **Yes** | Sleep for the server delay if present, else exponential backoff. Reduce concurrency. |
| `500`–`528`, `530`+ (5xx) | "The server failed to process the request (5xx)." | `TypeSafeInternalServerError` | `InternalServerError` | **Yes** | Retry with backoff; escalate with the `x-typesafe-request-id`. |
| `529 Overloaded` | "TypeSafe is temporarily overloaded. Retry after a short delay." | `TypeSafeInternalServerError` (matches the `status >= 500` branch) | `InternalServerError` (matches the `status >= 500` branch) | **Yes** | Retry with backoff. Note neither SDK has a dedicated "overloaded" class. |
| Any other non-2xx | — | `TypeSafeAPIError` | `APIError` | No (unless in `httpStatuses`) | Inspect `status` and `body`. |
| No HTTP response (DNS, TLS, connection closed) | "A request failed without an HTTP response." | `TypeSafeAPIConnectionError` (also a builtin `ConnectionError`) | `APIConnectionError` | **Yes** (`api_connection_error` / `apiConnectionError`, default true) | Retry with backoff; check network/proxy. |
| Timeout | "A request exceeded its configured timeout." | `TypeSafeAPITimeoutError` (also a builtin `TimeoutError`; carries `timeout`) | `APITimeoutError` (carries `timeoutMs`) | **Yes** (`api_timeout_error` / `apiTimeoutError`, default true) | Retry, or raise the client timeout (Python default 10.0 s; JS default 10,000 ms). |
| Caller cancellation | The caller cancelled through an `AbortSignal`. | — (no equivalent class in raw/docs/sdk__python__api__exceptions.md) | `APIUserAbortError` | No | Do not retry; the caller asked to stop. |
| 2xx with an invalid body | "A successful HTTP response whose body was missing or structurally invalid required data." | `TypeSafeAPIResponseValidationError` (carries `field_path`, e.g. `answers.tone.confidence`) | — (no equivalent class in the JS SDK reference) | No | Log the `field_path` and the request id; report to support@typesafe.ai. |

Sources for the mapping: statuses and meanings from raw/docs/api.md and raw/docs/models.md; Python classes from raw/docs/sdk__python__api__exceptions.md and the `STATUS_ERROR_TYPES` map in raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py; JS classes from the `raw/docs/sdk__javascript__api__classes__*Error*.md` pages and `APIError.fromResponse` in raw/github/typesafe-sdk-js/src/errors.ts; retryability from the default retry policies (below).

## Exception hierarchies

Python (raw/docs/sdk__python__api__exceptions.md):

```
Exception
└── TypeSafeError
    ├── TypeSafeAPIError            (status, body, headers, endpoint, request_id)
    │   ├── TypeSafeBadRequestError            400
    │   ├── TypeSafeAuthenticationError        401
    │   ├── TypeSafePermissionDeniedError      403
    │   ├── TypeSafeNotFoundError              404
    │   ├── TypeSafeUnprocessableEntityError   422
    │   ├── TypeSafeRateLimitError             429   (+ retry_after_ms)
    │   ├── TypeSafeInternalServerError        5xx
    │   └── TypeSafeAPIResponseValidationError (+ field_path)
    └── TypeSafeAPIConnectionError   (also ConnectionError)
        └── TypeSafeAPITimeoutError  (also TimeoutError, + timeout)
```

JavaScript (raw/docs/sdk__javascript__api__classes__*.md):

```
Error
└── TypeSafeError
    ├── APIError                    (status, body, headers, requestId; static fromResponse)
    │   ├── BadRequestError               400
    │   ├── AuthenticationError           401
    │   ├── PermissionDeniedError         403
    │   ├── NotFoundError                 404
    │   ├── UnprocessableEntityError      422
    │   ├── RateLimitError                429  (+ retryAfterMs)
    │   └── InternalServerError           5xx
    ├── APIConnectionError          (default message "Connection error.")
    │   └── APITimeoutError         (+ timeoutMs)
    └── APIUserAbortError           (default message "Request was aborted.")
```

Asymmetries worth knowing: Python has `TypeSafeAPIResponseValidationError` with no JS counterpart; JS has `APIUserAbortError` with no Python counterpart. In Python, `TypeSafeAPIError` also exposes `endpoint` ("the request method and URL, without credentials, query parameters, or fragment, when available"), which JS does not.

## Error metadata available on an API error

| Datum | Python | JavaScript | Notes |
|---|---|---|---|
| HTTP status | `status` | `status` | number |
| Body | `body` — "The server's JSON error body, plain response text, or `None` for an empty body." | `body` — "Parsed JSON, response text, or `undefined` for an empty body." | For 422, this is the `HTTPValidationError` object. |
| Headers | `headers` | `headers` (`Headers`) | |
| Request id | `request_id` (property) | `requestId` | Both read the `x-typesafe-request-id` response header; `None`/`undefined` if absent. Quote it in support requests. |
| Endpoint | `endpoint` | — | Method and URL, credentials stripped. |
| Server retry delay | `TypeSafeRateLimitError.retry_after_ms` | `RateLimitError.retryAfterMs` | Milliseconds, or `None`/`undefined` if unavailable. |

Both SDKs derive an error **message** from the body, preferring `error`, then `error.message`, `message`, `detail`, `detail.message`, and finally a formatted join of validation errors as `path: message` entries; a raw body is truncated at 200 characters (raw/github/typesafe-sdk-js/src/errors.ts, raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py).

## Rate limits

| Limit | Value | Breach |
|---|---|---|
| Throughput | 250,000 tokens per second | `429` |
| Requests | 1,200 requests per minute | `429` |

"A request over either limit returns `429 Too Many Requests`" (raw/docs/models.md). These limits are explicitly declared unstable: "the limits above can change without notice." Higher limits are available on custom and enterprise plans (sales@typesafe.ai). See [[reference/models-and-pricing]].

Guidance for direct HTTP callers (verbatim, raw/docs/api.md): "When you receive a `429 Too Many Requests` or `529 Overloaded` response, retry the request with exponential backoff instead of retrying immediately. Our client SDKs handle this automatically, so no extra handling is needed if you use one of our SDKs with its default retry policy."

## Retry-after headers

| Header | Read by | Precedence | Interpretation |
|---|---|---|---|
| `retry-after-ms` | Both SDKs | Preferred | Milliseconds. |
| `retry-after` | Both SDKs | Fallback | Seconds, or an HTTP-date; a date is converted to a delay from now (clamped at 0). |

raw/docs/models.md only mentions `retry-after`; the SDK implementations read `retry-after-ms` first (raw/github/typesafe-sdk-js/src/retry.ts, raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py). A negative or unparseable value yields no delay and the client falls back to backoff. In the JS SDK a server delay is only honored when it is `<= maxRetryAfterMs` (default 60,000 ms); otherwise backoff is used.

## Default retry policies

| Setting | Python `RetryPolicy` | JS `RetryPolicy` |
|---|---|---|
| Max retries after the initial attempt (`0` disables) | `max_retries = 2` | `maxRetries = 2` |
| First backoff delay | `backoff_initial = 0.5` (seconds) | `backoffInitialMs = 500` |
| Backoff cap | `backoff_max = 5.0` (seconds) | `backoffMaxMs = 5000` |
| Jitter (fraction subtracted) | `backoff_jitter = 0.25` | `backoffJitter = 0.25` |
| Retryable statuses | `http_statuses = {408, 429, *range(500, 600)}` | `httpStatuses = {408, 429, 500–599}` |
| Honor retry-after headers | `respect_retry_after = True` | `respectRetryAfter = true` |
| Max honored server delay | — (not present) | `maxRetryAfterMs = 60000` |
| Retry connection errors | `api_connection_error = True` | `apiConnectionError = true` |
| Retry timeouts | `api_timeout_error = True` | `apiTimeoutError = true` |
| Extra retryable exception types | `exceptions`, a `set` of exception types, default empty | — |
| Custom predicate | `predicate`, a callable taking the raised exception and returning `bool`, default `None` | — |
| Overall retry deadline | `timeout: float \| None = 30.0` | — |
| Per-request HTTP timeout | `DEFAULT_TIMEOUT = 10.0` seconds (raw/docs/sdk__python__api__constants.md) | `DEFAULT_TIMEOUT_MS = 10_000` (raw/github/typesafe-sdk-js/src/retry.ts) |

Both defaults cover `529` because it falls inside `500–599`.

Delay formula used by the JS SDK (raw/github/typesafe-sdk-js/src/retry.ts), for a zero-based attempt index: if a server delay is present and within `maxRetryAfterMs`, use it; otherwise `round(min(backoffInitialMs * 2**attempt, backoffMaxMs) * (1 - random() * backoffJitter))`. That gives roughly 375–500 ms, then 750–1000 ms, then 1500–2000 ms.

### Overriding the policy (Python, verbatim from raw/docs/sdk__python__api__retries.md)

```python
from typesafe_sdk import RetryPolicy, TypeSafeClient

client = TypeSafeClient(
    retry=RetryPolicy(
        max_retries=3, timeout=10.0, http_statuses={429, 500, 502, 503, 504}
    )
)
```

Note this override **drops** `408` and the rest of the 5xx range, including `529`. Add them back if you want the documented overload behavior.

## Recommended client behavior if you are not using an SDK

1. Retry only `408`, `429`, and `5xx`. Everything else is a permanent failure for that request.
2. Read `retry-after-ms` first, then `retry-after`; cap any honored delay (60 s is the SDK default cap).
3. Otherwise back off exponentially from 500 ms, doubling to a 5 s cap, subtracting up to 25% jitter.
4. Stop after 2–3 retries and surface the error with the `x-typesafe-request-id`.
5. On `422`, parse `detail[]` and log `loc` joined by `.` with `msg` — this is exactly what both SDKs do to build the error message. See [[reference/openapi-schemas]].
6. On `401`, do not retry and do not rotate through keys automatically.

## Version notes

- Statuses documented in raw/docs/api.md: `401`, `422`, `429`, `529` only. The SDKs additionally map `400`, `403`, `404`, and the whole `5xx` range; `408` appears only in the retry defaults.
- The OpenAPI document (raw/site/openapi.json, `info.version: 0.2.0`) declares only `200` and `422` responses per path — it is not an exhaustive status list.
- Both SDK versions referenced here are 0.6.0.

## Related

- [[reference/http-api]] — the endpoints and the error table as documented
- [[reference/openapi-schemas]] — `HTTPValidationError` and `ValidationError` field by field
- [[reference/models-and-pricing]] — the numeric rate limits and the dynamic-limits warning
- [[reference/python-sdk-retries-errors]] — the Python retry and exception API in full
- [[reference/javascript-sdk-errors]] — the JS error classes and `RetryPolicy` in full
- [[reference/environment-variables]] — `TYPESAFE_BASE_URL`, `TYPESAFE_LOG_LEVEL`

## Sources

- raw/docs/api.md (https://docs.typesafe.ai/api)
- raw/docs/models.md (https://docs.typesafe.ai/models)
- raw/docs/sdk__python__api__exceptions.md (https://docs.typesafe.ai/sdk/python/api/exceptions)
- raw/docs/sdk__python__api__retries.md (https://docs.typesafe.ai/sdk/python/api/retries)
- raw/docs/sdk__python__api__constants.md (https://docs.typesafe.ai/sdk/python/api/constants)
- raw/docs/sdk__javascript__api__classes__APIError.md and the sibling `*Error*` pages (https://docs.typesafe.ai/sdk/javascript/api/classes/APIError)
- raw/docs/sdk__javascript__api__interfaces__RetryPolicy.md (https://docs.typesafe.ai/sdk/javascript/api/interfaces/RetryPolicy)
- raw/github/typesafe-sdk-js/src/errors.ts, raw/github/typesafe-sdk-js/src/retry.ts
- raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py
