POST-only public endpoints

Last updated:

|Edit this page

Update: These endpoints can now be accessed with either your Team API key or your personal API key.

As explained in our API overview page, PostHog provides two different APIs.

This page refers to our public endpoints, which use the same API key as the PostHog snippet. The endpoints documented here are used solely with POST requests, and will not return any sensitive data from your PostHog instance.

Note: For this API, you should use your 'Project API Key' from the 'Project' page in PostHog. This is the same key used in your frontend snippet.

Sending events

To send events to PostHog, you can use any of our libraries or any Mixpanel library by changing the api_host setting to the address of your instance.

If you'd prefer to do the requests yourself, you can send events in the following format:

Single event

Note: Timestamp is optional. If not set, it'll automatically be set to the current time.

Terminal
POST https://[your-instance].com/capture/
Content-Type: application/json
Body:
{
"api_key": "<ph_project_api_key>",
"event": "[event name]",
"distinct_id": "[your users' distinct id]",
"properties": {
"key1": "value1",
"key2": "value2"
},
"timestamp": "[optional timestamp in ISO 8601 format]"
}

Batch events

You can send multiple events in one go with the Batch API.

There is no limit on the number of events you can send in a batch, but the entire request body must be less than 20MB by default (see API overview).

Note: Timestamp is optional. If not set, it'll automatically be set to the current time.

Terminal
POST https://[your-instance].com/batch/
Content-Type: application/json
Body:
{
"api_key": "<ph_project_api_key>",
"batch": [
{
"event": "[event name]",
"properties": {
"distinct_id": "[your users' distinct id]",
"key1": "value1",
"key2": "value2"
},
"timestamp": "[optional timestamp in ISO 8601 format]"
},
...
]
}

Alias

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"distinct_id": "123",
"properties": {
"alias": "456"
},
"timestamp": "2020-08-16 09:03:11.913767",
"event": "$create_alias"
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Capture

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"properties": {},
"timestamp": "2020-08-16 09:03:11.913767",
"distinct_id": "1234",
"event": "$event"
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Groups

Note: company is a group type. You can set it to the value you want such as organization, project, or channel.

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"event": "$event",
"distinct_id": "1234",
"properties": {
"$groups": {"company": "<company_name>"}
}
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Group identify

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"event": "$groupidentify",
"distinct_id": "groups_setup_id",
"properties": {
"$group_type": "<group_type>",
"$group_key": "<company_name>",
"$group_set": {
"name": "<company_name>",
"subscription": "premium"
"date_joined": "2020-01-23T00:00:00.000Z"
}
}
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Identify

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"timestamp": "2020-08-16 09:03:11.913767",
"context": {},
"distinct_id": "1234",
"$set": {},
"event": "$identify"
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Page view

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"properties": {},
"timestamp": "2020-08-16T09:03:11.913767",
"distinct_id": "1234",
"event": "$pageview"
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Screen view

Terminal
curl -v -L --header "Content-Type: application/json" -d '{
"api_key": "<ph_project_api_key>",
"properties": {},
"timestamp": "2020-08-16T09:03:11.913767",
"distinct_id": "1234",
"event": "$screen"
}' https://app.posthog.com/capture/ # if you're self-hosting, replace this with the URL of your instance

Feature flags

PostHog's feature flags enable you to safely deploy and roll back new features.

There are 3 steps to implement feature flags using the PostHog API:

Step 1: Evaluate the feature flag value using the /decide

/decide is the endpoint used to determine if a given flag is enabled for a certain user or not.

Request

Terminal
curl -v -L --header "Content-Type: application/json" -d ' {
"api_key": "<ph_project_api_key>",
"distinct_id": "distinct_id_of_your_user",
"groups" : { # Required only for group-based feature flags
"group_type": "group_id" # Replace "group_type" with the name of your group type. Replace "group_id" with the id of your group.
},
"person_properties": {"<personProp1>": "<personVal1>"}, # Optional. Include any properties used to calculate the value of the feature flag.
"group_properties": {"group type": {"<groupProp1>":"<groupVal1>"}} # Optional. Include any properties used to calculate the value of the feature flag.
}' https://app.posthog.com/decide?v=3 # or https://eu.posthog.com/decide?v=3

Note: person_properties and group_properties are optional.

By default, flag evaluation uses the user and group properties stored in PostHog. You only need to provide person_properties and group_properties if you wish to use different values than the ones in PostHog.

Response

Terminal
{
"config": {
"enable_collect_everything": true
},
"editorParams": {},
"errorComputingFlags": false,
"isAuthenticated": false,
"supportedCompression": [
"gzip",
"lz64"
],
"featureFlags": {
"my-awesome-flag": true,
"my-awesome-flag-2": true,
"my-multivariate-flag": "some-string-value",
"flag-thats-not-on": false,
},
"featureFlagPayloads": {
"my-awesome-flag": "example-payload-string",
"my-awesome-flag-2": {"color": "blue", "animal": "hedgehog"}
}
}

