Skip to main content
The PostHog Python SDK enables server-side event tracking, feature flag evaluation, and user identification for Python applications including Django, Flask, FastAPI, and more.

Installation

Install the PostHog Python library using pip:
pip install posthog

Initialization

1

Import the library

from posthog import Posthog
2

Create a client instance

Initialize PostHog with your project API key and host from your project settings:
posthog = Posthog(
    project_api_key='<ph_project_token>',
    host='<ph_client_api_host>'
)
3

Verify the connection

Send a test event to verify your setup:
posthog.capture(
    distinct_id='test_user',
    event='test_event'
)
Check your Activity feed to confirm the event was received.
Django Integration: If you’re using Django, check out the Django integration guide for automatic middleware setup and request tracking.

Capturing Events

Basic Event Capture

Capture events with a distinct user ID and optional properties:
posthog.capture(
    distinct_id='user_123',
    event='button_clicked',
    properties={
        'button_name': 'signup',
        'page': 'homepage'
    }
)

Capture with Timestamp

Include a custom timestamp for historical events:
from datetime import datetime

posthog.capture(
    distinct_id='user_123',
    event='purchase_completed',
    properties={'amount': 49.99},
    timestamp=datetime(2024, 1, 15, 10, 30)
)

Batch Events

Events are automatically batched for performance. Flush manually when needed:
posthog.capture('user_123', 'event_1')
posthog.capture('user_123', 'event_2')
posthog.capture('user_123', 'event_3')

# Flush all pending events
posthog.flush()

Identifying Users

Set User Identity

Identify users with their unique ID and properties:
posthog.identify(
    distinct_id='user_123',
    properties={
        'email': '[email protected]',
        'name': 'Jane Doe',
        'plan': 'premium',
        'signup_date': '2024-01-01'
    }
)

Update User Properties

Set or update properties for existing users:
posthog.set(
    distinct_id='user_123',
    properties={
        'plan': 'enterprise',
        'last_login': datetime.now().isoformat()
    }
)

Set Once

Set properties only if they don’t already exist:
posthog.set_once(
    distinct_id='user_123',
    properties={
        'first_visit': datetime.now().isoformat()
    }
)

Feature Flags

Check if Flag is Enabled

if posthog.feature_enabled('new-dashboard', 'user_123'):
    # Show new dashboard
    render_new_dashboard()
else:
    # Show old dashboard
    render_old_dashboard()

Get Flag Value

For multivariate flags, get the variant value:
variant = posthog.get_feature_flag('experiment-button-color', 'user_123')

if variant == 'blue':
    button_color = '#0000FF'
elif variant == 'green':
    button_color = '#00FF00'
else:
    button_color = '#FF0000'  # default

Get Flag Payload

Retrieve JSON payloads attached to feature flags:
payload = posthog.get_feature_flag_payload(
    'experiment-config',
    'user_123'
)

print(payload)  # {'theme': 'dark', 'maxItems': 10}

Include User Properties

Provide user properties for better flag targeting:
is_enabled = posthog.feature_enabled(
    'premium-feature',
    'user_123',
    person_properties={
        'plan': 'enterprise',
        'country': 'US'
    }
)

Get All Flags

Retrieve all feature flags for a user:
flags = posthog.get_all_flags('user_123')
print(flags)  # {'flag-1': True, 'flag-2': 'variant-a', 'flag-3': False}

Group Analytics

Associate users with groups (companies, organizations, etc.):
# Identify a group
posthog.group_identify(
    group_type='company',
    group_key='company_id_123',
    properties={
        'name': 'Acme Corporation',
        'plan': 'enterprise',
        'employees': 500
    }
)

# Capture event for a user in a group
posthog.capture(
    distinct_id='user_123',
    event='feature_used',
    properties={'feature': 'advanced_analytics'},
    groups={'company': 'company_id_123'}
)

Alias

Merge user identities when a user transitions from anonymous to identified:
# User was anonymous with ID 'anon_123'
# After signup, merge with permanent ID
posthog.alias(
    previous_id='anon_123',
    distinct_id='user_123'
)

Error Tracking

Capture Exceptions

try:
    risky_operation()
except Exception as e:
    posthog.capture_exception(
        e,
        distinct_id='user_123',
        properties={'context': 'payment_processing'}
    )

With Context Manager

with posthog.exception_autocapture(distinct_id='user_123'):
    # Any exception here is automatically captured
    process_payment()

Django Integration

For Django applications, use the Django-specific setup:
# settings.py
POSTHOG_API_KEY = '<ph_project_token>'
POSTHOG_HOST = '<ph_client_api_host>'

MIDDLEWARE = [
    'posthog.middleware.PostHogMiddleware',
    # ... other middleware
]

Flask Integration

from flask import Flask, request, session
from posthog import Posthog

app = Flask(__name__)
posthog = Posthog(
    project_api_key='<ph_project_token>',
    host='<ph_client_api_host>'
)

@app.route('/signup', methods=['POST'])
def signup():
    user_id = create_user(request.form)
    
    posthog.identify(
        distinct_id=user_id,
        properties={
            'email': request.form['email'],
            'name': request.form['name']
        }
    )
    
    posthog.capture(
        distinct_id=user_id,
        event='user_signed_up',
        properties={'source': 'web'}
    )
    
    return {'success': True}

FastAPI Integration

from fastapi import FastAPI, Depends
from posthog import Posthog

app = FastAPI()
posthog = Posthog(
    project_api_key='<ph_project_token>',
    host='<ph_client_api_host>'
)

@app.post('/api/purchase')
async def create_purchase(user_id: str, amount: float):
    # Process purchase
    purchase_id = process_payment(user_id, amount)
    
    # Track event
    posthog.capture(
        distinct_id=user_id,
        event='purchase_completed',
        properties={
            'amount': amount,
            'purchase_id': purchase_id
        }
    )
    
    return {'purchase_id': purchase_id}

Configuration Options

posthog = Posthog(
    project_api_key='<ph_project_token>',
    host='<ph_client_api_host>',
    
    # Batch settings
    max_batch_size=100,        # Max events per batch
    flush_at=20,               # Flush after this many events
    flush_interval=10,         # Flush after this many seconds
    
    # Performance
    max_queue_size=10000,      # Max queued events
    send=True,                 # Set to False for testing
    
    # Debugging
    debug=True,                # Enable debug logging
    on_error=lambda e, batch: print(f'Error: {e}')
)

Shutdown

Always flush and close the client when your application shuts down:
import atexit

# Ensure events are sent before exit
atexit.register(posthog.shutdown)

# Or call manually
posthog.shutdown()

Testing

Disable sending events in tests:
import os
from posthog import Posthog

posthog = Posthog(
    project_api_key='test_key',
    host='https://app.posthog.com',
    send=os.getenv('ENVIRONMENT') == 'production'
)
Or use a mock:
from unittest.mock import Mock

posthog = Mock()
posthog.capture('user_123', 'test_event')
posthog.capture.assert_called_once_with('user_123', 'test_event')

Best Practices

Use Environment Variables: Store your API key in environment variables, not in code.
Flush on Shutdown: Always call posthog.shutdown() or posthog.flush() before your application exits to ensure all events are sent.
Batch for Performance: The SDK automatically batches events. For high-volume applications, tune max_batch_size and flush_interval.
User Context: Include relevant user properties with identify() to enable better analytics and targeting.

Next Steps

Feature Flags

Learn advanced feature flag techniques

Django Integration

Full Django integration guide

API Reference

Complete Python SDK documentation

Group Analytics

Track B2B metrics by company or organization

Build docs developers (and LLMs) love