Requirements
- Python 3.8 or higher
- No other runtime dependencies beyond the standard library and
requests
Installation
Install the package from PyPI using pip:Initialization
Create a singleCanoClient instance when your application starts and reuse it across your codebase. The client spawns a background worker thread that handles batching and delivery automatically.
cano_client.py
Core Methods
client.track(event, user_id=None, anonymous_id=None, properties=None, timestamp=None)
Call track whenever a user performs an action you want to measure. Supply either a user_id (for authenticated users) or an anonymous_id (for unidentified visitors) — at least one is required.
timestamp if you need to record an event that occurred in the past:
client.identify(user_id, traits=None, timestamp=None)
Call identify to associate a user ID with a set of traits. This enriches all future events from that user with the traits you provide.
client.page(user_id, name=None, properties=None)
Call page to record a server-side page or screen view — useful for server-rendered apps where the frontend SDK isn’t available.
client.alias(user_id, previous_id)
Call alias to merge two identities — for example, when a previously anonymous user signs up and you want to link their historical events to their new account.
client.flush()
Call flush to synchronously send all events currently queued in memory. This is a blocking call and waits until the request completes or times out.
client.shutdown()
Call shutdown to flush all remaining queued events and gracefully stop the background worker thread. Always call this before your process exits.
Django Middleware
You can automatically track every incoming HTTP request in a Django application by adding a lightweight middleware class. Place this in your project’smiddleware.py file and add it to your MIDDLEWARE setting.
middleware.py
settings.py:
settings.py
Async Support
For async frameworks like FastAPI, Starlette, or async Django views, use
AsyncCanoClient instead of CanoClient. The async client exposes the same interface but all methods are coroutines — await each call accordingly.Error Handling and Retries
The SDK automatically retries failed requests on transient errors (HTTP429 Too Many Requests and 5xx server errors) with exponential backoff. By default it retries up to 3 times before dropping the batch and logging an error.
To handle errors explicitly, pass an on_error callback when initializing the client:
error-handling.py
batch argument passed to on_error is the list of event dictionaries that failed to deliver, so you can log or re-queue them as needed.