Trustworthy data

Identity stitching: why your user counts don't match

Your analytics tool counts one person as several until you call identify at login and signup. Get it wrong and every per-user number is wrong with it. Call identify with your own internal user ID, and only after the user has authenticated.

By Ansh Agrawal4 min readUpdated

Say your analytics tool reports more signups last month than your own database does. Same period, same definition, two different numbers. That gap is usually identity.

identify is the line of code that tells your analytics tool "this visitor is user 123". Before it runs, the tool only knows a visitor by a random Anonymous IDThe random identifier an analytics tool gives a visitor before it knows who they are. It survives until identify runs, and everything done before that point hangs off it.Glossary. After it runs, the tool knows who they are. Identity stitchingJoining all of one person's activity into a single profile, whatever device or browser it arrived from. Get it wrong and every per-user number is wrong in the same direction.Glossary means joining all of one person's activity into one profile, no matter which device or browser it came from.

Let's start with where people go wrong.

What are the common identity stitching mistakes?

  1. Calling identify before the user has authenticated. Authenticated means they've logged in or signed up. People fire identify the moment someone lands on the website or installs and opens the app. At that point you have no user ID, no email, nothing. So there's nothing real to tie the profile to. If that same person opens your product in another browser tomorrow, you still have no way of knowing it's them. Call it at login or signup instead, more on this below.
  2. Not linking IDs across sources. Some of your events come from the client, meaning the user's browser or app. Others come from your server, your own backend, for things like "payment succeeded". Both streams need to carry the same user ID. When they don't match, one person turns into several profiles instead of one. The fix: send the same internal user ID with every event, from both places.
  3. Losing the user across subdomains. The tool remembers a visitor with a cookie, a small file saved in their browser. www.yoursite.com and app.yoursite.com are subdomains of one root domain, and most tools share that cookie across them by default (Mixpanel, PostHog and Amplitude all do). It breaks when somebody has switched that setting off, when a hosted subdomain refuses the cookie, or when your marketing site and your app sit on two different root domains, say yoursite.com and yourapp.io. Either way, the same person ends up with one profile on the marketing site and another on the app. If the setting is off in the initialisation script of the analytics tool, turn it back on.
MistakeWhat you seeFix
Calling identify before the user has authenticatedNothing real to tie the profile to, so the same person in another browser tomorrow can't be recognisedCall it at login or signup
Client and server events carry different user IDsOne person turns into several profilesSend the same internal user ID with every event, from both places
The cookie isn't shared across subdomainsOne profile on the marketing site and another on the appIf the setting is off in the analytics tool's initialisation script, turn it back on

How should identity stitching work?

Someone arrives on your website as an anonymous visitor. Every Product analyticsThe analysis of what users do inside your product. The test is whether you can point at a row of data and name the user who did it.Glossary tool assigns them an anonymous ID on its own.

The moment they authenticate, at login or at signup, you call identify and give them a user ID. Use your internal user ID if you have one, the ID your own database gives each account. Email and phone number both work, but people change them. When they do, that user splits in two, which is the exact problem you're trying to avoid. Mixpanel's identity management guide gives the same advice: call identify at registration or login, with an ID that is unique to each user and doesn't change, such as a database ID. PostHog's guide to identifying users and Amplitude's advice on user IDs say the same.

identify creates a mapping between the anonymous ID and the user ID. Everything they did before logging in now sits in the same profile as everything they do after.

Say that user picks up a different laptop tomorrow, or a different browser, or their phone. They get a fresh anonymous ID. They log in, identify fires with the same user ID, and Mixpanel or whichever tool you use pulls that activity into the same profile. One person, one profile.

Anonymous ID assigned → user logs in → identify fires with your user ID → past and future activity land in one profile.

One person on a laptop (anon_a1), a phone (anon_b7) and a second browser (anon_c3) logs in on each, identify(user_123) fires, and all three feed a single profile, user_123, with every event from every device in one place.

How do you know identity stitching is broken?

Nothing alerts you when identity stitching fails. It quietly inflates your user count instead.

So compare that count against a source you trust. Take the number of users your analytics tool reports for a period, for example users who signed up last month. Put it next to the same number from your own database or your billing system. If analytics is meaningfully higher, you're counting one person as several.

That gap drags down every per-user number you have. ActivationThe point where a new user reaches the first moment of real value in your product, rather than merely creating an account. Every product defines its own activation event, and that definition decides what your activation rate means.Glossary falls because the denominator, the number of users you divide by, is too big. Events per user falls. RetentionThe share of users who come back. Bounded retention counts people who returned on exactly that day; unbounded counts that day or any day after, and the two give very different numbers.Glossary looks worse than it is, because someone who came back on a second device looks like someone who never came back.

In an illustrative example, true activation is 50%, 50 activated out of 100 people, but the tool shows 36%, 50 activated out of 140 profiles. Split profiles inflate the denominator, so activation, events per user and retention all look worse for the same people doing the same things.

Common questions

What is identity stitching in analytics?

Joining all of one person's activity into a single profile regardless of device or browser. Before identify runs, the tool knows a visitor only by a random anonymous ID; after it runs, that activity merges under the real user.

Why does Mixpanel show more users than my database?

Usually identity. If identify isn't called, or is called before the user has authenticated, one person is counted as several anonymous visitors and every per-user number inflates.

When should you call identify?

At login and at signup, once the user has authenticated. Calling it the moment someone lands on the website or opens the app gives it no user ID, so there's nothing real to tie the profile to.

Should you use email as the user ID in analytics?

Use your internal user ID, the one your own database gives each account, if you have one. Email and phone number both work, but people change them, and when they do that user splits in two.