Mixpanel Event Tracking
Overview
Mixpanel is built from the ground up as an event-based analytics platform designed for product teams. Unlike web analytics tools that focus on pageviews, Mixpanel tracks granular user actions - button clicks, feature usage, form submissions - and connects them to individual user journeys over time.
The core philosophy is simple: understand what users do and who they are, then analyze retention, engagement, and conversion based on actual behavior, not just traffic. This makes Mixpanel exceptionally powerful for SaaS, mobile apps, and any product where user engagement matters more than page hits.
Event Model
How Mixpanel Events Work
Every event in Mixpanel consists of:
- Event Name: What happened (e.g., "Song Played", "Purchase Completed")
- Properties: Key-value pairs providing context (e.g.,
{"song_name": "Bohemian Rhapsody", "genre": "Rock"}) - Distinct ID: Unique identifier for the user
- Timestamp: When the event occurred (auto-captured or custom)
Event Structure:
{
"event": "Song Played",
"properties": {
"distinct_id": "user_12345",
"time": 1640000000,
"song_name": "Bohemian Rhapsody",
"artist": "Queen",
"genre": "Rock",
"duration_seconds": 354,
"platform": "mobile_app"
}
}
Key Concepts
- Events: Individual occurrences (plays, clicks, purchases)
- Event Properties: Attributes describing the event
- User Properties: Attributes describing the user (set via
people.set()) - Super Properties: Properties sent with every event automatically
- Distinct ID: Unique user identifier (persists across sessions)
Limits
- Event name: 255 characters
- Property name: 255 characters
- Property value: 255 characters for strings
- Events per project: Unlimited (billed by volume)
- Properties per event: 255 properties
Standard Events
Mixpanel doesn't enforce predefined events like GA4, but provides recommended event naming conventions for common use cases.
Recommended Event Patterns
Authentication:
Sign Up: User creates accountLogin: User authenticatesLogout: User logs out
Content Engagement:
Page Viewed: User views a page/screenContent Viewed: User views specific contentSearch Performed: User searchesFilter Applied: User filters results
Social Actions:
Share: User shares contentComment: User commentsLike: User likes contentFollow: User follows another user
Product Usage:
Feature Used: Generic feature interactionButton Clicked: Button/CTA clickedForm Submitted: Form completionVideo Played: Video engagement
Implementation Examples
// Sign Up
mixpanel.track('Sign Up', {
'source': 'homepage',
'method': 'email',
'referrer': document.referrer
});
// Content Engagement
mixpanel.track('Page Viewed', {
'page_name': 'Product Details',
'product_id': 'SKU_12345',
'category': 'Electronics'
});
// Social Action
mixpanel.track('Share', {
'content_type': 'article',
'content_id': 'blog_post_123',
'platform': 'Twitter'
});
Custom Events
Custom events are the heart of Mixpanel. You define events that matter to your product.
Creating Custom Events
// Basic event
mixpanel.track('Button Clicked');
// Event with properties
mixpanel.track('Video Watched', {
'video_title': 'Getting Started Tutorial',
'video_length': 180,
'percent_watched': 75,
'quality': '1080p'
});
// Event with user context
mixpanel.track('Feature Enabled', {
'feature_name': 'Dark Mode',
'subscription_tier': 'Pro',
'enabled_from': 'settings_page'
});
Python SDK:
from mixpanel import Mixpanel
mp = Mixpanel('YOUR_PROJECT_TOKEN')
# Track event
mp.track('user_12345', 'Purchase Completed', {
'product_id': 'SKU_789',
'price': 49.99,
'currency': 'USD',
'payment_method': 'credit_card'
})
# Track with timestamp
import time
mp.track('user_12345', 'App Opened', {
'platform': 'iOS',
'version': '2.1.0'
}, meta={'time': int(time.time())})
Mobile (iOS - Swift):
// Track event
Mixpanel.mainInstance().track(event: "Level Completed", properties: [
"level_name": "Forest Temple",
"level_number": 5,
"score": 8750,
"time_seconds": 234
])
// Increment user property
Mixpanel.mainInstance().people.increment(property: "levels_completed", by: 1)
Mobile (Android - Kotlin):
val mixpanel = MixpanelAPI.getInstance(context, "YOUR_PROJECT_TOKEN")
// Track event
val properties = JSONObject()
properties.put("level_name", "Forest Temple")
properties.put("level_number", 5)
properties.put("score", 8750)
properties.put("time_seconds", 234)
mixpanel.track("Level Completed", properties)
Event Naming Best Practices
✅ Good Event Names:
Purchase Completed(clear action)Video Played(specific behavior)Onboarding Step Completed(descriptive)
❌ Bad Event Names:
click(too generic)PurchaseCompleted(use Title Case with spaces)purchase_completed_successfully_by_user(too verbose)
Ecommerce Events
Mixpanel supports comprehensive ecommerce tracking through custom events and properties.
Product Discovery
// Product List Viewed
mixpanel.track('Product List Viewed', {
'list_id': 'search_results',
'category': 'Electronics',
'products_count': 24,
'filters_applied': ['price_low_to_high', 'in_stock']
});
// Product Viewed
mixpanel.track('Product Viewed', {
'product_id': 'SKU_12345',
'product_name': 'Wireless Headphones',
'price': 199.99,
'currency': 'USD',
'category': 'Audio',
'brand': 'BrandName',
'in_stock': true,
'variant': 'Black'
});
Cart Actions
// Product Added
mixpanel.track('Product Added', {
'product_id': 'SKU_12345',
'product_name': 'Wireless Headphones',
'price': 199.99,
'quantity': 1,
'cart_total': 199.99,
'cart_item_count': 1
});
// Product Removed
mixpanel.track('Product Removed', {
'product_id': 'SKU_12345',
'reason': 'user_removed',
'cart_total': 0
});
// Cart Viewed
mixpanel.track('Cart Viewed', {
'cart_total': 399.98,
'cart_item_count': 2,
'product_ids': ['SKU_12345', 'SKU_67890']
});
Checkout Flow
// Checkout Started
mixpanel.track('Checkout Started', {
'checkout_id': 'CHK_ABC123',
'cart_total': 399.98,
'item_count': 2,
'checkout_type': 'express'
});
// Payment Info Added
mixpanel.track('Payment Info Added', {
'checkout_id': 'CHK_ABC123',
'payment_method': 'credit_card',
'card_type': 'visa'
});
// Order Completed
mixpanel.track('Order Completed', {
'order_id': 'ORDER_12345',
'revenue': 399.98,
'tax': 32.00,
'shipping': 15.00,
'total': 446.98,
'currency': 'USD',
'item_count': 2,
'payment_method': 'credit_card',
'coupon_code': 'SAVE20',
'discount_amount': 80.00,
'is_first_purchase': true
});
// Track revenue (appears in Revenue report)
mixpanel.people.track_charge(446.98, {
'order_id': 'ORDER_12345',
'products': ['SKU_12345', 'SKU_67890']
});
Subscription Events
// Subscription Started
mixpanel.track('Subscription Started', {
'plan_name': 'Pro Annual',
'plan_price': 299.99,
'billing_cycle': 'yearly',
'trial_days': 14,
'payment_method': 'credit_card'
});
// Subscription Upgraded
mixpanel.track('Subscription Upgraded', {
'old_plan': 'Basic Monthly',
'new_plan': 'Pro Monthly',
'price_difference': 20.00
});
// Subscription Cancelled
mixpanel.track('Subscription Cancelled', {
'plan_name': 'Pro Monthly',
'cancellation_reason': 'too_expensive',
'months_subscribed': 6
});
User Properties
User properties (called "People Properties" in Mixpanel) describe who the user is, not what they did.
Setting User Properties
JavaScript:
// Set properties (overwrites)
mixpanel.people.set({
'name': 'John Doe',
'email': 'john@example.com',
'subscription_tier': 'Pro',
'signup_date': '2024-01-15',
'total_purchases': 3
});
// Set once (never overwrites)
mixpanel.people.set_once({
'first_visit': new Date().toISOString(),
'initial_referrer': document.referrer
});
// Increment numeric properties
mixpanel.people.increment('page_views');
mixpanel.people.increment('credits_balance', 10);
// Append to list
mixpanel.people.append('favorite_categories', 'Electronics');
// Union (add unique values to list)
mixpanel.people.union('devices_used', ['iPhone', 'iPad']);
Python:
# Set properties
mp.people_set('user_12345', {
'name': 'John Doe',
'email': 'john@example.com',
'subscription_tier': 'Pro'
})
# Increment
mp.people_increment('user_12345', {'login_count': 1})
# Append
mp.people_append('user_12345', {
'transactions': 'ORDER_12345'
})
Standard User Properties
Mixpanel recognizes these special properties:
$email: Email address (used for messaging)$name: Full name$created: Account creation date$phone: Phone number$avatar: Profile picture URL
Setting Standard Properties:
mixpanel.people.set({
'$email': 'john@example.com',
'$name': 'John Doe',
'$created': '2024-01-15T10:30:00',
'$phone': '+1-555-123-4567'
});
Custom User Properties
mixpanel.people.set({
'company_size': '50-100',
'industry': 'Technology',
'role': 'Product Manager',
'feature_flags': {
'new_dashboard': true,
'beta_features': true
},
'lifetime_value': 1299.99,
'last_active': new Date().toISOString()
});
Event Properties
Event properties provide context about what happened during an event.
Default Properties
Mixpanel automatically captures:
$browser: Browser name$browser_version: Browser version$os: Operating system$device: Device model$screen_height,$screen_width: Screen dimensions$city,$region,$country: Geographic location$referrer: Referring URL$referring_domain: Referring domainmp_lib: SDK name/version
Super Properties
Properties that automatically attach to every event:
// Register super property (persists across sessions)
mixpanel.register({
'app_version': '2.1.0',
'environment': 'production',
'subscription_tier': 'Pro'
});
// Register once (won't overwrite)
mixpanel.register_once({
'initial_utm_source': 'google',
'first_landing_page': '/pricing'
});
// Unregister super property
mixpanel.unregister('environment');
Custom Event Properties
// Rich event properties
mixpanel.track('Article Read', {
'article_id': 'blog_123',
'article_title': 'Getting Started with Analytics',
'word_count': 1500,
'read_time_seconds': 240,
'scroll_depth_percent': 85,
'author': 'Jane Smith',
'category': 'Tutorial',
'tags': ['analytics', 'getting-started', 'beginner'],
'published_date': '2024-01-10',
'is_premium_content': false
});
Property Data Types
Mixpanel supports:
- String:
"Premium User" - Number:
29.99,42 - Boolean:
true,false - Date:
new Date(), ISO 8601 strings - Array:
["tag1", "tag2"](use for multi-select) - Object:
{key: "value"}(limited support, prefer flat structure)
Implementation Methods
1. JavaScript SDK (Web)
Installation:
<!-- Snippet (Recommended) -->
<script type="text/javascript">
(function(f,b){if(!b.__SV){var e,g,i,h;window.mixpanel=b;b._i=[];b.init=function(e,f,c){function g(a,d){var b=d.split(".");2==b.length&&(a=a[b[0]],d=b[1]);a[d]=function(){a.push([d].concat(Array.prototype.slice.call(arguments,0)))}}var a=b;"undefined"!==typeof c?a=b[c]=[]:c="mixpanel";a.people=a.people||[];a.toString=function(a){var d="mixpanel";"mixpanel"!==c&&(d+="."+c);a||(d+=" (stub)");return d};a.people.toString=function(){return a.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
for(h=0;h<i.length;h++)g(a,i[h]);var j="set set_once union unset remove delete".split(" ");a.get_group=function(){function b(c){d[c]=function(){call2_args=arguments;call2=[c].concat(Array.prototype.slice.call(call2_args,0));a.push([e,call2])}}for(var d={},e=["get_group"].concat(Array.prototype.slice.call(arguments,0)),c=0;c<j.length;c++)b(j[c]);return d};b._i.push([e,f,c])};b.__SV=1.2;e=f.createElement("script");e.type="text/javascript";e.async=!0;e.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?
MIXPANEL_CUSTOM_LIB_URL:"file:"===f.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";g=f.getElementsByTagName("script")[0];g.parentNode.insertBefore(e,g)}})(document,window.mixpanel||[]);
mixpanel.init('YOUR_PROJECT_TOKEN', {
debug: false,
track_pageview: true,
persistence: 'localStorage'
});
</script>
NPM:
npm install mixpanel-browser
import mixpanel from 'mixpanel-browser';
mixpanel.init('YOUR_PROJECT_TOKEN', {
debug: true,
ignore_dnt: true
});
// Track event
mixpanel.track('Page Viewed');
2. Server-Side SDKs
Node.js:
const Mixpanel = require('mixpanel');
const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN');
// Track event
mixpanel.track('Server Action', {
distinct_id: 'user_123',
action_type: 'api_call',
endpoint: '/api/users'
});
// Set user properties
mixpanel.people.set('user_123', {
$email: 'user@example.com',
plan: 'Enterprise'
});
Python:
from mixpanel import Mixpanel
mp = Mixpanel('YOUR_PROJECT_TOKEN')
# Track event
mp.track('user_123', 'API Request', {
'endpoint': '/api/data',
'method': 'GET',
'response_time_ms': 45
})
# Set people properties
mp.people_set('user_123', {
'$email': 'user@example.com',
'plan': 'Enterprise'
})
Ruby:
require 'mixpanel-ruby'
tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN')
# Track event
tracker.track('user_123', 'Purchase', {
'product_id' => 'SKU_789',
'price' => 49.99
})
# Set properties
tracker.people.set('user_123', {
'$email' => 'user@example.com',
'total_purchases' => 5
})
3. Mobile SDKs
iOS (Swift):
import Mixpanel
// Initialize
Mixpanel.initialize(token: "YOUR_PROJECT_TOKEN")
// Track
Mixpanel.mainInstance().track(event: "Button Tapped", properties: [
"button_name": "signup",
"screen": "home"
])
// Identify user
Mixpanel.mainInstance().identify(distinctId: "user_123")
// Set user properties
Mixpanel.mainInstance().people.set(properties: [
"$email": "user@example.com",
"name": "John Doe"
])
Android (Kotlin):
import com.mixpanel.android.mpmetrics.MixpanelAPI
val mixpanel = MixpanelAPI.getInstance(context, "YOUR_PROJECT_TOKEN")
// Track
val props = JSONObject()
props.put("button_name", "signup")
props.put("screen", "home")
mixpanel.track("Button Tapped", props)
// Identify
mixpanel.identify("user_123")
// Set user properties
mixpanel.people.set("email", "user@example.com")
mixpanel.people.set("name", "John Doe")
4. HTTP API (Direct)
curl https://api.mixpanel.com/track \
-d 'data=eyJldmVudCI6ICJTaWduIFVwIiwgInByb3BlcnRpZXMiOiB7InRva2VuIjogIllPVVJfVE9LRU4iLCAiZGlzdGluY3RfaWQiOiAidXNlcl8xMjMiLCAic291cmNlIjogImhvbWVwYWdlIn19'
Where data is base64 encoded JSON:
{
"event": "Sign Up",
"properties": {
"token": "YOUR_TOKEN",
"distinct_id": "user_123",
"source": "homepage"
}
}
Debugging & Validation
1. Mixpanel Debugger (Browser Console)
Enable debug mode:
mixpanel.init('YOUR_PROJECT_TOKEN', {debug: true});
Look for console logs:
[Mixpanel] Tracking "Page Viewed" with properties: {...}
[Mixpanel] Sent request to Mixpanel API
2. Live View
In Mixpanel interface:
- Navigate to Events > Live View
- Perform actions on your site/app
- See events appear in real-time (within seconds)
- Inspect event properties and user profiles
3. Events Page
- Events > All Events: See all event names
- Events > Event Details: Dive into specific event
- Click event to see sample properties and volume
4. Network Tab Inspection
Look for requests to:
https://api.mixpanel.com/track
https://api.mixpanel.com/engage (for People properties)
Inspect payload (base64 decode):
// Decode in console
atob('BASE64_STRING_FROM_NETWORK_TAB')
5. Mixpanel SDK Helpers
// Check current distinct_id
console.log(mixpanel.get_distinct_id());
// Check super properties
console.log(mixpanel.persistence.properties());
// Get all tracked data
console.log(mixpanel.get_config());
6. Data Validation
Check for:
- Events appearing in Live View
- Correct property names and values
- User profiles updating (People page)
- No duplicate users (merge if needed)
- Proper distinct_id assignment
Best Practices
Event Design
✅ Do:
- Use clear, action-based event names: "Purchase Completed" not "Purchase"
- Track user intent, not just clicks: "Search Performed" not "Button Clicked"
- Include relevant context in properties
- Be consistent with naming conventions (Title Case)
- Track both successes and failures
❌ Don't:
- Create overly specific events: "HomePage_BlueButton_Clicked_MorningSession"
- Use generic names: "Event1", "Action", "Click"
- Track PII without hashing
- Send excessive events (performance impact)
Property Strategy
✅ Do:
- Use descriptive property names:
video_titlenotvt - Keep property names consistent across events
- Use appropriate data types (numbers for metrics, not strings)
- Document property definitions
- Use arrays for multi-value properties
❌ Don't:
- Send empty/null values
- Use inconsistent property names:
user_idvsuserId - Nest objects deeply (flatten when possible)
- Exceed property limits
User Identity
// Before login (anonymous)
mixpanel.track('Page Viewed');
// After sign up - create alias
mixpanel.alias('user_12345'); // Links anonymous ID to user ID
// After login - identify
mixpanel.identify('user_12345');
// Set user properties
mixpanel.people.set({
'$email': 'user@example.com',
'name': 'John Doe'
});
Identity Best Practices:
- Call
alias()once when user first signs up - Call
identify()on every login/session start - Use consistent distinct_id format
- Don't reuse distinct_ids across different users
Performance
- Initialize SDK early in page load
- Batch events when possible (server-side)
- Avoid tracking every scroll/mouse move
- Use sampling for high-volume events
- Consider async tracking for non-critical events
Privacy & Compliance
✅ Do:
- Implement opt-out mechanisms
- Hash sensitive identifiers
- Configure EU data residency if needed
- Document data retention policies
- Respect Do Not Track (optional)
// Opt user out
mixpanel.opt_out_tracking();
// Opt user in
mixpanel.opt_in_tracking();
// Check opt-out status
if (!mixpanel.has_opted_out_tracking()) {
mixpanel.track('Event');
}
❌ Don't:
- Track PII without consent
- Send passwords, credit cards, SSNs
- Ignore regional privacy laws (GDPR, CCPA)
Testing
- Use separate projects for dev/staging/prod
- Test identity merging scenarios
- Validate event properties before launch
- Monitor for duplicate events
- Set up alerts for anomalies
Additional Resources: