|
| 1 | +# clamp-analytics |
| 2 | + |
| 3 | +Server-side analytics SDK for [Clamp Analytics](https://clamp.sh) in Python. |
| 4 | + |
| 5 | +Send tracked events from any Python server runtime to Clamp. Works with Django, FastAPI, Flask, Celery workers, scheduled jobs, and anything else that runs Python and can make outbound HTTPS calls. |
| 6 | + |
| 7 | +## Install |
| 8 | + |
| 9 | +```bash |
| 10 | +pip install clamp-analytics |
| 11 | +``` |
| 12 | + |
| 13 | +Python 3.9+ supported. |
| 14 | + |
| 15 | +## Quick start |
| 16 | + |
| 17 | +```python |
| 18 | +from clamp_analytics import init, track, Money |
| 19 | + |
| 20 | +init(project_id="proj_xxx", api_key="sk_proj_xxx") |
| 21 | + |
| 22 | +# Simple event |
| 23 | +track("signup", properties={"plan": "pro", "method": "email"}) |
| 24 | + |
| 25 | +# Link a server event to a browser visitor (e.g. inside a Stripe webhook) |
| 26 | +track( |
| 27 | + "subscription_started", |
| 28 | + anonymous_id="aid_xxx", |
| 29 | + properties={"plan": "pro", "total": Money(29.00, "USD")}, |
| 30 | +) |
| 31 | +``` |
| 32 | + |
| 33 | +Get a server API key at <https://clamp.sh/dashboard> (Settings → API Keys, format `sk_proj_...`). Set it as an environment variable; never commit it. |
| 34 | + |
| 35 | +## API |
| 36 | + |
| 37 | +### `init(project_id, api_key, endpoint=None)` |
| 38 | + |
| 39 | +Initializes the SDK. Call once at process startup (e.g. in your Django `settings.py`, FastAPI lifespan, Flask app factory). Stores config in module-level state; subsequent `track()` calls use it. |
| 40 | + |
| 41 | +`endpoint` is optional and overrides the default `https://api.clamp.sh`. Use this for self-hosted Clamp deployments or integration testing. |
| 42 | + |
| 43 | +### `track(name, properties=None, anonymous_id=None, timestamp=None)` |
| 44 | + |
| 45 | +Sends a server event. |
| 46 | + |
| 47 | +- **`name`**: event name string. Examples: `"signup"`, `"subscription_started"`, `"feature_used"`. |
| 48 | +- **`properties`**: optional dict. Values may be `str`, `int`, `float`, `bool`, or `Money`. No nested dicts (other than `Money`) and no arrays. |
| 49 | +- **`anonymous_id`**: optional string. Links the server event to a browser visitor. Read the browser's anonymous ID via the JS SDK's `getAnonymousId()` and pass it through your auth flow (e.g. Stripe's `client_reference_id`). |
| 50 | +- **`timestamp`**: optional. Pass a `datetime` (timezone-aware preferred; naive datetimes are assumed UTC) or an ISO 8601 string. If omitted, the SDK uses the current UTC time. |
| 51 | + |
| 52 | +Returns `True` on success. Raises `ClampHTTPError` on a non-2xx response or `ClampNotInitializedError` if `init()` wasn't called. |
| 53 | + |
| 54 | +### `Money(amount, currency)` |
| 55 | + |
| 56 | +A typed monetary value. Use it for revenue, refunds, taxes; anywhere a currency-denominated amount belongs. |
| 57 | + |
| 58 | +```python |
| 59 | +track("purchase", properties={ |
| 60 | + "plan": "pro", |
| 61 | + "total": Money(29.00, "USD"), |
| 62 | + "tax": Money(4.35, "USD"), |
| 63 | +}) |
| 64 | +``` |
| 65 | + |
| 66 | +`amount` is in major units (29.00, not 2900). `currency` is an ISO 4217 code (uppercase, three letters). |
| 67 | + |
| 68 | +### `capture_error(exception, context=None, anonymous_id=None, timestamp=None)` |
| 69 | + |
| 70 | +Sends an exception as a `$error` event. Convenience over `track()` that extracts message, type, and stack from the exception. The server adds a stable fingerprint at ingest so the same bug groups across occurrences. |
| 71 | + |
| 72 | +```python |
| 73 | +from clamp_analytics import capture_error |
| 74 | + |
| 75 | +try: |
| 76 | + process_webhook(payload) |
| 77 | +except Exception as e: |
| 78 | + capture_error(e, context={"webhook": "stripe"}) |
| 79 | +``` |
| 80 | + |
| 81 | +- **`exception`**: any exception instance. Stack trace is captured via `traceback.format_exception`. |
| 82 | +- **`context`**: optional flat mapping of additional properties. Values must be primitives (`str`, `int`, `float`, `bool`); the reserved key `handled` is ignored. |
| 83 | +- **`anonymous_id`**: optional. Links the error to a browser visitor via the same anonymous ID flow as `track`. |
| 84 | +- **`timestamp`**: optional `datetime` or ISO 8601 string. |
| 85 | + |
| 86 | +Same return value and exceptions as `track()`. Lengths are capped (`error.message` 1KB, `error.type` 64 chars, `error.stack` 16KB) to match server-side limits. |
| 87 | + |
| 88 | +## Framework integrations |
| 89 | + |
| 90 | +Per-framework integration patterns (Django middleware, FastAPI dependency, Flask `after_request`, Celery task hook) are documented at <https://clamp.sh/docs/sdk/python>. |
| 91 | + |
| 92 | +## Errors |
| 93 | + |
| 94 | +The SDK is synchronous and raises on failure. There are no automatic retries. If you want fire-and-forget behavior, wrap the call yourself: |
| 95 | + |
| 96 | +```python |
| 97 | +import logging |
| 98 | + |
| 99 | +try: |
| 100 | + track("subscription_started", properties={...}) |
| 101 | +except Exception: |
| 102 | + logging.exception("failed to send to Clamp") |
| 103 | +``` |
| 104 | + |
| 105 | +For high-throughput webhook handlers, consider sending events through a background task queue (Celery, RQ, Dramatiq). |
| 106 | + |
| 107 | +## Links |
| 108 | + |
| 109 | +- Dashboard: <https://clamp.sh/dashboard> |
| 110 | +- Docs: <https://clamp.sh/docs/sdk/python> |
| 111 | +- Source: <https://github.com/clamp-sh/analytics-python> |
| 112 | +- Issues: <https://github.com/clamp-sh/analytics-python/issues> |
0 commit comments