Skip to main content
13 May 2026Docs · 05

← Docs

Examples

Four drafts, PostHog repo.

Each piece below is unedited output from devrel content draft against PostHog's public handbook. Generated 2026-05-13 on devrel-origin v0.2.14.

Setup: git clone PostHog/posthog · devrel init · devrel kb add for 25 docs · devrel content draft "..." --type X.

01Blog post

Reading PostHog Funnels with HogQL: How to Find Your Worst Step Week-Over-Week

prompt: Reading PostHog funnels with the conversion drop-off API: how to find your worst step week-over-week

grounded in: 5 KB doc(s)materialized-columns, safe-django-migrations, CONTRIBUTING, clickhouse-event-table-migrations, query-performance-optimization

editorial mode: fast · code validation: passed

View full draft (7,976 chars)+

Meta description: Learn how to query PostHog's events table in HogQL to identify which funnel step loses the most users week-over-week. Includes working queries and performance optimization tips.


Funnel analysis shows you where users drop off in multi-step flows—sign-up sequences, onboarding paths, checkout processes. But spotting which step degraded this week versus last week requires comparing conversion rates across time windows. This tutorial shows you how to query PostHog's underlying events data to surface the funnel step with the biggest week-over-week drop-off increase.

Prerequisites

  • PostHog Cloud or self-hosted instance with funnel insights already configured
  • Access to HogQL queries (PostHog's SQL-like query interface)
  • Familiarity with funnel step definitions in your PostHog project (event names, property filters)
  • Basic SQL knowledge for aggregating and comparing time-series data

You'll be querying the events table via HogQL. If you're on PostHog Cloud, you can run these queries directly in the SQL insight editor.


Step 1: Define Your Funnel Steps as Event Filters

PostHog funnels are sequences of events. Before querying conversion rates, list the exact event names and any property filters that define each step. For example, a sign-up funnel might be:

  1. Step 1: $pageview with a specific URL pattern
  2. Step 2: form_submitted with a form identifier
  3. Step 3: account_created

Write down these event names and filters—you'll use them in the WHERE clauses below.


Step 2: Query Funnel Conversion Rates for Two Time Windows

PostHog stores events in the events table with columns event, timestamp, distinct_id, properties, and team_id. To calculate conversion rates between steps, you'll:

  1. Count distinct users who completed Step 1 in each time window
  2. Count distinct users who completed Step 1 and then Step 2 within a session window
  3. Divide Step 2 completions by Step 1 completions to get the conversion rate

Here's a HogQL query template for a two-step funnel comparing this week versus last week:

Evidence note: the source material does not verify a safe copy-paste SQL query for this check because current_url, form_submitted, last_week_rate, last_week_step1, last_week_step2, rate_change did not appear in the provided evidence. Treat this as a current evidence limitation rather than a runnable query.

Replace placeholders:

  • <value>: Your PostHog team ID (find it in Project Settings)
  • Event names and property filters: Match your funnel definition
  • Session window (INTERVAL 1 HOUR): Adjust based on expected user behavior

Performance note: This query joins the events table to itself. For high-volume projects, consider materializing frequently-queried properties to speed up JSONExtractString operations. See [handbook/materialized-columns.md] for details on automatic and manual materialization via Dagster.


Step 3: Extend the Query to All Funnel Steps

If your funnel has more than two steps, repeat the CTE pattern for each step pair. Then combine results to compare all step pairs:

Evidence note: the source material does not verify a safe copy-paste SQL query for this check because rate_change did not appear in the provided evidence. Treat this as a current evidence limitation rather than a runnable query.

The step with the most negative rate change is your worst-performing step this week.


Step 4: Optimize Query Performance for Large Datasets

For projects with millions of events per week, these self-joins can be slow. Apply these optimizations:

4.1 Materialize Frequently-Filtered Properties

If you filter on properties in every query, materialize them as ClickHouse columns. PostHog automatically materializes properties used in slow queries (see [handbook/materialized-columns.md]), but you can manually trigger materialization via Dagster:

Configure the create_materialized_columns_op with your property name. Materialized columns can make property filters up to 25× faster [handbook/materialized-columns.md].

4.2 Limit the Time Window

Instead of querying all events in a 7-day window, filter to business hours or high-traffic periods if your funnel is time-sensitive:

Evidence limitation: this runnable code example was removed because it failed syntax validation. Use the surrounding verified data model and source notes instead of copying an invalid snippet.

4.3 Use PREWHERE for Early Filtering

ClickHouse evaluates PREWHERE before reading all columns. Move your most selective filter (usually team_id and event) to PREWHERE:

Evidence note: the source material does not verify a safe copy-paste SQL query for this check because current_url did not appear in the provided evidence. Treat this as a current evidence limitation rather than a runnable query.

See [handbook/query-performance-optimization.md] for more ClickHouse optimization patterns.


Step 5: Verify Results Against PostHog's Funnel Insight

Run the same funnel in PostHog's Insights UI for the same time windows. Compare the conversion rates:

  1. Go to InsightsNew InsightFunnel
  2. Define the same steps and filters
  3. Set the date range to "Last 7 days" and "Previous 7 days"
  4. Check the conversion rate for each step

Your HogQL query results should match within a few percentage points. Small discrepancies can occur due to:

  • Session window differences: PostHog's UI may use a different default session timeout
  • Timezone offsets: Ensure your query uses the same timezone as your PostHog project settings
  • Sampling: PostHog may sample large datasets in the UI; HogQL queries run on full data

Troubleshooting

Query Times Out or Returns Empty Results

Cause: The events table is large, and self-joins without proper indexing are slow.

Fix:

  1. Add LIMIT 10000 to each CTE to test the query logic on a subset
  2. Verify team_id and event filters are correct (typos will return zero rows)
  3. Check that your time window actually contains events

Conversion Rates Are Unexpectedly High or Low

Cause: Session window is too long or too short, or property filters don't match your funnel definition.

Fix:

  1. Inspect a sample of raw events to confirm property values
  2. Adjust the session window (INTERVAL 1 HOUR) based on actual user behavior
  3. Verify property extraction syntax (e.g., JSONExtractString vs. JSONExtractRaw)

Materialized Columns Not Speeding Up Queries

Note: Materialized columns require backfilling to be effective [handbook/materialized-columns.md]. This is typically a weekend operation due to cluster load. Verify the column exists by checking your table schema, and ensure your WHERE clause references the materialized column directly rather than extracting from the JSON properties field.


Next Steps

  1. Automate the Query: Schedule this query to run weekly via PostHog's scheduled insights or an external cron job. Export results to Slack or email.
  2. Drill Down into Drop-Off Causes: Once you identify the worst step, query for common properties among users who dropped off to understand why.
  3. Compare Across Cohorts: Segment the funnel by user properties (e.g., new vs. returning users, mobile vs. desktop) to find which cohort regressed.

Evidence limitation: the current KB evidence does not verify a self-contained command, schema, or example for this step.


Sources:

  • [handbook/materialized-columns.md]
  • [handbook/query-performance-optimization.md]

Word count: ~1,280 words

02Tutorial

Setting Up Session Replay for a Next.js App with PostHog: Install, Masking, and Performance Budget

prompt: Setting up session replay for a Next.js app with PostHog: install, masking, performance budget

grounded in: 5 KB doc(s)README, manual-dev-setup, data-warehouse, setup-ssl-locally, developing-locally

editorial mode: fast · code validation: passed

View full draft (7,386 chars)+

Source citations: root/README.md

Session replay is one of PostHog's core features—it captures real user sessions so you can watch exactly how people interact with your app, diagnose bugs, and understand behavior patterns. This guide walks you through installing PostHog in a Next.js application, configuring privacy masking, and setting performance budgets to keep replay overhead minimal.


Prerequisites

  • Next.js 13+ (App Router or Pages Router)
  • Node.js 18+ and npm/pnpm/yarn
  • A PostHog account (Cloud or self-hosted instance)
  • Your PostHog Project API Key and Host URL (find these in Project Settings)

Step 1: Install the PostHog JavaScript SDK

PostHog provides a dedicated posthog-js library for browser-side tracking. Install it:

npm install posthog-js
# or
pnpm add posthog-js

Step 2: Initialize PostHog in Your Next.js App

App Router (Next.js 13+)

Create a client-side provider component to initialize PostHog once. The exact file structure will depend on your Next.js setup:

'use client'

import posthog from 'posthog-js'
import { PostHogProvider } from 'posthog-js/react'
import { useEffect } from 'react'

export function PHProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    posthog.init('<your-api-key>', {
      api_host: '<value>',
      session_recording: {
        maskAllInputs: true,
        maskTextSelector: '[data-private]',
      },
    })
  }, [])

  return <PostHogProvider client={posthog}>{children}</PostHogProvider>
}

