Rate limiting¶
The SDK does not retry 429s for you. It surfaces them as
RateLimited carrying the server's Retry-After so the caller
decides what to do.
Why we don't retry¶
Auto-retry inside the SDK is the wrong layer for two reasons:
- The caller's idea of "acceptable latency" is application-specific. A batch importer might wait minutes; an interactive UI must give up in seconds.
- Hidden retries amplify load on a server that's already overloaded — the opposite of what 429s are designed to prevent.
What you get¶
from rtls_sdk import RateLimited
try:
tags = client.tags.list()
except RateLimited as exc:
sleep_for = exc.retry_after_seconds or 1.0
log.info("backing off %.1fs", sleep_for)
retry_after_seconds is parsed from Retry-After (number of seconds).
If the server didn't send one, it's None — pick a default.
A copy-pasteable retry helper¶
import time
from rtls_sdk import RateLimited, RtlsError
def with_retry(call, *, max_attempts=5, max_wait=60.0):
"""Retry on RateLimited up to ``max_attempts``, honoring Retry-After."""
for attempt in range(max_attempts):
try:
return call()
except RateLimited as exc:
wait = min(exc.retry_after_seconds or 2 ** attempt, max_wait)
time.sleep(wait)
raise RtlsError(f"giving up after {max_attempts} 429s")
# Usage
tags = with_retry(lambda: client.tags.list())
For richer retry logic (jitter, circuit breakers, fault budgets), wrap
the SDK call in your application's existing retry library (tenacity,
urllib3 Retry, etc.).
Test-server quirks¶
Some deployments rate-limit aggressively under load. If you're seeing
constant RateLimited raises on a quiet workload, contact the server
team — the SDK is correctly surfacing the server's response.