Materialization
Contents
Materialization pre-computes your query results and stores them in S3. When someone calls your endpoint, PostHog returns the materialized results instead of running the query again - making responses much faster.
When to use materialization
Materialization is ideal when:
- Your data doesn't need to be real-time - Results are only as fresh as the last sync
- Your query is expensive - Complex queries with large datasets benefit the most
- You have high traffic - Avoid repeatedly running the same query
If you need real-time data or frequently update your query, stick with direct execution or use caching instead.
How to enable materialization
- Go to your endpoint's Configuration tab
- Enable the Materialization toggle
- Choose a sync frequency:
- Every hour - Updates every hour
- Every 6 hours - Updates four times a day
- Daily - Updates once per day
- Weekly - Updates once per week
PostHog will run the first materialization immediately. After that, it syncs on your chosen schedule.
Materialization status
On the endpoint page, you'll see one of these statuses:
- Completed - Latest materialization succeeded, results are available
- Running - Materialization is in progress
- Failed - Materialization encountered an error
If materialization fails, the endpoint falls back to direct execution until the next successful sync.
Limitations
Materialization is not available when:
- The endpoint has incompatible variables - Only certain variable types are supported. See the compatibility table below.
- The endpoint is inactive - Only active endpoints and their versions are materialized.
Variable compatibility
Not all variables work with materialization. Compatibility depends on the operator and where the variable is used in the query.
| Variable type | Supported | Notes |
|---|---|---|
Equality (=) on columns | ✅ Yes | Standard column filters work out of the box |
Range (>=, >, <, <=) on timestamp columns | ✅ Yes | Uses timestamp bucketing to pre-aggregate data |
Variables in HAVING clauses | ❌ No | Cannot be materialized |
Variables in OR conditions | ❌ No | Cannot be materialized |
Timestamp bucketing
When your query uses timestamp range variables (e.g., timestamp >= {variables.start_date} AND timestamp < {variables.end_date}), PostHog uses bucketed materialization.
Instead of materializing a single result set, PostHog pre-aggregates your data into time buckets. At read time, it re-aggregates only the buckets that fall within your actual variable values.
Supported bucket granularities:
hourday(default)weekmonth
By default, PostHog uses toStartOfDay to bucket your data. This means each row in the materialized result represents one day of pre-aggregated data.
Configuring bucket size
You can customize the bucket granularity for each timestamp column directly in the Configuration tab. When materialization is enabled and your query has timestamp range variables, a dropdown appears for each timestamp column, letting you select a bucket size:
- Hour
- Day
- Week
- Month
Smaller buckets give more precise results, while larger buckets produce less granular results.
Bucketed materialization only works with aggregate functions that can be re-aggregated across buckets: count, sum, min, max, and their combinator variants like countIf and sumIf. Functions like avg, uniq, and countDistinct cannot be re-aggregated and are not supported.
How materialization works with execution
When you execute an endpoint:
- If materialization is enabled and complete, PostHog returns the materialized results
- If materialization is running or failed, PostHog executes the query directly
See Execution for more details on how PostHog chooses between materialized and direct execution.