Generated banner for Tools And Features Hogql
Generated logo for Tools And Features Hogql

Tools And Features Hogql

PostHogOfficialCreated by PostHog

  • Skill
  • Tool integration
  • Data and AI
  • MIT
  • No issues found

From the author

HogQL queries for PostHog analytics
— the description by PostHog

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

HogQL is PostHog's SQL dialect, designed as a wrapper around ClickHouse SQL to simplify data access and analytics within the PostHog platform. This skill provides the essential reference material for constructing queries, including how to access event and person properties, utilize PostHog-specific functions, and integrate UI-based filters into your SQL insights. It emphasizes performance and efficiency, offering guidelines on aggregation, time-range filtering, and query structure to help users build effective analytics dashboards.

What it does

  • Provides syntax patterns for querying events, persons, and sessions.
  • Explains how to access event and person properties using specific dot notation.
  • Details the use of the {filters} placeholder for dynamic dashboard filtering.
  • Lists supported ClickHouse functions and aggregation methods for data analysis.
  • Offers performance optimization tips like using uniq() over count(distinct).
  • Provides guidance on using cohort and action-based filtering in SQL.

When to use it

  • When you need to create custom SQL insights in PostHog.
  • When you want to perform complex data analysis beyond standard UI-based reports.
  • When you need to optimize existing queries for better performance.
  • When you are building dashboards that require dynamic date ranges or user-defined filters.

Advantages

  • Simplifies complex ClickHouse SQL for common analytics tasks.
  • Enables seamless integration with PostHog's UI-based filtering system.
  • Provides access to specialized PostHog functions like windowFunnel and sequenceMatch.
  • Promotes efficient query patterns to reduce load on the underlying database.

Usage examples

“Write a HogQL query to count pageviews by browser.”
SELECT properties.$browser AS browser, count() FROM events WHERE event = '$pageview' AND {filters} GROUP BY browser
“How do I count unique users in a HogQL query?”
Use the uniq() function instead of count(distinct) for better performance, for example: SELECT uniq(person_id) FROM events WHERE {filters}

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/posthog/tools-and-features-hogql/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip tools-and-features-hogql.zip -d .agents/skills

Documentation by PostHog

HogQL queries for PostHog

This skill helps you write HogQL queries for PostHog analytics. HogQL is PostHog's SQL dialect, a wrapper around ClickHouse SQL with simplified property access and PostHog-specific functions.

Reference files

  • references/expressions.md - Sql expressions - docs

  • references/aggregations.md - Supported aggregations - docs

  • references/clickhouse-functions.md - Supported clickhouse functions - docs

  • references/data-access.md - Accessing data using sql - docs

  • references/variables.md - Sql variables - docs

  • references/useful-functions.md - Useful sql functions - docs

  • references/posthog.md - PostHog table schemas (events, persons, groups, sessions)

  • references/sessions.md - Sessions - docs

Consult the documentation for SQL syntax, available functions, and query patterns.

Key principles

  • Property access: Use properties.$property_name for event properties and person.properties.$property_name for person properties

  • Null handling: HogQL has simplified null handling compared to raw ClickHouse SQL

  • Filters placeholder: Use {filters} in queries to allow UI-based filtering in PostHog dashboards

  • Aggregations: Prefer ClickHouse aggregation functions like count(), uniq(), avg(), sum()

Common patterns

Event queries
SELECT event, count()
FROM events
WHERE {filters}
GROUP BY event
ORDER BY count() DESC
Property breakdowns
SELECT properties.$browser AS browser, count()
FROM events
WHERE event = '$pageview' AND {filters}
GROUP BY browser
Person properties
SELECT person.properties.email, count()
FROM events
WHERE {filters}
GROUP BY person.properties.email

Framework guidelines

  • Use properties.$name syntax for event properties, person.properties.$name for person properties

  • Use bracket notation for special characters like properties['$feature/cool-flag']

  • For cohorts, filter with person_id IN COHORT 'cohort-name'

  • For actions, use matchesAction('action-name') in WHERE clauses

  • Include {filters} placeholder in WHERE clauses to enable UI-based filtering in dashboards

  • Use {variables.name} for reusable SQL variables across dashboards

  • Access dashboard date range with {filters.dateRange.from} and {filters.dateRange.to}

  • ALWAYS include a time range filter - shorter is faster (e.g., timestamp >= now() - INTERVAL 7 DAY)

  • Prefer uniq() over count(distinct) for counting unique values - it's more efficient

  • Don't scan the same table multiple times - use materialized views for reusable subsets

  • Use timestamp-based pagination instead of OFFSET for large datasets

  • Name queries descriptively for easier debugging in query_log

  • Use dateTrunc() for time-based grouping (e.g., dateTrunc('day', timestamp))

  • For funnel queries, use windowFunnel() or sequenceMatch() functions

  • Test queries in the PostHog SQL editor before using them in insights or the API

Files

references/expressions.md

SQL expressions - Docs

SQL expressions enable you to directly access, modify, and aggregate data in many places in PostHog including:

Image: SQL trends breakdown filterImage: SQL trends breakdown filter

Tip: If you're having trouble getting results from your expression, try debugging by using a different visualization (trends table often works best as it shows all values returned) or breaking down your expression into pieces and testing each one.

Accessible data

SQL expressions can access data like:

  • event properties (properties)

  • person properties (person.properties)

  • event

  • elements_chain (from autocapture)

  • timestamp

  • distinct_id

  • person_id

  • When joined, data warehouse source properties

