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.
POST https://[your-instance].com/capture/Content-Type: application/jsonBody:{"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.
POST https://[your-instance].com/batch/Content-Type: application/jsonBody:{"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
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
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 asorganization
,project
, orchannel
.
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
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
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
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
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
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
andgroup_properties
are optional.By default, flag evaluation uses the user and group properties stored in PostHog. You only need to provide
person_properties
andgroup_properties
if you wish to use different values than the ones in PostHog.
Response
{"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 returntrue
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:
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:
$feature_flag_response
: This is the name of the variant the user has been assigned to e.g., "control" or "test"$feature_flag
: This is the key of the feature flag in your experiment.
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:
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
{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
{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
{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.
{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
{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.