Retries & timeouts
The SDK retries automatically on transient failures so you don't have to write retry loops in your own code. This page documents the policy so you can tune it for your workload — or turn it off when you don't want it.
What gets retried
| Failure | Retried? | Why |
|---|---|---|
HTTP 429 (rate limited) | yes | Honors Retry-After (delta-seconds or HTTP-date) |
HTTP 500, 502, 503, 504 | yes | Server-side transient |
| Network error (DNS / connect / read timeout) | yes | Transport-level transient |
HTTP 4xx (other than 429) | no | Validation / auth / not-found — fail fast |
HTTP 2xx / 3xx | n/a | Already successful |
Idempotency keys are preserved across retries (auto-generated or explicit), so retried mutations land on the same server-side record.
Backoff schedule
delay = 0.5 * 2^attempt + uniform(0, 0.5) seconds, capped at
max_retries. With the default max_retries=3 that's:
| Attempt | Cumulative wait |
|---|---|
| 1 (first retry) | ~0.5–1.0 s |
| 2 | ~1.5–2.5 s |
| 3 (last retry) | ~3.5–5.0 s |
A 429 response's Retry-After overrides this — if the server says
"retry in 12 seconds," the SDK waits 12 seconds.
Tune retries
from thrustlab import Client
# More aggressive — useful in CI where every transient blip is intolerable
client = Client(api_key="key_...", max_retries=5)
# Fail-fast — useful when you want exceptions to propagate immediately
client = Client(api_key="key_...", max_retries=0)
max_retries=0 disables retries entirely. The SDK still honors HTTP
errors with typed exceptions (see errors).
Tune timeouts
timeout= is the per-request httpx timeout, not the total wall-clock
time. With retries you can wait up to roughly timeout * (1 + max_retries)
seconds.
client = Client(
api_key="key_...",
timeout=60.0, # bump per-attempt cap
max_retries=5, # bump retry budget
)
For finer-grained control (different connect / read / write / pool
timeouts), pass a custom httpx.Client:
import httpx
from thrustlab import Client
http = httpx.Client(timeout=httpx.Timeout(
connect=10.0,
read=300.0, # long read for image uploads / sweeps
write=60.0,
pool=10.0,
))
client = Client(api_key="key_...", http_client=http)
When http_client= is set, the timeout= argument is ignored — your
custom client owns the timeouts.
Catching retry exhaustion
After max_retries retries, the SDK raises a typed exception with the
last observed error:
from thrustlab.exceptions import RateLimitError, APIServerError, NetworkError
try:
client.simulations.create(project_id="proj_xxx", ...)
except RateLimitError as exc:
print(f"slow down — Retry-After was {exc.retry_after}s")
except APIServerError as exc:
print(f"server returned {exc.http_status} after retries; req={exc.request_id}")
except NetworkError as exc:
print(f"transport failure: {exc}")
RateLimitError exposes .retry_after (seconds, may be None).
APIServerError and friends expose .http_status and .request_id.
Recipe: outer-loop retry across SDK exhaustion
For workloads where you want a longer retry horizon than the SDK budget, wrap calls in your own loop:
import time
from thrustlab import Client
from thrustlab.exceptions import RateLimitError, APIServerError, NetworkError
client = Client(max_retries=3)
def submit_with_outer_retry(payload, *, max_outer_retries=5):
delay = 5.0
for attempt in range(max_outer_retries):
try:
return client.simulations.create(**payload)
except (RateLimitError, APIServerError, NetworkError):
if attempt == max_outer_retries - 1:
raise
time.sleep(delay)
delay = min(delay * 2, 60.0)
See also
- Configuration
- Errors
- Idempotency — keys are preserved across retries
- Rate limits guide