Wrap your root layout with the provider component.

Pages Router (Next.js 12 and earlier)

Initialize PostHog in your app entry point:

import posthog from 'posthog-js'
import { PostHogProvider } from 'posthog-js/react'
import { useEffect } from 'react'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    posthog.init('<your-api-key>', {
      api_host: '<value>',
      session_recording: {
        maskAllInputs: true,
        maskTextSelector: '[data-private]',
      },
    })
  }, [])

  return (
    <PostHogProvider client={posthog}>
      <Component {...pageProps} />
    </PostHogProvider>
  )
}

Configuration

Replace <your-api-key> and <value> with your actual values. For PostHog Cloud, use https://app.posthog.com as the host. For self-hosted PostHog, use your instance URL.

Store these values as environment variables in your deployment configuration to avoid hardcoding credentials.


Step 3: Configure Privacy Masking

Session replay captures DOM snapshots and user interactions. By default, PostHog masks sensitive data, but you should tune this for your app's privacy requirements.

Mask All Text Inputs

The maskAllInputs: true option (shown above) redacts all <input>, <textarea>, and <select> values. Users will see *** in replays instead of actual input text.

Mask Specific Elements

Use the maskTextSelector option to target custom elements:

session_recording: {
  maskTextSelector: '[data-private],.sensitive-info, #user-email',
}