Properties can be accessed with dot notation like person.properties.$initial_browser which also works for nested or JSON properties. They can also be accessed with bracket notation like properties['$feature/cool-flag'].

Note: PostHog's properties always include $ as a prefix, while custom properties do not (unless you add it).

Property identifiers must be known at query time. For dynamic access, use the JSON manipulation functions from below on the properties field directly.

Types

Types (and names) for the accessible data can be found in the database and properties tabs in data management. They include:

  • STRING (default)

  • JSON (accessible with dot or bracket notation)

  • DATETIME(in ISO 8601, read more in our data docs)

  • INTEGER

  • NUMERIC(AKA float)

  • BOOLEAN

Types can be converted using functions like toString, toDate, toFloat, JSONExtractString, JSONExtractInt, and more.

Operators

Expressions can use operators to filter and combine data. These include:

  • Comparison operators like =, !=, <, or >=

  • Logical operators like AND, OR, IS or NOT

  • Arithmetic operators like +, -, *, /

Functions and aggregations

You can filter, modify, or aggregate accessed data with supported ClickHouse functions like dateDiff() and concat() and aggregations like sumIf() and count().

Here are some of the most common and useful ones:

Comparisons
FunctionDefinition
if(cond, then, else)Checks a condition, and if true (or non-zero), returns the result of an expression
multiIf(cond1, then1, cond2, then2, ..., else)Enables chaining multiple if statements together, each with a condition and return expression
in(value, set)Checks if an array or string contains a value
match(value, regexp)Checks whether a string matches a regular expression pattern
likeChecks if a string matches a pattern that contains string(s) and symbols %, _, \ (escaped literals)
Aggregations
AggregationDefinition
countCounts the values. If you want a condition, use sumIf
count(distinct)Counts the number of uniqExact values
uniqCalculates the approximate number of different values (uniqExact is slower but exact).
uniqExactCalculates the exact number of different argument values (uniq is faster and you should use it if a close approximation is good enough).
sumCalculates the total (sum) numeric value
sumIf(column, cond)Calculates the total (sum) numeric value for values (column) meeting a condition (cond)
avgCalculates the average numeric value
medianComputes an approximate middle (50%) value for a numeric data sequence.
Strings
FunctionDefinition
extract(haystack, pattern)Extracts a fragment of a string (haystack) using a regular expression (pattern) like extract(properties.$current_url, 'ref=([^&]*)')
concat(s1, s2, ...)Concatenates strings (s1, s2, etc.) listed without separator
splitByChar(separator, s)Splits string (s) into substrings separated by a specified character (separator)
replaceOne(haystack, pattern, replacement)Replace the first occurrence of matching a substring (pattern) with a replacement string (replacement). Example: replaceOne(properties.$current_url, 'https://us.posthog.com', '/')
replaceRegexpOne(haystack, pattern, replacement)Replace the first occurrence of matching a regular expression (pattern) with a replacement string (replacement)
substring(s, start)Extracts a substring from a string (s) starting at index (start)
Dates
FunctionDefinition
dateDiff('unit', startdate, enddate)Returns the count in unit between startdate and enddate
toDayOfWeek, toHour, toMinuteConverts date number of day of week (1-7), hour in 24-hour time (0-23), and minute in hour (0-59) like toHour(timestamp)
now(), today(), yesterday()Returns the current time, date, or yesterday's date respectively
intervalA length of time for use in arithmetic operations with other dates and times like person.properties.trial_started + interval 30 day

Use cases

  • Checking if a property or autocapture element chain contains a specific value or any of an array of values using in or match.

  • Modifying the display string in the visualization by extracting or concatenating properties using concat(), +, extract(), or replaceOne like concat('OS Version: ', properties.$os_version).

  • Grouping or binning events based on properties using if(), multiIf() like multiIf(properties.$device_type == 'Desktop', 'Desktop', properties.$os == 'iOS', 'iOS', 'Non-iOS').

  • Accessing nested properties such as properties.$set.$geoip_city_name.

  • Filtering for events that happened in the last X minutes, hours, or days with dateDiff(), now(), and interval like dateDiff('minute', timestamp, now()) < 30.

  • Creating percentages by calculating the sum of one property over the sum of all related properties inline with sum(), /, +, and * like sumIf(1, properties.$browser = 'Chrome') / sumIf(1, properties.$browser = 'Safari' or properties.$browser = 'Chrome')

  • Binning events based on time of day, week, and month with toHour, toDayOfWeek, toStartOfWeek, toMonth like multiIf(5 >= toHour(timestamp) and toHour(timestamp) < 12, 'morning', 12 >= toHour(timestamp) and toHour(timestamp) < 17, 'afternoon', 'night')

  • Breaking down by multiple properties using concat() like concat(properties.$os_name, ' - ', properties.$os_version).

  • Matching URL patterns with like like (properties.$current_url LIKE '%/blog%')

  • Filter null property values with IS NOT NULL like person.properties.$initial_utm_source IS NOT NULL.

  • Breakdown by values in an array by using a combination of JSONExtractArrayRaw and arrayJoin like arrayJoin(JSONExtractArrayRaw(properties.$active_feature_flags ?? '[]'), ',').

  • Extracting the ID from autocaptured elements like extract(elements_chain, '[:|"]attr__id="(.*?)"').

Community questions

Ask a question

Was this page useful?

HelpfulCould be better

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
PostHog
Publisher
PostHog (Official)
Source
PostHog/skills · skills/posthog/tools-and-features/skills/hogql · commit a5d30e7
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
PostHog/skills

AutoGineer is not affiliated with or endorsed by PostHog or PostHog.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tools-and-features-hogql.zip