Identifying Ghost members with PostHog

An extension to PostHog's installation guide for Ghost that allows identifying subscribers

Identifying Ghost members with PostHog

I've mentioned I've used PostHog for product and web analytics in the past, but one thing you should know is that I also track visitors to this site using PostHog.

It's been useful to know where my visitors are coming from and, more precisely, what companies are checking me out. Simply by adding UTM parameters in the link to my blog in my CVs.

Well, one thing I did not like was that I couldn't figure out whether my subscribers were visiting me. Sure, I could make a guess by checking the city of the visitor because I don't have many subscribers, but I knew something could be done.

So this post is an extension of PostHog's ghost installation guide, in which I detail how to call Ghost's API to track subscribers.

Extending the code snippet

PostHog's guide gives you the following snippet:

<script>
    !function(t,e){ /* removed because its a really long line */ }
    posthog.init('your-posthog-key', {
        api_host: 'https://eu.i.posthog.com',
        persistence: "memory", // For cookieless tracking
    })
</script>

We should copy and paste this in the code injection section of Ghost, as instructed in the guide, but should modify its body to include a call to the Members API as such:

<script>
    !function(t,e){ /* removed because its a really long line */ }
    posthog.init('your-posthog-key', {
        api_host: 'https://eu.i.posthog.com',
        persistence: "memory", // For cookieless tracking
    })
    fetch("/members/api/member/", {
      "method": "GET",
    })
      .then(r => {
      // Ghost returns 204 when making unauthenticated requests
      if (r.status !== 200) throw new Error("Couldn't identify member");
      return r.json()
    })
      .then(({email}) => {  // using `email` for demonstration only
      // Call identify
      posthog.identify(email)
    })
      .catch(console.error);
</script>

A note on privacy and data usage

Before diving into implementation, it's important to understand what happens when you send member data from Ghost to PostHog.

By calling posthog.identify(email), you're explicitly linking a visitor session to a specific person (via their email address). This means that any actions taken on your site from that point forward can be tied back to that individual in PostHog. While this can be incredibly useful for understanding subscriber behavior, it also means you're storing personal data in PostHog’s infrastructure.

A few considerations:

  • Transparency: If you track identified users, you should clearly communicate this in your site's privacy policy or terms of service.
  • Data residency: Make sure your PostHog instance (e.g. eu.i.posthog.com) aligns with your data residency or GDPR requirements.
  • Minimization: Only send what you need. While emails are useful for identification, think twice before tracking more sensitive attributes unless truly necessary.
  • Retention: Be mindful of how long PostHog stores data and whether you're purging stale profiles.

In fact, Ghost recommends using the member field uuid instead of the user email for analytics. I personally do it by calling posthog.identify(`subscriber_${uuid}`).

In short, PostHog gives you powerful tools, but it’s up to you to use them responsibly, especially when it comes to user privacy.