Skip to content

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:

  1. The caller's idea of "acceptable latency" is application-specific. A batch importer might wait minutes; an interactive UI must give up in seconds.
  2. 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.