Then mark elements in your JSX:

<div data-private>
  User's credit card: 4111 1111 1111 1111
</div>

Unmask Safe Inputs

If you want to capture certain inputs (e.g., search queries), set maskAllInputs: false and selectively mask sensitive fields:

session_recording: {
  maskAllInputs: false,
  maskInputOptions: {
    password: true,
    email: true,
  },
}

Block Entire Sections

Add the ph-no-capture class to prevent recording specific DOM subtrees:

<div className="ph-no-capture">
  <CreditCardForm />
</div>

PostHog will replace this section with a placeholder in replays.


Step 4: Set a Performance Budget

Session replay adds network and CPU overhead. PostHog provides several knobs to control resource usage:

Sampling Rate

Record only a percentage of sessions:

session_recording: {
  sampleRate: 0.5, // Record 50% of sessions
}

Use conditional sampling to record more sessions for authenticated users:

posthog.init('<your-api-key>', {
  session_recording: {
    sampleRate: posthog.get_distinct_id() ? 1.0: 0.2,
  },
})

Network Payload Size

Limit the size of network request/response bodies captured in replays:

session_recording: {
  recordHeaders: true,
  recordBody: true,
  networkPayloadCapture: {
    recordHeaders: true,
    recordBody: true,
  },
}

Configure URL patterns to exclude from network capture based on your application's API structure.

Canvas and Performance Capture

Disable canvas recording if your app uses heavy graphics:

session_recording: {
  recordCanvas: false,
}

Disable performance metrics if you don't need Web Vitals in replays:

session_recording: {
  capturePerformance: false,
}

Minimum Session Duration

Skip very short sessions (e.g., bots, accidental clicks):

session_recording: {
  minimumDuration: 5000, // Only record sessions longer than 5 seconds
}

Step 5: Verify Session Replay is Working

  1. Start your Next.js dev server:

    npm run dev
    
  2. Open your app in a browser and interact with it (click buttons, fill forms, navigate pages).

  3. Go to PostHogSession Replay in your project dashboard. You should see your session appear within 30 seconds.

  4. Click on a session to watch the replay. Verify that:

    • Masked inputs show ***
    • Elements with data-private are redacted
    • Sections with ph-no-capture are blocked

Troubleshooting

