trevosdk runs experiments and records conversions from your Python backend. Zero
dependencies, Python 3.10+, and assignment is byte-identical to every other Trevo SDK — the
same user gets the same variant in the browser and on the backend.
Create a client
You need a server key:tsk_secret_…, from Settings → SDK keys with platform Server.
It is shown once. Never put it in client code.
trevo.flush() / trevo.close().
Assignment needs config, so wait for the first fetch once at startup:
Read a variant
get_variant reads an immutable in-memory config snapshot — no network call, nothing that
blocks — so it is safe anywhere: Django and Flask views, FastAPI handlers, Celery tasks,
cron scripts.
Each call records an exposure (deduplicated per identity and variant within a 10-minute
window). To read without recording one, pass track_exposure=False.
Identity is passed per call
A server holds no ambient user state, so every call takes the identity explicitly:user_id wins for bucketing when present; an
event carrying both is what links a user’s anonymous browsing to their conversions. With no
identity at all, get_variant returns "control" and records nothing.
Bucket with the identity the browser is using at that moment — the user id when
identified, otherwise the trevo_id cookie. Anything else assigns the same person different
variants on the client and the server.
Track conversions
insert_id derived from the thing that happened, and Trevo
records the event once no matter how many deliveries arrive:
Linking identities without a cookie
Webhooks and cron jobs have no cookie to read. State the link directly:anonymous_id belongs to one user — pointing it at a
different user raises and changes nothing.
Framework notes
- Django / Flask — instantiate once at module scope (a settings or extensions file) and call from views directly.
-
FastAPI / async — the sync client is safe in async handlers (nothing blocks on the
read path). For an awaitable lifecycle, wrap it:
-
Pre-fork servers (
gunicorn --preload, uWSGI, Celery prefork) — a client built before the fork keeps working in the child. Its worker threads do not survive a fork, so the SDK re-initialises them and its locks viaos.register_at_fork; the firstget_variantortrackin the child restarts delivery. Nopost_forkhook is needed. -
Serverless and short-lived scripts — background timers do not get a chance to fire, so
call
trevo.flush()before returning. The client is also a context manager:with TrevoClient(...) as trevo:flushes and closes on exit.
Local development
Pin variants without touching Trevo:force_variants={"checkout-cta": "treatment"}. Forced reads never record an
exposure, so local testing never pollutes results. The SDK’s own test suite deletes the
variable, so exporting it cannot turn a conformance run red.
Errors
Everything raised or passed toon_error is typed, so you can branch on what happened:
TrevoConfigError, TrevoDeliveryError (carries .status), TrevoSerializationError,
TrevoQueueFullError and TrevoEventRejectedError, all under TrevoError.
Options
API summary
AsyncTrevoClient mirrors the same surface with await-able ready(), alias(),
flush(), and aclose(), plus async with support.