Reliability
The SDK does not block your call path and does not raise into your application. The queue, flush interval, retry ladder, circuit breaker, and every case where an event is dropped.
Two guarantees, and everything on this page follows from one of them.
Your call path never waits for Capsera.
Capsera does not raise into your application. The single exception is
BudgetExceededError, which occurs only when a budget you configured stops a call.
Delivery path
your call -> wrapper builds an event -> queue.Queue(maxsize=10_000)
| daemon thread, every 500 ms
v
batch POST -> backend
The queue write is non-blocking. Your function returns as soon as the provider returns.
The worker runs on a daemon thread, so it cannot delay process exit. This is also why short-lived processes need an explicit flush.
Retries and the circuit breaker
A failed batch is retried three times with exponential backoff. Re-sending is safe because the backend deduplicates on a request ID, so a batch that arrived before the connection dropped is not counted twice.
After five consecutive failures the circuit opens for 30 seconds. While open, nothing is sent and events are dropped rather than accumulating. When it closes, delivery resumes and logs that it has.
During a sustained backend outage the SDK loses telemetry rather than growing memory inside your process.
Cases where an event is dropped
There are four, all bounded and all logged.
The queue is full. 10,000 pending events means delivery has been failing. New events are dropped rather than blocking your call.
The circuit is open. For up to 30 seconds at a time.
The monthly limit was reached. The backend returns 429 and the SDK stops sending for the rest of the process.
The process exited before a flush. Preventable with shutdown().
Loss outside these four cases would be a bug. The SDK's evaluation harness tracks undocumented event loss as a failing metric.
Draining before exit
capsera.flush() # deliver queued events, keep the worker running
capsera.shutdown() # deliver queued events, stop the worker
Long-running services need neither, because the 500 ms flush interval handles delivery. Scripts, cron jobs, CI steps, Lambda handlers, and notebook cells need one of them.
Failing open
Every interception path is wrapped. A changed provider response shape, a model missing from the pricing catalog, malformed routing rules, or an unreachable backend all produce missing or approximate telemetry rather than an exception in your code.
Routing and budget enforcement fail open specifically:
Routing. If rules cannot be fetched, the call proceeds on its original model.
Budgets. If the pre-call check does not complete within budget_check_timeout
(1 second by default), the call is allowed. Failing closed would mean a network
problem between your service and Capsera could halt production traffic.
Observing failures
Failing open means failures are quiet, so there are two ways to see them:
capsera.init(
api_key=...,
on_error=lambda exc: logging.warning("capsera: %s", exc),
debug=True, # one INFO line per intercepted call
)
on_error fires on delivery failures. debug=True confirms interception is
happening. Neither is enabled by default.
Overhead
Median per-call overhead is well under a millisecond. The work is building a small object and putting it on a queue. The evaluation harness enforces a median threshold on every run and fails the run if it regresses.