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/useful-functions.md

Useful SQL functions - Docs

Our SQL flavor includes many functions to aggregate and manipulate queried data. Below are some examples of some of the most popular SQL functions you can use in your insights.

Aggregate functions

These aggregate results for columns across all rows. They include:

  • avg(): Calculates the average numeric value of a column.

  • sum(): Calculates the total (sum) numeric value of a column.

  • max(), min(): Finds the maximum or minimum value of a column.

SQL

Run in PostHog

PostHog AI

SELECT
   avg(properties.$screen_height),
   sum(properties.$screen_height),
   max(properties.$screen_height),
   min(properties.$screen_height)
FROM events
WHERE event = '$pageview' AND properties.$screen_height IS NOT NULL

You can find a full list of these in supported aggregations.

Count

Use count() to count the number of rows in a particular column. count(*) counts all rows, while count(column_name) counts the number of non-null values in a column.

Regular functions

Our SQL flavor provides many functions for accessing, modifying, and calculating data from queries. Along with the ones listed below, many basics include calculation operators (+, -, /, *), type conversions (toInt, toString), conditional statements (if, multiIf), and rounding (floor, round).

You can find a full list of these in supported ClickHouse functions.

Date and time
  • now(), today(), yesterday(): Returns the current time, date, or yesterday’s date respectively.

  • interval: A length of time for use in arithmetic operations with other dates and times.

  • toDayOfWeek, toHour, toMinute: Converts date number of day of week (1-7), hour in 24-hour time (0-23), and minute in hour (0-59).

  • toStartOfYear, toStartOfMonth, toMonday, toStartOfDay, toStartOfMinute: rounds date down to the nearest year, month, Monday, day, hour, or minute respectively

  • dateDiff('unit', startdate, enddate): Returns the count in unit between startdate and enddate.

  • formatDateTime: Formats a time according to a MySQL datetime format string.

  • parseDateTimeBestEffort: Converts most human-readable dates into a datetime.

SQL

Run in PostHog

PostHog AI

SELECT
   formatDateTime(now(), '%a %b %T') AS current_time,
   toDayOfWeek(now()) AS current_day_of_week,
   dateDiff('day', timestamp, now()) AS days_since_event,
   parseDateTimeBestEffort('4-Dec-2023') AS converted_date
FROM events
WHERE timestamp > now() - interval 1 day

If you want to access dashboard or insight date filters, you can use the variables filters.dateRange.from and filters.dateRange.to.

Read more examples in How to do time-based breakdowns (hour, minute, real time) and Using SQL for advanced time and date filters.

String
  • extract: Extracts a fragment of a string using a regular expression.

  • concat: Concatenates strings listed without separator.

  • splitByChar, splitByString, splitByRegexp, splitByWhitespace: splits a string into substring separated by a specified character, string, regular expression, or whitespace character respectively.

  • match: Return whether the string matches a regular expression pattern.

  • replaceOne, replaceRegexpOne: Replace the first occurrence of matching a substring or regular expression pattern respectively with a replacement string.

  • trim: Remove specified characters (or whitespace) from the start or end of a string.

  • upper, lower: Converts a string to uppercase or lowercase.

SQL

Run in PostHog

PostHog AI

select
   extract(elements_chain, '[:|"]attr__class="(.*?)"') as class_name,
    concat(properties.$os, ' version: ', properties.$os_version),
    replaceRegexpOne(properties.$current_url, '^/', 'site/') AS modified_current_url
from events
where event = '$autocapture'

Read more in How to analyze autocapture events with SQL.

JSON

You can access nested data in JSON and objects directly.

SQL

Run in PostHog

PostHog AI

select properties.$set.$geoip_country_name
from events

You can parse JSON with JSONExtractRaw() to return a value.

SQL

Run in PostHog

PostHog AI

SELECT
  JSONExtractRaw(properties.$set) as set_properties
FROM events
WHERE properties.$set IS NOT NULL

Specialized JSONExtract functions exist for different data types including:

  • JSONExtractFloat

  • JSONExtractArrayRaw

  • JSONExtractString

  • JSONExtractBool

Array
  • arrayElement(arr, n): Retrieves the element with the index of n from the array arr.

  • arrayJoin(arr): Takes a row and generates multiple rows for the number of elements in the array. It copies all the column values, except the column where this function is applied. It replaces the applied column with the corresponding array value.