Note: errorComputingFlags will return true if we didn't manage to compute some flags (for example, if there's an ongoing incident involving flag evaluation).

This enables partial updates to currently active flags in your clients.

Step 2: Include feature flag information when capturing events

If you want use your feature flag to breakdown or filter events in your insights, you'll need to include feature flag information in those events.

This ensures that the feature flag value is attributed correctly to the event.

Note: this step is only required for events captured using our server-side SDKs or API.

To do this, include the $feature/feature_flag_name property in your event:

Terminal
curl -v -L --header "Content-Type: application/json" -d ' {
"api_key": "<ph_project_api_key>",
"distinct_id": "distinct_id_of_your_user",
"properties": {
"$feature/feature-flag-key": "variant-key", # replace feature-flag-key with your flag key. Replace 'variant-key' with the key of your variant
},
"event": "your_event_name"
}' https://app.posthog.com/capture/ # or https://eu.posthog.com/capture/

Step 3: Send a $feature_flag_called event

To track usage of your feature flag and view related analytics in PostHog, submit the $feature_flag_called event whenever you check a feature flag value in your code.

You need to include two properties with this event:

  1. $feature_flag_response: This is the name of the variant the user has been assigned to e.g., "control" or "test"
  2. $feature_flag: This is the key of the feature flag in your experiment.
Terminal
curl -v -L --header "Content-Type: application/json" -d ' {
"api_key": "<ph_project_api_key>",
"distinct_id": "distinct_id_of_your_user",
"properties": {
"$feature_flag": "feature-flag-key",
"$feature_flag_response": "variant-name"
},
"event": "$feature_flag_called"
}' https://app.posthog.com/capture/ # or https://eu.posthog.com/capture/

Advanced: Overriding server properties

Sometimes, you may want to evaluate feature flags using person properties, groups, or group properties that haven't been ingested yet, or were set incorrectly earlier.

You can provide properties to evaluate the flag with by using the person properties, groups, and group properties arguments. PostHog will then use these values to evaluate the flag, instead of any properties currently stored on your PostHog server.

For example:

Terminal
curl -v -L --header "Content-Type: application/json" -d ' {
"api_key": "<ph_project_api_key>",
"distinct_id": "distinct_id_of_your_user",
"groups" : { # Required only for group-based feature flags
"group_type": "group_id" # Replace "group_type" with the name of your group type. Replace "group_id" with the id of your group.
},
"person_properties": {"<personProp1>": "<personVal1>"},
"group_properties": {"group_type": {"property_name":"property_value"}} #
}' https://app.posthog.com/decide?v=3 # or https://eu.posthog.com/decide?v=3

Responses

Status code: 200

Responses

JavaScript
{
status: 1
}

Meaning: A 200: OK response means we have successfully received the payload, it is in the correct format, and the project API key (token) is valid. It does not imply that events are valid and will be ingested. As mentioned under Invalid events, certain event validation errors may cause an event not to be ingested.

Status code: 400

Responses

JavaScript
{
type: 'validation_error',
code: 'invalid_project',
detail: 'Invalid Project ID.',
attr: 'project_id'
}

Meaning: We were unable to determine the project to associate the events with.

Status code: 401

Responses

JavaScript
{
type: 'authentication_error',
code: 'invalid_api_key',
detail: 'Project API key invalid. You can find your project API key in PostHog project settings.',
}

Meaning: The token/API key you provided is invalid.


JavaScript
{
type: 'authentication_error',
code: 'invalid_personal_api_key',
detail: 'Invalid Personal API key.',
}

Meaning: The personal API key you used for authentication is invalid.

Status code: 503 (Deprecated)

Responses

JavaScript
{
type: 'server_error',
code: 'fetch_team_fail',
detail: 'Unable to fetch team from database.'
}

Meaning: (Deprecated) This error will only occur in self-hosted Postgres instances if the database becomes unavailable. On ClickHouse-backed instances database failures cause events to be added to a dead letter queue, from which they can be recovered.

Invalid events

We perform basic validation on the payload and project API key (token), returning a failure response if an error is encountered.

However, we will not return an error to the client when the following happens:

  • An event does not have a name
  • An event does not have the distinct_id field set
  • The distinct_id field of an event has an empty value

The three cases above will cause the event to not be ingested, but you will still receive a 200: OK response from us.

This approach allows us to process events asynchronously if necessary, ensuring reliability and low latency for our event ingestion endpoints.

Reading data from PostHog

We have another set of APIs to read/modify anything in PostHog. See our API documentation for more information.

Also, feel free to reach out if you'd like help with the API.

Questions?

Was this page useful?