vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Flask
Quick startEvent forwarding
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceGoogle Ads
Client setup
CursorClaude DesktopVS CodeCodex
Realtime
Debug ViewRealtime Dashboard
Integration guides
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
SDK
Browser SDK
InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdown

Documentation

vTilt
Quick startEvent forwarding

MCP server

Realtime

Debug ViewRealtime Dashboard

Integration guides

NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails

SDK

DocsIntegration guidesFlask

Flask

Integrate vTilt with Flask — render the SDK in your Jinja base template, identify on every authenticated request, send server-side events over HTTP.

There's no native Python SDK yet, but vTilt's wire format is a plain JSON POST so any Python code can emit events. The pattern below covers the two pieces that matter: the Browser SDK (rendered into your Jinja base template) and a thin Python helper for server-side captures.

#1. Configure environment

# .env
VTILT_TOKEN=YOUR_PROJECT_TOKEN
VTILT_HOST=https://www.vtilt.com
text

#2. Inject the user into every template

Use @app.context_processor to make a vtilt_context object available to every template render.

# app.py
import os
from flask import Flask, render_template
from flask_login import current_user

app = Flask(__name__)
VTILT_TOKEN = os.environ['VTILT_TOKEN']
VTILT_HOST = os.environ.get('VTILT_HOST', 'https://www.vtilt.com')

@app.context_processor
def inject_vtilt():
    user = (
        {'id': str(current_user.id), 'email': current_user.email}
        if current_user.is_authenticated
        else None
    )
    return {
        'vtilt_context': {
            'token': VTILT_TOKEN,
            'host': VTILT_HOST,
            'user': user,
        }
    }
python

#3. Render the SDK in your base template

{# templates/base.html #}
<!doctype html>
<html lang="en">
<head>
  <script>
    window.__vtilt = {{ vtilt_context | tojson }};
  </script>
  <script>
    !(function(t,e){/* ...vTilt loader snippet from Quick start... */})(document, window.vt || []);

    vt.init(window.__vtilt.token, {
      api_host: window.__vtilt.host,
      autocapture: true,
      capture_pageview: true,
      capture_pageleave: true,
    });

    if (window.__vtilt.user) {
      vt.identify(window.__vtilt.user.id, { email: window.__vtilt.user.email });
    }
  </script>
</head>
<body>{% block content %}{% endblock %}</body>
</html>
jinja

Important

Important: Identifying users in the base template means every authenticated page-load identifies — not just login. This is the most common bug to avoid; see Identify & alias for the full model.

#4. Server-side capture helper

For events that fire outside a browser session — webhooks, scheduled jobs, server-side conversions — POST directly to vTilt.

# vtilt.py
import json
from urllib.request import Request, urlopen

def vtilt_capture(token: str, host: str, distinct_id: str, event: str, properties: dict | None = None) -> None:
    payload = json.dumps({
        'api_key': token,
        'event': event,
        'distinct_id': distinct_id,
        'properties': properties or {},
    }).encode('utf-8')

    req = Request(
        f'{host}/api/e',
        data=payload,
        headers={'Content-Type': 'application/json'},
        method='POST',
    )
    try:
        urlopen(req, timeout=2).read()
    except Exception:
        pass  # never block the response on analytics
python
# routes.py
from .vtilt import vtilt_capture

@app.route('/checkout', methods=['POST'])
def checkout():
    vtilt_capture(
        VTILT_TOKEN, VTILT_HOST,
        distinct_id=str(current_user.id),
        event='purchase_completed',
        properties={'amount': 99.99},
    )
    return {'ok': True}
python

Tip

Tip: For low-latency endpoints, run vtilt_capture in a background thread or push it to a job queue (RQ, Celery). Analytics shouldn't block your user-facing response.

#Next steps

  • Identify & alias — anonymous → known user merge.
  • Django integration guide — same Python pattern, larger framework.
  • Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
PreviousDjangoIntegration guidesNextLaravelIntegration guides

On this page

  • 1. Configure environment
  • 2. Inject the user into every template
  • 3. Render the SDK in your base template
  • 4. Server-side capture helper
  • Next steps