PostHog counts unique users by distinct_id — but only if you've actually identified them. If you're seeing inflated numbers or struggling to segment by user properties, your identification isn't set up right. Here's how to do it correctly.
Identify Users with posthog.identify()
PostHog needs a consistent identifier for each user to count them as unique. The most common approach is to identify them when they log in or sign up.
Call identify() when a user logs in or signs up
As soon as you know who the user is, call posthog.identify() with their user ID and optional properties. PostHog uses this ID to track that user across sessions and devices.
// When user logs in or signs up
const userId = user.id; // e.g., "user_12345"
const userProperties = {
email: user.email,
name: user.name,
plan: user.subscription_plan,
created_at: user.created_at
};
posthog.identify(userId, userProperties);Always use the same distinct_id for the same user
PostHog's unique user count is based on distinct IDs. If the same user gets different IDs in different sessions, PostHog counts them as multiple users. Use your database user ID — not session IDs or email addresses (which can change).
// ❌ Wrong: Using email as distinct_id
posthog.identify(user.email); // Email can change
// ✅ Right: Using stable user ID from your database
posthog.identify(user.id); // Doesn't changePass user properties to enable segmentation
The second argument to identify() should be an object of user properties. These let you segment unique users by plan, country, company, or other attributes in the Insights UI.
posthog.identify(userId, {
email: user.email,
company_id: user.company_id,
plan: 'professional',
signup_date: user.created_at,
is_active: true
});identify() once per session, not every time you capture an event. PostHog batches calls automatically, so you won't waste quota.View Unique Users in Insights
Once users are identified, you can query unique user counts in the Insights tab and break them down by properties, dates, or events.
Create a new insight with the Unique Users metric
Navigate to Insights and click + New insight. In the metric dropdown, select Unique users instead of total events. This counts how many distinct user IDs fired an event in your time range.
// This is what PostHog does under the hood:
// SELECT count(DISTINCT distinct_id) FROM events WHERE {filters};
// Your JavaScript SDK doesn't need to do anything—
// just make sure you called identify() earlier.Segment unique users by properties or events
In the insight builder, click Breakdown by and choose a user property like plan or country. Now you'll see unique user counts for each segment. Use Filter to restrict to specific cohorts or event types.
// No SDK code needed for this—it's all in the UI.
// But make sure your events are capturing context:
posthog.capture('page_view', {
page: '/pricing',
utm_source: 'google',
user_plan: user.plan
});Compare unique users across time ranges
Use the date range picker at the top of the insight to compare unique user counts week-over-week or month-over-month. The Change column shows the percentage difference automatically.
// SDK-side, ensure you're tracking user activity consistently:
posthog.capture('feature_used', {
feature: 'export_data',
export_format: 'csv'
});
// PostHog will aggregate these by day/week/month based on your insight settings.distinct_id (e.g., after a logout), PostHog will count them as a new user. Use posthog.alias(old_id, new_id) to merge IDs instead.Debug Unique User Counts
If your unique user count seems off, check that users are actually being identified and events are tagged with the right distinct_id.
Verify identify() is being called
Check your browser console or SDK logs to confirm posthog.identify() is firing when users log in. If it's not being called at all, PostHog will use anonymous IDs (your distinct_id will be a UUID), which inflates your unique count.
// Add a debug log to confirm identify() is called:
posthog.identify(userId, props);
console.log(`Identified user: ${userId}`);
// Check the PostHog SDK state:
console.log(posthog.getDistinctId()); // Should return your userId, not a UUIDCheck user properties in the PostHog interface
Go to Data Management > People and search for a specific user. You should see their distinct_id and all properties you set via identify(). If properties are missing or the ID looks like a random string, your identification code isn't running.
// If your user doesn't appear in PostHog People, check:
if (typeof posthog !== 'undefined') {
posthog.identify(userId, { email: user.email });
} else {
console.error('PostHog SDK not loaded');
}identify or capture requests and check the distinct_id and properties being sent.Common Pitfalls
- Calling identify() with non-stable IDs (like session tokens or email addresses) — your unique count balloons because the same user gets counted multiple times.
- Not calling identify() at all — PostHog assigns random UUID distinct_ids, making it impossible to link users across devices or sessions.
- Capturing events before identifying users — events won't be tied to the user unless you call identify() first (or in the same batch).
- Mixing identified and anonymous events — if part of your traffic identifies users and part doesn't, your unique count will be hard to interpret.
Wrapping Up
Tracking unique users in PostHog boils down to identifying users with a stable ID, attaching user properties for segmentation, and checking the Unique users metric in Insights. If your counts seem off, make sure identify() is being called and you're using the same ID consistently. If you want to track this automatically across tools, Product Analyst can help.