SQL

Run in PostHog

PostHog AI

SELECT flag, count()
FROM (
   SELECT arrayJoin(JSONExtractArrayRaw(assumeNotNull(properties.$active_feature_flags))) as flag
   FROM events
   WHERE event = '$pageview' and timestamp > '2023-08-01'
)
GROUP BY flag
ORDER BY count() desc

Read more in How to filter and breakdown arrays with SQL.

Sparkline

A sparkline is a tiny graph contained in one cell of your query result. As an argument, it takes an array of integers.

SQL

Run in PostHog

PostHog AI

SELECT sparkline(range(1, 10)) FROM (SELECT 1)

You can use it to visualize queries, such as a 24-hour $pageview count for different $current_url values.

SQL

Run in PostHog

PostHog AI

SELECT
    pageview,
    sparkline(arrayMap(h -> countEqual(groupArray(hour), h), range(0,23))),
    count() as pageview_count
FROM
(
    SELECT
        properties.$current_url as pageview,
        toHour(timestamp) AS hour
    FROM
        events
    WHERE
         timestamp > now () - interval 1 day
         and event = '$pageview'
) subquery
GROUP BY
    pageview
ORDER BY
    pageview_count desc

You can also use it for art.

SQL

Run in PostHog

PostHog AI

select
    sparkline(arrayMap(a -> cos(toSecond(timestamp) + a/4), range(100 + 5 * toSecond(timestamp))))
from events
SemVer

If you have a SemVer version number, you can use the sortableSemVer() function to get a version number that you can use for sorting purposes.

SELECT DISTINCT properties.$lib_version FROM events WHERE event = '$pageview' AND timestamp >= now() - INTERVAL 1 DAY ORDER BY sortableSemVer(properties.$lib_version) DESC LIMIT 10

Session replays

You can create a button to view the replay for a session by using the recordingButton() function with the session_id. For example, to get a list of recent replays, you can use:

SQL

Run in PostHog

PostHog AI

SELECT
    person.properties.email,
    min_first_timestamp AS start,
    recordingButton(session_id)
FROM
    raw_session_replay_events
WHERE
    min_first_timestamp >= now() - INTERVAL 1 DAY
    AND min_first_timestamp <= now()
ORDER BY
    min_first_timestamp DESC
LIMIT 10
Actions

You can use actions in SQL by using the matchesAction() function with the name of your action. For example, to get a count of the action clicked homepage button, you can do:

SQL

Run in PostHog

PostHog AI

SELECT count()
FROM events
WHERE matchesAction('clicked homepage button')
Translation

You can translate a language code to a language name using the languageCodeToName() function. You can see what the mapping looks like in our codebase

SQL

Run in PostHog

PostHog AI

SELECT
    languageCodeToName('en') AS english, -- English
    languageCodeToName('fr') AS french, -- French
    languageCodeToName('pt') AS portuguese, -- Portuguese
    languageCodeToName('ru') AS russian, -- Russian
    languageCodeToName('zh') AS chinese -- Chinese

These HTML tags are currently supported, but for security reasons, none of them support attributes right now:

  • <div>, <p>, <span>, <pre>, <code>

  • <em>, <strong>, <b>, <i>, <u>

  • <h1>, <h2>, <h3>, <h4>, <h5>, <h6>

  • <ul>, <ol>, <li>

  • <table>, <thead>, <tbody>, <tr>, <th>, <td>

  • <blockquote>, <hr>

Most useful is the <a> tag for creating links. URLs are automatically clickable in the Table visualization, but you can also set a custom link using an <a> tag.

SQL

Run in PostHog

PostHog AI

select
    properties.$pathname,
    <a href={f'https://posthog.com/{properties.$pathname}'} target='_blank'>Link</a> as link
from events
where event = '$pageview'
Text effects

Plain text is so boring, so we added some text effects to make it cooler. Wrap any text in <blink> to make it blink, <marquee> to make it horizontally scroll, and <redacted> to hide it unless hovered.

SQL

Run in PostHog

PostHog AI

select
    <span>is this <blink>{event}</blink> real?</span>,
    <marquee>so real, yes!</marquee>,
    <redacted>but this one is hidden</redacted>
from events
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