How to Build a Real-Time Dashboard with the Mixpanel API
·APIScout Team
mixpanelanalyticsdashboardtutorialapi integration
How to Build a Real-Time Dashboard with the Mixpanel API
Mixpanel tracks user behavior and lets you query that data via API. This guide shows how to instrument your app with events, then build a custom dashboard that displays funnels, retention, and engagement metrics in real time.
What You'll Build
- Event tracking (page views, button clicks, signups, purchases)
- Custom analytics dashboard with charts
- Funnel analysis (signup → activation → conversion)
- Retention cohort visualization
- Real-time event feed
Prerequisites: Node.js 18+, Mixpanel account (free: 20M events/month).
1. Setup
Install
# Server-side tracking
npm install mixpanel
# Client-side tracking
npm install mixpanel-browser
Initialize Server-Side
// lib/mixpanel.ts
import Mixpanel from 'mixpanel';
export const mixpanel = Mixpanel.init(process.env.MIXPANEL_TOKEN!, {
host: 'api.mixpanel.com',
});
Initialize Client-Side
// lib/mixpanel-client.ts
import mixpanel from 'mixpanel-browser';
mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_TOKEN!, {
track_pageview: true,
persistence: 'localStorage',
});
export default mixpanel;
2. Track Events
Server-Side Events
// Track signup
mixpanel.track('Sign Up', {
distinct_id: user.id,
email: user.email,
plan: 'free',
source: 'organic',
});
// Track purchase
mixpanel.track('Purchase', {
distinct_id: user.id,
amount: 29.00,
plan: 'pro',
currency: 'USD',
});
// Set user profile
mixpanel.people.set(user.id, {
$email: user.email,
$name: user.name,
plan: 'pro',
signup_date: new Date().toISOString(),
});
Client-Side Events
// components/TrackingProvider.tsx
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import mixpanel from '@/lib/mixpanel-client';
export function TrackingProvider({ children, userId }: {
children: React.ReactNode;
userId?: string;
}) {
const pathname = usePathname();
useEffect(() => {
if (userId) {
mixpanel.identify(userId);
}
}, [userId]);
useEffect(() => {
mixpanel.track('Page View', { path: pathname });
}, [pathname]);
return <>{children}</>;
}
// Track button clicks
export function trackClick(name: string, props?: Record<string, any>) {
mixpanel.track('Button Click', { button_name: name, ...props });
}
Event Naming Best Practices
| Event | Properties | When |
|---|---|---|
Sign Up | plan, source, method | User creates account |
Login | method (email, google, github) | User logs in |
Page View | path, referrer | Every page load |
Feature Used | feature_name, context | User engages with feature |
Purchase | amount, plan, currency | Payment completed |
Subscription Changed | from_plan, to_plan | Upgrade/downgrade |
Churn | reason, days_active | User cancels |
3. Query Data via API
Authentication
Mixpanel uses service account authentication for the Data Export API:
// lib/mixpanel-query.ts
const MIXPANEL_PROJECT_ID = process.env.MIXPANEL_PROJECT_ID!;
const MIXPANEL_SERVICE_ACCOUNT = process.env.MIXPANEL_SERVICE_ACCOUNT!;
const MIXPANEL_SERVICE_SECRET = process.env.MIXPANEL_SERVICE_SECRET!;
const auth = Buffer.from(
`${MIXPANEL_SERVICE_ACCOUNT}:${MIXPANEL_SERVICE_SECRET}`
).toString('base64');
export async function queryMixpanel(endpoint: string, params: Record<string, string>) {
const url = new URL(`https://mixpanel.com/api/query${endpoint}`);
url.searchParams.set('project_id', MIXPANEL_PROJECT_ID);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
const res = await fetch(url.toString(), {
headers: { Authorization: `Basic ${auth}` },
});
return res.json();
}
Query Event Counts
// Get daily signups for the last 30 days
const signupData = await queryMixpanel('/insights', {
event: JSON.stringify([{ event: 'Sign Up' }]),
type: 'general',
unit: 'day',
interval: '30',
});
Funnel Query
const funnelData = await queryMixpanel('/funnels', {
funnel_id: 'your_funnel_id',
from_date: '2026-02-06',
to_date: '2026-03-08',
unit: 'week',
});
4. Build the Dashboard
Dashboard API Route
// app/api/dashboard/route.ts
import { NextResponse } from 'next/server';
import { queryMixpanel } from '@/lib/mixpanel-query';
export async function GET() {
const [signups, purchases, activeUsers] = await Promise.all([
queryMixpanel('/insights', {
event: JSON.stringify([{ event: 'Sign Up' }]),
type: 'general',
unit: 'day',
interval: '30',
}),
queryMixpanel('/insights', {
event: JSON.stringify([{ event: 'Purchase' }]),
type: 'general',
unit: 'day',
interval: '30',
}),
queryMixpanel('/insights', {
event: JSON.stringify([{ event: 'Login' }]),
type: 'unique',
unit: 'day',
interval: '7',
}),
]);
return NextResponse.json({ signups, purchases, activeUsers });
}
Dashboard Component
// app/dashboard/analytics/page.tsx
'use client';
import { useEffect, useState } from 'react';
interface DashboardData {
signups: any;
purchases: any;
activeUsers: any;
}
export default function AnalyticsDashboard() {
const [data, setData] = useState<DashboardData | null>(null);
useEffect(() => {
fetch('/api/dashboard')
.then(res => res.json())
.then(setData);
}, []);
if (!data) return <div>Loading analytics...</div>;
return (
<div className="grid grid-cols-3 gap-6">
<MetricCard
title="Signups (30d)"
value={sumEvents(data.signups)}
trend={calculateTrend(data.signups)}
/>
<MetricCard
title="Purchases (30d)"
value={sumEvents(data.purchases)}
trend={calculateTrend(data.purchases)}
/>
<MetricCard
title="Active Users (7d)"
value={sumEvents(data.activeUsers)}
trend={calculateTrend(data.activeUsers)}
/>
<div className="col-span-3">
<h3>Daily Signups</h3>
{/* Render chart with data.signups time series */}
</div>
</div>
);
}
function MetricCard({ title, value, trend }: {
title: string;
value: number;
trend: number;
}) {
return (
<div className="border rounded-lg p-6">
<p className="text-sm text-gray-500">{title}</p>
<p className="text-3xl font-bold">{value.toLocaleString()}</p>
<p className={trend >= 0 ? 'text-green-500' : 'text-red-500'}>
{trend >= 0 ? '↑' : '↓'} {Math.abs(trend)}%
</p>
</div>
);
}
5. Key Metrics to Track
| Metric | Mixpanel Query | Business Value |
|---|---|---|
| DAU / MAU | Unique "Login" events | User engagement |
| Signup conversion | Funnel: Visit → Signup | Acquisition efficiency |
| Activation rate | Funnel: Signup → First Value Action | Onboarding quality |
| Revenue per user | "Purchase" events aggregated | Monetization |
| Retention (Day 1/7/30) | Retention report on "Login" | Stickiness |
| Feature adoption | "Feature Used" by feature_name | Product-market fit |
| Churn rate | Users inactive for 30+ days | Retention health |
Pricing
| Plan | Events/Month | Price |
|---|---|---|
| Free | 20M | $0 |
| Growth | 100M+ | From $20/month |
| Enterprise | Custom | Custom |
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Tracking too many events | Noise, hard to find insights | Track 10-15 key events max |
| Not identifying users | Can't track across sessions | Call identify() after login |
| Events without properties | Useless data | Always add context (plan, source, etc.) |
| Client-only tracking | Missed server events (purchases) | Use server-side for critical events |
| No naming convention | "signup", "Sign_Up", "user_signup" | Pick a convention and enforce it |
Choosing analytics? Compare Mixpanel vs PostHog vs Amplitude on APIScout — pricing, features, and which fits your stack.