Skip to main content

mbuzz

Server-side multi-touch attribution for Python. Track customer journeys, attribute conversions, know which channels drive revenue.

Unlike client-side analytics, mbuzz runs server-side — no ad-blocker blind spots, no iOS tracking gaps, no browser privacy loss. Captures 30–40% more touchpoints than client-side tools.

Installation

pip install mbuzz

Requires Python 3.9+.

Quick Start

1. Initialize

import mbuzz

mbuzz.init(api_key="sk_live_...")

Call init() once on app boot. For Flask apps, see Flask Integration below.

2. Track Events

Track steps in the customer journey:

mbuzz.event("page_view", url="/pricing")
mbuzz.event("add_to_cart", product_id="SKU-123", price=49.99)
mbuzz.event("checkout_started", cart_total=99.99)

# Group events into funnels for focused analysis
mbuzz.event("signup_start", funnel="signup", source="homepage")
mbuzz.event("signup_complete", funnel="signup")

3. Track Conversions

Record revenue-generating outcomes:

mbuzz.conversion(
    "purchase",
    revenue=99.99,
    funnel="purchase",   # optional: group into funnel
    order_id=order.id,
)

4. Identify Users

Link visitors to known users (enables cross-device attribution):

# On signup or login
mbuzz.identify(
    user.id,
    traits={
        "email": user.email,
        "name": user.name,
    },
)

Funnels

Group related events into funnels for focused conversion analysis in your dashboard.

# Signup funnel
mbuzz.event("pricing_view", funnel="signup")
mbuzz.event("signup_start", funnel="signup")
mbuzz.event("signup_complete", funnel="signup")

# Purchase funnel
mbuzz.event("add_to_cart", funnel="purchase")
mbuzz.event("checkout_started", funnel="purchase")
mbuzz.conversion("purchase", funnel="purchase", revenue=99.99)

Why funnels?

  • Separate signup flow from purchase flow
  • Analyze each conversion path independently
  • Filter dashboard to specific customer journeys

Flask Integration

mbuzz ships with a Flask middleware that handles visitor cookies, session resolution, and request context automatically.

from flask import Flask
import mbuzz
from mbuzz.middleware.flask import init_app

app = Flask(__name__)

mbuzz.init(api_key="sk_live_...")
init_app(app)

After init_app, every request carries a visitor_id in context. Track events from anywhere in your request lifecycle:

@app.route("/pricing")
def pricing():
    mbuzz.event("page_view", page="/pricing")
    return render_template("pricing.html")

Access the current visitor or user from context:

mbuzz.visitor_id()   # Current visitor ID (from cookie)
mbuzz.user_id()      # Current user ID (if identify() was called)

Django Integration

# settings.py
MIDDLEWARE = [
    # ...
    "mbuzz.middleware.django.MbuzzMiddleware",
]

Initialise the SDK once at startup — in your AppConfig.ready() or settings.py:

import mbuzz

mbuzz.init(api_key="sk_live_...")

Install with pip install mbuzz[django].

FastAPI Integration

Works with any Starlette app; FastAPI is Starlette underneath.

from fastapi import FastAPI
import mbuzz
from mbuzz.middleware.fastapi import MbuzzMiddleware

app = FastAPI()

mbuzz.init(api_key="sk_live_...")
app.add_middleware(MbuzzMiddleware)

Install with pip install mbuzz[fastapi].

Other Frameworks

The core mbuzz.event, mbuzz.conversion, and mbuzz.identify functions are framework-agnostic. You can call them from any Python web app or background job.

Full-page caching — the snippet below is REQUIRED

Add this to your base template or nothing is tracked. Since 0.10.0 a page response never sets the visitor cookie: only POST /_mbuzz/session mints, and the snippet is what calls it.

A page response can be stored by a full-page cache (Cloudflare, Varnish, nginx, a CDN) and replayed to every visitor. A Set-Cookie sitting in that cache hands everyone the first visitor's id, so unrelated people merge into one journey — corruption rather than loss, since every row exists and is simply attributed to the wrong person. Minting only on a response no cache stores is the only way to prevent it.