No Sessions Appear in PostHog

  • Check configuration: Ensure your Project API Key and Host URL are correct.
  • Verify initialization: Open browser DevTools → Network tab and look for requests to PostHog endpoints. If missing, PostHog isn't initializing.
  • Check sampling rate: If you set sampleRate < 1.0, you may need to refresh multiple times to trigger a recorded session.

Replays Are Choppy or Missing Interactions

  • Network throttling: Session replay sends snapshots in batches. Slow networks can delay uploads. Check the Network tab for failed requests.
  • Ad blockers: Some ad blockers block PostHog. Test in an incognito window without extensions.

Sensitive Data Leaking in Replays

  • Review masking selectors: Use browser DevTools to inspect elements and confirm they match your maskTextSelector.
  • Test with real data: Fill out forms with fake sensitive data and verify it's redacted in the replay.

High CPU Usage

  • Disable canvas recording: Set recordCanvas: false if your app has heavy animations.
  • Reduce sampling: Lower sampleRate to 0.1 or 0.2 for high-traffic apps.

Next Steps

  • Analyze user behavior: Use PostHog's Product Analytics to correlate session replays with feature usage, conversion funnels, and retention cohorts (source: root/README.md).
  • A/B test with replays: Combine Feature Flags with session replay to watch how users interact with experimental features (source: root/README.md).
  • Monitor performance: Enable capturePerformance: true to track Web Vitals (LCP, FID, CLS) alongside replays.

Knowledge base references:

  • PostHog feature overview: root/README.md
03Cold outreach

Introducing PostHog Data Warehouse to Your Series B SaaS: A Technical Deep Dive

prompt: Outreach to a Series B SaaS data lead introducing PostHog data warehouse

grounded in: 5 KB doc(s)data-warehouse, developing-locally, README, implementing-mcp-tools, writing-skills

editorial mode: fast · code validation: passed

View full draft (8,310 chars)+

If you're leading data at a Series B SaaS company, you've likely hit the point where product analytics data lives in one system, customer data in another, and your data warehouse somewhere else entirely. PostHog's data warehouse feature eliminates this fragmentation by letting you query external data sources alongside your product analytics events—all in one place, using SQL.

This guide walks through what PostHog's data warehouse actually does, how to connect your first source, and what makes it different from traditional ETL pipelines.

What PostHog Data Warehouse Solves

