When to open this page: when taking over a product that uses PostHog, when you need to audit what tracking has been implemented / build data dashboards / want an agent to query data by natural language. Includes key points from official docs + one original logged-in state direct investigation SOP(Section 7, not in the docs, the most time-saving method when auditing projects built by others).
1. What is PostHog
An open-source all-in-one product data platform ("Product OS"), which can be understood as "a self-controllable, SQL-deep-queryable GA + Sensors Data". Its core offering is Product Analytics, and it also comes with a bundle of sub-products including web analytics, Session Replay, Feature Flags, A/B Experiments, Surveys, error tracking, and LLM Analytics. The Cloud version is split into the US region (us.posthog.com) and the EU region (eu.posthog.com).
Don't mix up the two endpoints: Event reporting goes to us.i.posthog.com(public, used by frontend SDKs, no authentication required); backend/private APIs go to us.posthog.com(authentication required). You'll remember it after stepping on this pit once — if you try to query data via the API but hit the i. endpoint, you will keep getting 401 errors.
2. Core concepts (know these terms and you can understand the backend)
| Concept | Plain language explanation | Key notes |
|---|---|---|
| Event | A single action by a user, the smallest unit of data | Four elements: event name / distinct_id / timestamp / properties;$properties with the $ prefix are built into PostHog (e.g. $host, $browser) |
| Property | Additional information attached to an event | For example, a payment event has the amount attached price_usd |
| distinct_id | distinct_id: The unique identifier that distinguishes "who this is" | Anonymous users get a random UUID; after login, this should be replaced with the user ID |
| Person (user profile) | A profile that aggregates all events from the same person | Profiles are only created for identified events |
| identify | identify: Connects anonymous visitors and logged-in users | Call identify as soon as possible after login/registration posthog.identify(唯一ID,{...}). Big pitfall: If two people share the same distinct_id (e.g. mistakenly passing null/true), they will be merged into a single person |
| Insight (chart) | A single analysis chart, with 7 types | See Section 3 |
| Dashboard | Multiple Insights assembled on one screen | Can generate public share links; public dashboards refresh every 30 minutes |
| Action | Action: Bundles several events to use as one | Has retroactive effect (also applies to historical events); can be used to "rename" a single event or merge old and new metric definitions |
| Cohort | A group of people segmented by conditions | Static fixed list / Dynamic automatically updates every 24 hours |
| HogQL / SQL | HogQL: If charts can't meet your needs, write SQL | A wrapper around ClickHouse SQL,properties.$xxx HogQL uses dot syntax to access properties |
| Autocapture | Autocapture: Auto-collected, available without writing code | Page views/clicks are automatically recorded (prefixed with $); high volume, generic semantics (e.g. "clicked a button labeled delete") |
| Manual tracking | Business events explicitly reported by developers writing code | Clear semantics (e.g. paid_order); official recommendation is to use both together payment_success); the official recommendation is to use both in combination |
3. 7 Insight types + four high-frequency entry points
7 chart types: Trends (metrics over time) / Funnels (step conversion rates) / Retention / User Paths (browsing paths) / Stickiness / Lifecycle (splits users into four states: new - returning - churned - dormant to view health) / SQL (custom query tables).
Four high-frequency entry points:
- Activity Live Events — Real-time raw event stream; check this to verify "did the tracking event actually get reported".
- Product analytics → New New Insight — Create a new chart.
- Dashboards Dashboards — Assemble a dashboard.
- SQL HogQL — Write HogQL for deep queries.
4. Key metric definition pitfalls (scattered across docs, you only know them after stepping on them)
- Total count vs Unique users Aggregation: In Trends, each line selects its aggregation method separately. Unique users at daily granularity = DAU.
- WAU/MAU uses a "rolling window": meaning "unique users who had events in the past 7/30 days", rolling day by day,not calendar week/calendar month — this differs from the definition in many BI tools; you must note this when reporting numbers externally, otherwise you will be challenged for mismatched figures.
- Count per user: Events per user, can take average/median/P90 — use this directly for "average chat rounds per user" "average orders per user", no need to write SQL.
- First-time filter: Only counts the first time a user performs an event, used for new user/activation analysis; there are two modes (first-ever occurrence / first matching event), their meanings are different so don't pick the wrong one.
- Breakdown bins: For numeric property breakdowns you can set bins to directly produce histograms; Trends supports at most 3 breakdowns, and by default only shows the top 25 values (you need to click "Load more").
- Common idiom for counting new/returning users: in tracking, use $set_once to write a non-overwritable registration date property, or use the Cohort "completed event for the first time", or directly use the Lifecycle chart to split automatically.
$set_onceWrite a non-overwritable registration date property, use the Cohort "completed event for the first time", or directly use the Lifecycle chart to split automatically.
5. MCP integration (let agents query data by natural language)
- Official hosted server:
https://mcp.posthog.com/mcp(automatically routes to US/EU based on account, no self-deployment needed). - One command for Claude Code:
claude mcp add --transport http posthog https://mcp.posthog.com/mcp -s user, after restarting, first use will pop up a browser for OAuth authorization (just log in with your existing PostHog account, no need to manually create a key). - Read-only mode: Add?read_only=true to the URL,
?readonly=trueall create/edit/delete tools are removed — PMs should use this for daily data queries for zero accidental operations; switch to full permissions when you need to build dashboards. - Capabilities: query/create/edit Insights and Dashboards, execute-sql, read-data-schema (check event property definitions), Feature Flags, experiments, search docs, etc. (official statement says 450+ tools, names change with version iterations, don't hardcode them).
- Alternative authentication: Settings → Personal API keys, the official MCP preset goes directly to app.posthog.com
app.posthog.com/settings/user-api-keys?preset=mcp_server, key prefix phx_phx_, shown only once. - Cost: MCP itself is free, underlying queries are billed according to normal rates.
6. Billing (judging if it's sufficient)
1 million events per month are free (free tier includes 1 project, 1 year of data retention). Over-quota anonymous events start at ~$0.00005 per event, cheaper at higher volumes;identified (real-name events that have called identify) events are about 5 times more expensive(starting at $0.000248 per event) — this is the main cost lever; turning on full autocapture will push up event volume. You can set a billing limit capped per product to prevent unexpected bills. Rule of thumb: within 1 million events/month it's all free, no need to worry about adding a few more business tracking events and blowing up costs.
7. ⭐ Hands-on investigation SOP (original, not in docs, highest reuse value)
To audit exactly what exists in a PostHog project built by someone else,don't click through the UI page by page. The fastest path:
- Enter the backend: In a sandbox Chrome, log in to us.posthog.com via Google/GitHub SSO to obtain the logged-in state cookie.
- Fetch internal APIs within the page(
fetch(url,{credentials:'include'})):- Event definitions: /api/projects/@current/event_definitions
GET /api/projects/<id>/event_definitions/?limit=250 - Dashboards: /api/projects/@current/dashboards
GET /api/projects/<id>/dashboards/; Charts: /api/projects/@current/insights.../insights/?saved=true - Project info/token: /api/projects/@current
GET /api/projects/<id>/
- Event definitions: /api/projects/@current/event_definitions
- Run HogQL for deep queries(most useful):
POST /api/environments/<id>/query/,body{"query":{"kind":"HogQLQuery","query":"<SQL>"}},The header must include X-PostHog-Project-IdX-Csrftoken(get the value from window.POSTHOG_APP_CONTEXT in the pagedocument.cookiein the browser consoleposthog_csrftoken, missing it will cause 403). - Three must-run HogQL queries(to judge data health, faster than looking at charts):
- Volume per event + unique users + first and last timestamp:
SELECT event, count(), count(DISTINCT person_id), toDate(min(timestamp)), toDate(max(timestamp)) FROM events WHERE timestamp > now() - INTERVAL 90 DAY GROUP BY event ORDER BY 2 DESC - View data sources by domain (key for catching "is production data flow broken"):
SELECT properties.$host, count(), toDate(max(timestamp)) FROM events WHERE timestamp > now()-INTERVAL 90 DAY GROUP BY 1 ORDER BY 2 DESC—— Tell at a glance whether real data comes from the production site or staging/localhost/internal backend. - Check test/production data mixing by environment property:
SELECT properties.environment, count() FROM events GROUP BY 1
- Volume per event + unique users + first and last timestamp:
- ⚠️ Number one lesson (personal mishap on 2026-07-05): First confirm whether the project you are querying is the production project. Just because someone gives you a project URL doesn't mean it's production. Real incident: CompanionAI's PostHog had two projects with the same name "Default project" — the 318867 they gave me was staging (environment distribution only had null/development/staging,no production value), and the real production was in another separate project 387337 (which I didn't have permission to see at first). I queried 318867 and misread the scattered test data from production domains there as "no data after 4-18" as production data outage, and even used it as evidence of "missing PostHog key", sending a completely wrong P0 request to developers, only to be immediately refuted in one sentence (production 387337 had 46,000 entries in the past 90 days, and data was still coming in that day).Step 0 triple check before starting investigation: ① Does this project's environment distribution have a production value? If not = you are most likely in staging; ② Are there multiple, even identically named projects in the org/account?
environmentdistribution have aproductionvalue? If not = you are most likely in staging; ② Are there multiple, even identically named projects in the org/account?/api/organizations/@current/projects/Pull the full project list; ③ Does data for the target production domain have entries up to "today"? If any of the three is abnormal, first suspect "I connected to the wrong project" instead of "production is broken". - Second lesson: Only reading docs + dashboards leads to misjudgment, so hands-on investigation comes first — but investigating the wrong target is more dangerous than not investigating at all, because you will have the confidence of "I checked the data myself" and weave every anomaly into "something is broken" (confirmation bias), instead of thinking "I'm looking at the wrong target". The prerequisite for hands-on investigation is to first lock onto the correct target with step 0. This shares the same root as the research discipline "static materials can't answer 'is it still running now'", but adds one more layer: first confirm "is what I'm querying the thing that's supposed to be running".
8. How to do "round distribution" for AI chat products
- Quick version: Trends, event = message event, aggregation select Count per user and take median/P90 — "how many rounds does a typical user chat per day".
- Histogram version: SQL Insight with two layers of aggregation (inner layer counts messages per session_id, outer layer bins sessions by round count):
SELECT multiIf(turns<=5,'1-5',turns<=10,'6-10',turns<=20,'11-20','20+') AS bucket, count() AS sessions FROM (SELECT properties.chat_session_id AS sid, count() AS turns FROM events WHERE event='<消息事件>' GROUP BY sid) GROUP BY bucket ORDER BY bucket - AI-specific: PostHog has an LLM Analytics product line that records each model call as a standard event ($ai_generation)
$ai_generation), with built-in per-user cost/round dashboards; it is worth evaluating for AI chat products — round distribution can be built directly on these events without separate tracking.
Sources
Official documentation (primary): posthog.com/docs and /data/events, /data/persons, /product-analytics/identify, /product-analytics/insights, /product-analytics/trends (aggregations/filters/breakdowns), /funnels, /data/actions, /data/cohorts, /sql, /product-analytics/autocapture, /model-context-protocol (and claude-code/tools/faq subpages), /api/personal-api-keys, /pricing, /ai-observability/start-here. The hands-on investigation SOP was verified through actual operation during the CompanionAI dashboard research on 2026-07-05.