The same endpoint solves the original problem too: a cached page never enters your application, so the middleware cannot run — but this one request always reaches it.

The middleware answers POST /_mbuzz/session on all three frameworks. There is nothing extra to mount. Call it once per page, from your base template:

<script>
  fetch('/_mbuzz/session', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url: location.href, referrer: document.referrer || '' }),
    credentials: 'same-origin',
    keepalive: true
  }).catch(function () {});
</script>

Two things to keep as they are:

  • Inline the script, don't load it as a file. Asset optimisers delay external scripts until the visitor first interacts. A visitor who lands and converts without clicking anything first would never be established.
  • credentials: 'same-origin' is required, or the cookie never comes back.

The visitor id is never created or read in JavaScript. It stays HttpOnly and server-set, which is what preserves its full two-year life — a cookie written by document.cookie is capped at 7 days under Safari's ITP, and 24 hours after an ad click.

Background Jobs

For tracking from Celery, RQ, or other background workers, pass visitor_id explicitly:

# Capture at request time
@app.route("/checkout")
def checkout():
    visitor_id = mbuzz.visitor_id()
    process_order.delay(order.id, visitor_id)
    return redirect("/thank-you")

# Use in the job
@celery.task
def process_order(order_id, visitor_id):
    order = Order.get(order_id)
    mbuzz.conversion(
        "purchase",
        visitor_id=visitor_id,
        revenue=order.total,
    )

Configuration Options

mbuzz.init(
    api_key="sk_live_...",       # Required
    enabled=True,                # Default: True — toggle without code changes
    debug=False,                 # Default: False — enables verbose logging
    timeout=5.0,                 # Default: 5.0s — API request timeout
    skip_paths=["/health"],      # Optional — paths to skip tracking
    skip_extensions=[".css"],    # Optional — file extensions to skip
)

The 4-Call Model

Method When to Use
init Once on app boot
event User interactions, funnel steps
conversion Purchases, signups, any revenue event
identify Login, signup, when you know the user

Error Handling

mbuzz never raises exceptions. All methods fail silently and log errors in debug mode — your application flow is never interrupted by tracking failures.

Why Server-Side?

Client-side tracking loses 30–40% of marketing data to ad blockers, iOS tracking protections, and 7-day cookie caps. Server-side attribution runs inside your application, where requests flow through your own domain as first-party data — so you see the full customer journey.

Full explainer: mbuzz.co/articles/server-side-vs-client-side-tracking

Attribution Models

mbuzz runs 8 attribution models side-by-side out of the box — first-touch, last-touch, linear, time-decay, position-based, Markov, Shapley, and data-driven — so you can see how much each model disagrees about which channel deserves credit.

Requirements

  • Python 3.9+
  • No required dependencies for the core SDK
  • flask, django or starlette only required for that framework's middleware

Contributing

PRs welcome — especially framework adapters (Django, FastAPI, Starlette) and async support.

License

MIT License

Metadata

Release files for mbuzz 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mbuzz 0.10.0
File Size Uploaded
mbuzz-0.10.0.tar.gz 98.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mbuzz 0.10.0
File Interpreter ABI Platform
mbuzz-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 120.5 kB

Release files / mbuzz-0.10.0.tar.gz

Download URL mbuzz-0.10.0.tar.gz
Size 98.3 kB
Tags Source
SHA-256 checksum
How to use checksums
d920204bddf4ea1ebfc29f943a4346ecf01ffba24dabc3da4208875ed58591cc
BLAKE2b-256 checksum
How to use checksums
fd58a623b72a7ee02c0f08975e85d307d87adc265cede6d8894ac7c979278635
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / mbuzz-0.10.0-py3-none-any.whl

Download URL mbuzz-0.10.0-py3-none-any.whl
Size 22.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1486755211a24fe4b95ee6163a0fdc18d0d5d28fd528e3979e98cffd20ff5f4b
BLAKE2b-256 checksum
How to use checksums
c30ff20d163a98e9b197f2d14d92c411e17c8415438e0124cd417a158b39652f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.11.0

2 release files

This release

0.10.0 This release

2 release files

0.8.3

2 release files

0.8.0

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page