Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions contents/docs/data/event-filtering.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
title: Event ingestion filtering
sidebar: Docs
showTitle: true
availability:
free: none
selfServe: none
enterprise: full
---

Event ingestion filtering lets you drop events at ingestion time based on event metadata. Filters are evaluated early in the [ingestion pipeline](/docs/how-posthog-works/ingestion-pipeline), before transformations run, making it the most efficient way to exclude unwanted events from your data.

Common use cases include:

- Dropping events from automated bots or internal tools
- Filtering out test or staging traffic by `distinct_id`
- Removing high-volume, low-value events to reduce costs
- Temporarily filtering buggy data while you roll out a client-side fix

You can find it at [**Data management** > **Event filtering**](https://app.posthog.com/data-management/event-filtering).

## How it works

Each incoming event is evaluated against a set of conditions you define. If an event matches the filter, PostHog drops it before ingestion. If it doesn't match, it continues through the pipeline as normal.

Filters run **before** [transformations](/docs/cdp/transformations) and [destinations](/docs/cdp/destinations), so dropped events don't trigger any downstream processing.

If your filtering logic can be expressed here, we recommend using event ingestion filtering over a [drop events transformation](/docs/cdp/transformations/drop-events). It runs earlier in the pipeline and doesn't require writing custom code.

## Operating modes

The filter has three modes:

| Mode | Behavior |
|------|----------|
| **Disabled** | No filtering is applied. All events are ingested normally. |
| **Dry run** | Matching events are counted but **not** dropped. Use this to validate your filter before going live. |
| **Live** | Matching events are dropped from ingestion. |

We recommend starting with **dry run** mode and checking the metrics to confirm your filter is matching the right events before switching to **live**.

## Creating a filter

Filters are built using a visual expression builder that supports nested boolean logic (AND, OR, NOT).

Each condition has three parts:

1. **Field** – what to match on:
- `event_name` – the name of the event (e.g., `$pageview`, `signup`)
- `distinct_id` – the [distinct ID](/docs/data/persons) attached to the event

2. **Operator** – how to match:
- `equals` – the field value matches the specified value exactly
- `contains` – the field value contains the specified string

3. **Value** – the string to match against

### Combining conditions

- **AND groups** match when **all** conditions in the group are true.
- **OR groups** match when **any** condition in the group is true.
- **NOT** negates a group, matching when the group's conditions are **not** true.

You can nest groups up to five levels deep and add up to 20 conditions total. Conditions and groups can be reordered using drag and drop.

**Example:** To drop all `$pageview` events from a bot with distinct ID containing `bot-crawler`:

```
AND
├── event_name equals "$pageview"
└── distinct_id contains "bot-crawler"
```

**Example:** To drop events from multiple internal tools:

```
OR
├── distinct_id contains "internal-tool-a"
└── distinct_id contains "internal-tool-b"
```

## Test cases

Events dropped by a filter can't be recovered, so verify your filter works correctly before turning it on. Test cases help you do this – they validate the filter against example events and serve as living documentation of what the filter is intended to match.

Each test case specifies:

- An **event name**
- A **distinct ID**
- The **expected result** – either "Drop" or "Ingest"

Test cases run locally in your browser and show a pass/fail result. The filter can't be enabled until all tests pass – if you try to save in **live** mode with failing tests, it automatically downgrades to **dry run**.

## Metrics

The event filtering page shows approximate counts of:

- **Dropped** – events the filter dropped in live mode
- **Would be dropped** – events the filter matched in dry run mode (not actually dropped)

Use these metrics to validate your filter is matching the right volume and type of events before going live.

> **Note:** Metrics are processed on a best-effort basis and may be off by a small percentage.

## Limits

- Maximum **20 conditions** per filter
- Maximum **five levels** of nesting
- Only `event_name` and `distinct_id` fields are supported
- Only `equals` and `contains` operators are supported
- One filter configuration per project

## Further reading

- [How the ingestion pipeline works](/docs/how-posthog-works/ingestion-pipeline)
- [Ingestion warnings](/docs/data/ingestion-warnings)
- [Transformations](/docs/cdp/transformations)
4 changes: 4 additions & 0 deletions src/navs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2616,6 +2616,10 @@ export const docsMenu = {
name: 'Data import and export',
url: '/docs/getting-started/data-import-export',
},
{
name: 'Event ingestion filtering',
url: '/docs/data/event-filtering',
},
{
name: 'Ingestion warnings',
url: '/docs/data/ingestion-warnings',
Expand Down
Loading