SDKs
Python
atrix-analytics for Python 3.9+: thread-safe, dependency-free, never raises.
Server-side Atrix Analytics for Python 3.9 and newer, with no runtime dependencies. Events are batched, gzipped, retried with backoff and deduplicated by a client-generated UUIDv7. The client is thread-safe and never raises; with an invalid key every call does nothing.
pip install atrix-analyticsQuickstart
from atrix_analytics import Atrix
atrix = Atrix("atx_pk_eu_production_XXXXXXXXXXXXXXXXXXXXXX") # one per process
atrix.capture("user_42", "order_completed", {"amount": 1999, "currency": "AED"})
# Person properties. Pass the visitor's anonymous id (from the web or mobile SDK) to link their history.
atrix.identify("user_42", set={"plan": "pro"}, set_once={"signup_source": "ads"}, anon_id="0190f2a4-5b6c-7d8e-9f01-23456789abcd")
# Groups: per event, plus group profiles.
atrix.capture("user_42", "report_exported", groups={"company": "acme"})
atrix.group_identify("company", "acme", {"name": "Acme", "seats": 40})
# Link a billing customer to the user so revenue webhooks are attributed to them.
atrix.set_revenue_customer("user_42", "cus_Nffrfeu", provider="stripe")
atrix.shutdown() # optional: also runs automatically at interpreter exitThe host comes from the key: atx_pk_eu_… sends to https://eu.i.analytics.atrix.dev. To use a first-party
proxy, pass host="https://analytics.example.com".
Feature flags
import os
from atrix_analytics import Atrix
atrix = Atrix(os.environ["ATRIX_KEY"], flags_secret_key=os.environ.get("ATRIX_FLAGS_SECRET"))
if atrix.is_feature_enabled("new-checkout", "user_42", person_properties={"plan": "pro"}):
...
variant = atrix.get_feature_flag("pricing", "user_42") # "test" | "control" | False | NoneWith flags_secret_key, flags are evaluated locally (definitions polled with an ETag on a daemon thread) and
only inconclusive flags call /flags. Reads emit $feature_flag_called once per user, flag and value.
Options
| Argument | Default | |
|---|---|---|
host | from the key | Capture origin override. |
flush_at / flush_interval | 20 / 30.0 s | Batching. |
max_batch_size | 100 | Events per request. |
max_queue_size / max_queue_bytes | 1000 / 5 000 000 | The oldest events are dropped past this and reported as $dropped_events. |
request_timeout | 10.0 s | |
consent | "granted" | "pending" holds events in memory; "denied" makes no network calls. Also set_consent(). |
before_send | fn(event) -> event or None. A hook that raises drops the event. | |
property_denylist | Keys removed after the hook. | |
super_properties | Stamped on every event. register() / unregister() at runtime. | |
flush_at_exit | True | Flush through atexit, waiting at most 5 s. |
flags_secret_key, flags_poll_interval | Local flag evaluation. | |
debug | False | Log problems. |
Behaviour
capture()never blocks on the network: HTTP runs on a daemon thread, one request in flight at a time.- Network errors, timeouts, 408, 429 and 5xx are retried, waiting for
Retry-Afterormin(10 min, 1 s·2^n)with ±50% jitter. A 413 splits the batch; any other 4xx drops it. flush(block=True, timeout=None)waits until the queue is empty or a retry is scheduled.shutdown(timeout=None)flushes and turns the client off. Worker threads are daemons.