PostHog is an all-in-one product analytics platform that includes product analytics, session replay, feature flags, and web analytics. The data warehouse feature extends this by letting you:

  1. Import external data sources (Postgres, MySQL, Stripe, Zendesk, etc.) into PostHog
  2. Query imported data alongside product events using HogQL (PostHog's SQL dialect)
  3. Join customer data with behavioral data without moving data between systems
  4. Build unified dashboards that combine product metrics with business metrics

Instead of exporting PostHog events to your data warehouse, you bring your warehouse data into PostHog. This inverts the traditional ETL pattern and keeps analysis close to where product decisions happen.

Source: handbook/data-warehouse.md

Architecture: How Data Flows

When you connect a source to PostHog's data warehouse:

  1. Temporal workflows orchestrate the sync process
  2. Data gets imported into MinIO (PostHog's object storage layer)
  3. Tables become queryable via HogQL alongside your product analytics events
  4. Incremental syncs keep data fresh without full table scans

PostHog uses MinIO for object storage, which means imported data sits in the same infrastructure as session recordings and file exports. On PostHog Cloud, this is managed for you. For self-hosted instances, MinIO runs as part of the Docker Compose stack.

Source: handbook/data-warehouse.md, handbook/developing-locally.md

Connecting Your First Source: Postgres Example

Let's walk through connecting a Postgres database. This example uses PostHog's local development setup, but the same flow applies to PostHog Cloud.

Prerequisites

  • A PostHog instance (Cloud or self-hosted)
  • A Postgres database with tables you want to query
  • Network access from PostHog to your database

Step 1: Navigate to the Source Creation Flow

In PostHog, go to the Data Pipeline section and select New Source. For local development, navigate to http://localhost:8010/project/pipeline/new/source and select Postgres.

Step 2: Configure Connection Details

For local development with PostHog's own Postgres instance, use these settings:

Host: 127.0.0.1
Port: 5432
Database: posthog
User: posthog
Password: posthog
Schema: public

For production databases, use a read-only database user with access only to the tables you want to import. PostHog never writes to your source database.

Step 3: Select Tables and Sync Type

PostHog offers three sync modes:

  • Incremental: Syncs only new/updated rows based on a cursor column (e.g., updated_at)
  • Append-only: Adds new rows without updating existing ones (ideal for immutable event logs)
  • Full table: Re-imports the entire table on each sync (use for small dimension tables)

Choose incremental sync for large transactional tables. Select the tables you want to import and their sync types, then complete the import.

Source: handbook/data-warehouse.md

Step 4: Verify the Import

The temporal-worker-data-warehouse process imports data into your local MinIO instance. Once the sync completes, your table appears in PostHog's data warehouse section and becomes queryable via HogQL.

MySQL Sources: Additional Setup

MySQL sources require extra configuration. If you're connecting MySQL locally:

  1. Install MySQL:
brew install mysql
brew services start mysql
  1. Create a test database and table:
mysql -u root

Evidence note: the source material does not verify a safe copy-paste SQL query for this check because test_user did not appear in the provided evidence. Treat this as a current evidence limitation rather than a runnable query.

  1. Create a read-only user with appropriate permissions for the tables you want to sync.

  2. Connect in PostHog using the same flow as Postgres, but select MySQL as the source type.

Source: handbook/data-warehouse.md

Querying Imported Data with HogQL

HogQL is PostHog's SQL dialect built on ClickHouse. It supports standard SQL syntax with extensions for product analytics use cases.

Basic Query Structure

Evidence note: the source material does not verify a safe copy-paste SQL query for this check because your_imported_table did not appear in the provided evidence. Treat this as a current evidence limitation rather than a runnable query.

Joining with Product Events

The real power comes from joining imported data with PostHog's events table. The specific columns and tables available depend on what you've imported and your PostHog event schema.

Note: The exact column names, table names, and join keys will vary based on your imported data structure and PostHog event properties. Consult your imported table schema and PostHog event properties to construct appropriate joins.

Source: handbook/data-warehouse.md

Sync Frequency and Data Freshness

PostHog's data warehouse syncs run on configurable schedules. The exact frequency depends on your sync configuration and table size. For real-time use cases, consider using PostHog's event ingestion API instead of data warehouse imports. The data warehouse is optimized for batch analytics, not sub-second latency.

Troubleshooting Common Issues

Connection Timeouts

If PostHog can't reach your database:

  1. Verify network access: Ensure PostHog's IP addresses are whitelisted in your firewall
  2. Check credentials: Test the connection string with a SQL client
  3. Validate SSL settings: Some databases require SSL; others block it

Sync Failures

If a sync fails after initial setup, check the sync logs in PostHog's data warehouse UI to diagnose the issue.

Query Performance

If queries run slowly:

  1. Add indexes to join columns in your source database
  2. Use incremental sync instead of full table for large datasets
  3. Filter by timestamp early in the query to reduce data scanned

Source: handbook/data-warehouse.md

Accessing Imported Data in MinIO (Self-Hosted)

For self-hosted PostHog instances, imported data lives in MinIO. To inspect it:

  1. Navigate to http://localhost:19001/ (or your MinIO host)
  2. Log in with username object_storage_root_user and password object_storage_root_password
  3. Open the data-warehouse bucket
  4. Each synced table has its own folder with data files

This is useful for debugging sync issues or verifying data landed correctly.

Source: handbook/data-warehouse.md

When to Use Data Warehouse vs. Event Ingestion

Use data warehouse imports when:

  • You have existing data in external systems (CRM, billing, support)
  • You need to join business context with product events
  • Data updates hourly or daily (not real-time)

Use event ingestion when:

  • You're tracking user behavior in your product
  • You need real-time analytics
  • Data is generated by user actions, not batch processes

The two features complement each other. Events capture what users do; data warehouse imports explain who they are and what they're worth.

Next Steps

  1. Identify your first source: Start with a small table to test the workflow
  2. Create a read-only user: Never use production credentials with write access
  3. Build a test query: Join imported data with product events to validate the setup
  4. Monitor sync status: Track sync success in PostHog's data warehouse UI

PostHog's data warehouse turns your product analytics platform into a unified analytics hub. Instead of stitching together dashboards from multiple tools, you query everything in one place—using SQL you already know.

All technical details sourced from: handbook/data-warehouse.md, handbook/developing-locally.md

04Battle card

PostHog vs Mixpanel for Product Analytics: When Each One Wins

prompt: PostHog vs Mixpanel for product analytics: when each one wins

grounded in: 5 KB doc(s)type-system, README, implementing-mcp-tools, project-structure, sandboxed-agents

editorial mode: fast · code validation: passed

View full draft (9,023 chars)+

Choosing between PostHog and Mixpanel isn't about picking the "better" tool—it's about matching capabilities to your team's workflow, infrastructure constraints, and product philosophy. Both platforms deliver event-based product analytics, but they diverge sharply on deployment models, data ownership, extensibility, and pricing transparency. This guide cuts through the marketing noise to show you when each platform wins on technical merit.

The Core Difference: Deployment Philosophy

PostHog is an open-source, self-hostable platform that gives you full control over your data pipeline. You can run it on your infrastructure (AWS, GCP, self-managed Kubernetes) or use PostHog Cloud. The codebase is public—every API endpoint, database schema, and query optimization is inspectable on GitHub.

Mixpanel is a proprietary SaaS platform. You send events to Mixpanel's infrastructure, query through their UI, and trust their black-box algorithms for retention calculations and funnel attribution. There's no self-hosted option, no source code access, and limited control over data residency beyond region selection.

PostHog wins when:

  • You need data to stay in your VPC for compliance (HIPAA, SOC 2, GDPR with strict data residency)
  • You want to extend analytics with custom SQL queries, warehouse integrations, or ML pipelines
  • Your team values transparency—being able to audit how metrics are calculated

Mixpanel wins when:

  • You want zero infrastructure overhead and instant setup
  • Your data volumes fit comfortably within Mixpanel's pricing tiers
  • You prioritize a polished, opinionated UI over extensibility

Feature Parity: What Both Platforms Do Well

Both PostHog and Mixpanel cover the product analytics fundamentals:

  • Event tracking: Autocapture (PostHog) or manual instrumentation (both)
  • Funnels: Multi-step conversion analysis with time windows
  • Retention cohorts: Day-N retention, unbounded retention, custom cohort definitions
  • User profiles: Merge anonymous → identified users, store custom properties
  • Segmentation: Filter events by user properties, event properties, or behavioral cohorts

Where they differ is in how these features integrate with the rest of your stack.

When PostHog Wins: Extensibility and Data Control

1. SQL-First Analytics with HogQL

PostHog exposes a SQL query interface called HogQL that compiles to ClickHouse SQL. You can write queries against your event data, join with external tables, and export results to your data warehouse. The HogQL implementation lives in posthog/hogql within the Django backend application (source: handbook/project-structure.md).

Mixpanel's query interface is UI-driven. You can't write raw SQL, and exporting data for custom analysis requires their Data Pipelines add-on (extra cost).

PostHog wins when:

  • Your data team needs to join product analytics with warehouse data (Snowflake, BigQuery)
  • You want to build custom dashboards in Metabase, Looker, or Grafana
  • You need to audit metric definitions or debug discrepancies in funnel calculations

2. All-in-One Platform: Analytics + Feature Flags + Session Replay

PostHog bundles product analytics, feature flags, session replay, A/B testing, and web analytics in one platform (source: root/README.md). This eliminates the need to stitch together LaunchDarkly (flags), FullStory (replays), and Mixpanel (analytics).

Mixpanel only does analytics. If you need feature flags or session replay, you're paying for and integrating separate tools.

PostHog wins when:

  • You want to correlate feature flag exposure with conversion metrics in the same query
  • You need to watch session replays of users who dropped off in a funnel
  • You're optimizing for vendor consolidation and cost efficiency

3. Transparent Pricing and Self-Hosting

PostHog's pricing is usage-based with public pricing. Self-hosting is available for the open-source version; enterprise features (SAML SSO, advanced permissions) require a license.

Mixpanel's pricing is opaque—you negotiate with sales based on MTUs (monthly tracked users). Costs can spike unpredictably if your user base grows or if you accidentally track high-cardinality events.

PostHog wins when:

  • You need predictable costs tied to event volume, not user counts
  • You want to avoid sales calls and annual contracts
  • You're a startup optimizing for capital efficiency

When Mixpanel Wins: Polished UX and Behavioral Cohorts

1. Best-in-Class Funnel and Retention UI

Mixpanel's funnel builder is more intuitive than PostHog's for non-technical users. Drag-and-drop event ordering, inline property filters, and automatic conversion time breakdowns make it faster to explore hypotheses without writing queries.

Mixpanel's retention reports also surface insights like "users who did X in week 1 retained 20% better"—PostHog requires you to build these comparisons manually with HogQL.

Mixpanel wins when:

  • Your PM team needs self-service analytics without SQL knowledge
  • You prioritize speed of exploration over query flexibility
  • You're willing to trade extensibility for a more opinionated, guided workflow

2. Advanced Behavioral Cohorts

Mixpanel's cohort engine lets you define segments like "users who did event A but not event B within 7 days, then returned within 30 days." PostHog supports cohorts, but the UI is less expressive for complex temporal logic.

Mixpanel wins when:

  • You need to target re-engagement campaigns based on nuanced behavioral patterns
  • Your growth team lives in cohort-based experimentation
  • You don't need to export cohorts to external tools (Mixpanel's cohort sync is limited)

3. Enterprise Support and Onboarding

Mixpanel offers white-glove onboarding, dedicated CSMs, and 24/7 support for enterprise customers. PostHog's support is community-driven (Slack, GitHub issues) unless you're on an enterprise plan.

Mixpanel wins when:

  • You need guaranteed SLAs and hands-on implementation help
  • Your team lacks in-house analytics engineering expertise
  • You're at a scale where vendor support justifies the premium

The Hybrid Approach: When to Use Both

Some teams run PostHog for engineering-driven analytics (debugging, feature flag analysis, session replay) and Mixpanel for product-driven analytics (funnel optimization, retention cohorts). This works if:

  • You have budget for both tools
  • Your data volumes are low enough that dual instrumentation doesn't double costs
  • You're okay with maintaining two event schemas and reconciling discrepancies

Most teams find this operationally painful. Pick one as your source of truth.

Decision Framework

| You Should Choose PostHog If: | You Should Choose Mixpanel If: | |------------------------------------------------------------|--------------------------------------------------------| | You need data to stay in your infrastructure | You want zero infrastructure management | | Your data team writes SQL regularly | Your PM team needs self-service, no-code analytics | | You want to consolidate analytics, flags, and replay | You only need analytics and already have other tools | | You need transparent, usage-based pricing | You're willing to negotiate enterprise contracts | | You value open-source auditability | You prioritize a polished, opinionated UX | | You're building a data warehouse integration | You're optimizing for speed of exploration |

Getting Started

PostHog:

  1. Sign up for PostHog Cloud (includes free tier)
  2. Install the JavaScript SDK or Python SDK
  3. Start with autocapture, then add custom events for key actions
  4. Explore HogQL queries for custom analysis

Mixpanel:

  1. Sign up for Mixpanel
  2. Instrument events with the JavaScript SDK
  3. Build your first funnel in the UI
  4. Request a pricing quote if you exceed the free tier

The Bottom Line

PostHog wins on extensibility, data ownership, and cost transparency. If your team writes SQL, needs to self-host, or wants an all-in-one platform, PostHog is the better choice.

Mixpanel wins on UX polish and behavioral cohort sophistication. If your PM team needs self-service analytics and you're willing to pay for a premium SaaS experience, Mixpanel delivers.

For most engineering-led product teams, PostHog's open-source model and SQL-first approach make it the more future-proof choice. For product-led teams that prioritize ease of use over extensibility, Mixpanel's opinionated UI is worth the premium.


Sources:

What this proves

Repo-grounded, not vibes-grounded.

Every draft above cites which KB files Kai pulled from before generating. Open the trace JSON for any piece (sibling file in .devrel/deliverables/<week>/) to see grounding sources, code-validation results, and the full editorial revision log.

With no KB matches, Kai refuses to generate (status="insufficient_evidence") instead of producing ungrounded prose. See Concepts §4 for the full forbidden-claims contract.