Send events

Last updated:

Once your PostHog instance is up and running, the next step is to start sending events.

We recommend starting with autocapture for your web app as it's the quickest way to get set up, gives you full coverage, and avoids manually adding custom events. On top you can add custom events to track the most important events.

Tip: Autocapture sends lots of events. We provide a generous free tier but as you scale you might switch to mainly using custom events on the areas of the product that are more stable and tune autocapture to your needs.

1. Setup autocapture

When you call posthog.init the PostHog JS library begins automatically capturing user events:

  • Page views, including the URL
  • Autocaptured events, such as any click, change of input, or submission associated with a, button, form, input, select, textarea, and label tags

Hiding certain elements

PostHog puts significant effort into ensuring it doesn't capture sensitive data from your website. However, if there are specific elements you want to ensure aren't captured, you can add the ph-no-capture class name.

<input class='ph-no-capture'><!-- Input with sensitive user info --></input>

Tip: Including the ph-no-capture class will also exclude elements from being shown in session recordings.

2. Capture custom events

Setting up autocapture is a great way to get started, but typically when integrating tracking into your product, you'll want to send additional events for when specific things occur.

On the client-side, we can use the capture method, with the first argument being the name of the event you want to track.

posthog.capture('User signed up')

Tip: We recommend adding custom events for the most important actions in your product that are unlikely to change, such as user sign-ups, purchases, and when features are used. While you can name your events however you'd like, we typically recommend using a '[object][verb]' format, where '[object]' is whatever entity the action relates to, and the '[verb]' is the action itself. Some examples of this could include: Project created, User signed up, or Invite sent.

At first, it may seem somewhat unnecessary to send these custom events when we already have autocapture setup, but these custom events are important for two main reasons:

  1. Sending custom properties on an event
  2. Keeping events consistent over time

Sending custom properties on an event

Suppose you're building an Saas app and want to track when a user purchases a plan. With autocapture, you'll already see Clicked button with text 'Purchase' events, but these won't have any information on which plan the user purchased!

We include extra information on events by adding a second parameter in our capture call, which contains a map of custom property names and values.

posthog.capture('Plan purchased', {
price: 1599,
planId: 'XYZ12345',
frequency: 'monthly',
features: {
'SSO': true,
'Custom branding': true,
'Custom domains': false,

Later, we can use these properties to filter events based on a particular planId, or to calculate the aggregate values of a property over time.

Tip: Always include more properties than you might need at first! There's no limit to the number of properties an event can have, and your future self will thank you once again when you need to access a property that you may not have thought you needed.

Keeping events consistent and reliable

A second reason why Custom events become important is for keeping tracking consistent and reliable. When elements of your frontend change, this can affect how autocapture events show up in PostHog. Continuing with the example above, if we changed the text for our add to cart button to just 'Add', our autocapure events would begin showing up as Clicked button with text 'Add' instead of what they had previously been.

While combining these two events using Actions to fix this drift is possible, manually track these high-value actions with custom events is far more reliable.

3. Capture backend events

We've now covered sending events using autocapture and custom tracking, but up until now we've only been sending events from our frontend website. While for simple websites this is all you'll need to set up, if you're building a web or mobile app with a backend, we also highly recommend setting up tracking from your server in addition to your frontend.

There are several reasons why it's often preferable to send certain custom events from the server-side:

  1. More reliable delivery: As these events originate from your server, there's no way for them to get accidentally blocked by client-side ad-blockers
  2. More reliable data: You can fetch up-to-date information directly from your database or other services, which may not be readily available on the frontend

Our SDK pages contain information on installing PostHog on your specific platform. Once you have the library installed, you can send a capture event using the same capture method as in posthog-js.

distinctId: 'distinct_id',
event: 'Order created',
properties: {
orderId: '#0054'
subtotal: 3599,
customerName: 'Max Hedgehog',

The only major difference on the server side is that we must include a distinct_id with every event. Often, this will come in the form of a unique user ID and can be pulled from a session cookie when requests arrive from the client.

When should I send events from the server vs. the client?

In general, our guidance is to track events on both the frontend and backend whenever possible, to ensure maximum reliability and the most flexibility when analyzing your data.

That being said, there are certain events that we highly recommend tracking from the server-side specifically:

  1. Sign-up events: Given how high value these events are, you should also send a server-side even whenever possible
  2. C(R)UD events: This is a broad class of events, but generally speaking, whenever you receive an API request to create or update a specific resource within your application, it's useful to forward these to PostHog. We also recommend including context about the request itself in the event (latency, errors, any properties passed in the request payload, etc.)
  3. Backend jobs: PostHog is for more than just optimizing your frontend! It can often be useful to send events whenever backend jobs or workflows are kicked off, which allows you to analyze them within PostHog.

Tip: We recommend using different event names for your backend and frontend events to avoid the chance of duplicate counting. Often these should be the CRUD events themselves e.g. User created for the backend and User signed up for the frontend. Additionally, you can use the source property to filter for events from a specific source.

Event ingestion nuances

It's a priority for us that events are fully processed and saved as soon as possible. However, there is a class of events which we deliberately process with a slight delay. Specifically, an event is delayed by around a minute if it fits all of the following three conditions:

  • isn't from an anonymous user (anonymous users are recognized by having the distinct_id the same as the $device_id property)
  • isn't an $identify event (e.g. from posthog.identify())
  • its distinct_id cannot be matched to an existing person

This delay mechanism is called the event buffer, and it materially improves handling of an edge case which could otherwise inflate unique user counts.

How does the event buffer help?

Starting with version 1.38.0, PostHog stores the person associated with an event inline with the event record. This greatly improves query performance, but because events are immutable, it also means that persons can't be merged retroactively. See this scenario where that's problematic:

  1. User visits signup page, in turn frontend captures anonymous $pageview for distinct ID XYZ (anonymous distinct ID = device ID).
    This event gets person ID A.
  2. User click signup button, initiating in a backend request, in turn frontend captures anonymous $autocapture (click) for distinct ID XYZ.
    This event gets person ID A.
  3. Signup request is processed in the backend, in turn backend captures identified signup for distinct ID
    OOPS! We haven't seen before, so this event gets person ID B.
  4. Signup request finishes successfully, in turn frontend captures identified $identify aliasing distinct ID XYZ to
    This event gets person ID A.

Here, the event from step 3 got a new person ID B, impacting unique users counts. If it were delayed just a bit and processed after the event from step 4, all events would get the expected person ID A. This is exactly what the event buffer achieves.


Was this page useful?

Next article

Identify users

PostHog allows you to identify your users with an ID of your choice. This enables PostHog to associate events with a specific user, track them on different platforms, and connect events from before and after they log in for the first time. All events within PostHog are associated with a specific person, either an Anonymous person or an Identified person , typically based on whether they're logged in to your application or not. Identifying users is done using the identify method in one of…

Read next article