> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # Application metrics **Metrics is in open alpha** Any team can turn on metrics: open [Metrics](https://app.posthog.com/metrics) in PostHog and select **Enable metrics** in the onboarding view. See [turn on metrics](#turn-on-metrics) below for the full flow. Setup details, including the ingestion endpoint, may change before general availability. During the alpha, PostHog keeps metrics data for 30 days. **Application metrics** lets you send application and infrastructure metrics (request counts, latencies, queue depths, resource usage) to PostHog and analyze them alongside your [logs](/docs/logs.md), [traces](/docs/distributed-tracing.md), and product data. We call these *application metrics* to distinguish them from the other kinds of metrics PostHog helps you track — [experiment metrics](/docs/experiments/metrics.md), [revenue metrics](/docs/revenue-analytics/revenue-metrics.md), and [usage metrics](/docs/customer-analytics/usage-metrics.md) measure your product and business, whereas application metrics measure the systems running it. ## Turn on metrics Metrics is in **open alpha**. Any team can turn it on for their project — no invitation or support request needed: 1. Open [Metrics](https://app.posthog.com/metrics) in PostHog. 2. On a new project, the onboarding view shows a preview of the metrics viewer and a getting-started checklist. Select **Enable metrics**. 3. Metrics turns on for your whole project, and the **Overview**, **Viewer**, and **SQL** tabs become available. Once enabled, there are three ways to send metrics: 1. **Already have PostHog installed?** Record metrics directly with the [`posthog.metrics` API](#already-have-posthog-installed-use-posthogmetrics) built into the PostHog SDK, with no extra packages. 2. **Using OpenTelemetry?** Point any standard OpenTelemetry library or Collector at [PostHog's OTLP endpoint](#set-up-metrics-with-opentelemetry), with no PostHog-specific packages. 3. **Already exposing Prometheus metrics?** Run the metrics agent with [Docker](/docs/metrics/installation/docker.md) or [Kubernetes](/docs/metrics/installation/kubernetes.md) to scrape your existing `/metrics` endpoints, with no code changes. ## Already have PostHog installed? Use posthog.metrics If [posthog-js](/docs/libraries/js.md) is already running on your site, you can record metrics immediately: no OpenTelemetry setup, no new dependencies, and no extra authentication beyond the project API key you already configured: JavaScript ```javascript // A counter for things that only go up posthog.metrics.count('checkout.completed') // A gauge for values that go up and down posthog.metrics.gauge('cart.items', 3) // A histogram for distributions like durations posthog.metrics.histogram('api.request.duration', 187, { unit: 'ms' }) ``` Add attributes to slice a metric by dimension, and set a service name so your metrics are easy to find and filter: JavaScript ```javascript posthog.init('', { api_host: 'https://us.i.posthog.com', metrics: { serviceName: 'storefront-web', environment: 'production', }, }) posthog.metrics.count('checkout.completed', 1, { attributes: { plan: 'pro' } }) posthog.metrics.histogram('api.request.duration', 187, { unit: 'ms', attributes: { route: '/api/checkout', status: '200' }, }) ``` Metrics are safe to record from hot paths. Samples are aggregated in memory and sent as one data point per series every 10 seconds, so a burst of 10,000 `count()` calls costs one data point on the wire. Pending metrics are flushed automatically when the page is hidden or closed, and you can force a flush with `posthog.metrics.flush()`. The `metrics` config also accepts `flushIntervalMs`, `maxSeriesPerFlush` (a cardinality guardrail, default 1000 series per window), `resourceAttributes`, and a `beforeSend` hook to filter or modify metrics before they're aggregated. Metrics deliberately carry no user or session context: every distinct attribute value creates a new series, so attach low-cardinality dimensions like plan, route, or status. Never attach user IDs. > **Note:** `posthog.metrics` is available in [posthog-js (web)](/docs/metrics/installation/javascript.md), [posthog-node](/docs/metrics/installation/nodejs.md), and [posthog-python](/docs/metrics/installation/python.md). See the [installation guides](/docs/metrics/installation.md) for per-platform setup. If you're on another SDK, or not using PostHog SDKs at all, use the OpenTelemetry setup below. ## Set up metrics with OpenTelemetry ### 1\. Get your project token You'll need your PostHog project token to authenticate metric requests. This is the same token you use for capturing events with the PostHog SDK. > **Important:** Use your **project token**, which starts with `phc_`. Do **not** use a personal API key (which starts with `phx_`). You can find your project token in [Project Settings](https://app.posthog.com/settings). ### 2\. Point your OTLP metrics exporter at PostHog Metrics are ingested over the OpenTelemetry Protocol (OTLP) via HTTP. Configure your OpenTelemetry SDK with these environment variables: Terminal ```bash OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="https://us.i.posthog.com/i/v1/metrics" OTEL_EXPORTER_OTLP_METRICS_HEADERS="Authorization=Bearer " OTEL_SERVICE_NAME="my-app" ``` Alternatively, you can pass the token as a query parameter instead of a header: Terminal ```bash OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="https://us.i.posthog.com/i/v1/metrics?token=" ``` ### 3\. Record metrics from your code If you'd rather configure the exporter in code, here's a complete setup. #### Python Terminal ```bash pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp ``` Python ```python from opentelemetry import metrics from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter exporter = OTLPMetricExporter( endpoint="https://us.i.posthog.com/i/v1/metrics", headers={"Authorization": "Bearer "}, ) reader = PeriodicExportingMetricReader(exporter, export_interval_millis=15000) metrics.set_meter_provider(MeterProvider(metric_readers=[reader])) meter = metrics.get_meter("my-app") # A counter for things that only go up checkouts = meter.create_counter("checkouts", description="Completed checkouts") checkouts.add(1, {"plan": "pro"}) # A histogram for distributions like durations request_duration = meter.create_histogram("http.request.duration", unit="ms") request_duration.record(42.5, {"route": "/api/checkout", "status": "200"}) ``` #### Node.js Terminal ```bash npm install @opentelemetry/api @opentelemetry/sdk-metrics @opentelemetry/exporter-metrics-otlp-http ``` typescript ```typescript import { metrics } from '@opentelemetry/api' import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http' import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics' const exporter = new OTLPMetricExporter({ url: 'https://us.i.posthog.com/i/v1/metrics', headers: { Authorization: 'Bearer ' }, }) const meterProvider = new MeterProvider({ readers: [new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 15000 })], }) metrics.setGlobalMeterProvider(meterProvider) const meter = metrics.getMeter('my-app') // A counter for things that only go up const checkouts = meter.createCounter('checkouts', { description: 'Completed checkouts' }) checkouts.add(1, { plan: 'pro' }) // A gauge for values that go up and down const queueDepth = meter.createGauge('queue.depth') queueDepth.record(17, { queue: 'emails' }) ``` > **Note:** If your language isn't listed, any OpenTelemetry SDK works. Check the [OpenTelemetry documentation](https://opentelemetry.io/docs/languages/) for your language and point its OTLP metrics exporter at PostHog. ### Already running an OpenTelemetry Collector? If your services already ship metrics to an [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/), add PostHog as an exporter, with no application changes needed: YAML ```yaml exporters: otlphttp/posthog: metrics_endpoint: "https://us.i.posthog.com/i/v1/metrics" headers: Authorization: "Bearer " service: pipelines: metrics: receivers: [otlp] exporters: [otlphttp/posthog] ``` ### Already exposing Prometheus metrics? If your services expose Prometheus `/metrics` endpoints, you don't need to change any code or run your own Collector. Deploy the PostHog metrics agent with [Docker](/docs/metrics/installation/docker.md) or the [Kubernetes Helm chart](/docs/metrics/installation/kubernetes.md) and it scrapes your existing endpoints and forwards them to PostHog, exemplars included: Terminal ```bash docker run -d \ -e POSTHOG_API_KEY= \ -e SCRAPE_TARGETS=your-app:9090 \ posthog/metrics-agent:latest ``` ## Supported metric types PostHog ingests all OTLP metric types: | Type | What it's for | Example | | --- | --- | --- | | **Counter (sum)** | Values that only go up | Requests served, checkouts completed | | **Gauge** | Values that go up and down | Queue depth, memory in use, active connections | | **Histogram** | Distributions | Request duration, payload size | | **Exponential histogram** | Distributions with automatic bucketing | Same as histogram, with better resolution across wide ranges | | **Summary** | Pre-computed quantiles | Latency percentiles from legacy clients | A metric's identity is its **name, type, and attributes** together. `http.requests` with `{route: "/home"}` and `http.requests` with `{route: "/checkout"}` are separate series, so keep attribute values low-cardinality. Attach things like route, status, or plan, not user IDs or request IDs. ### Naming and units Give metrics descriptive, dot-separated names like `checkout.failed`, `email.sent`, or `queue.depth`, not vague ones like `metric1` or `counter`. Set an explicit unit on histograms (`ms`, `bytes`) so charts are unambiguous. Use one name per metric type: recording `queue.depth` as both a gauge and a counter blends both series in charts. ### What to instrument Good starting points: - **Critical operations**: count successes and failures of the flows that matter: checkouts, signups, imports - **Service boundaries**: time external API calls and database queries with histograms - **Business events**: counters for orders, upgrades, or invites, sliced by plan or region - **Resource usage**: gauge queue depths, connection pools, and cache sizes ## Use your metrics Once metrics are flowing, open [Metrics](https://app.posthog.com/metrics) in PostHog. The metrics scene has three tabs — **Overview** for ingestion health, **Viewer** to chart and break down a metric, and **SQL** to query the raw data. Chart any metric over time, group and filter it by attribute, let the anomaly badge point at what changed, pivot into the logs and traces behind it, and set an [alert](/docs/metrics/explore.md#alerting) to get notified when a metric crosses a threshold. See [Use your metrics](/docs/metrics/explore.md) for the full tour. ## Troubleshooting **No metrics showing up?** 1. Check you're using your **project token** (`phc_...`), not a personal API key (`phx_...`) 2. Check the endpoint path is exactly `/i/v1/metrics`, requests without a token return `401 Unauthorized` 3. OTLP SDKs export on an interval (60 seconds by default), lower `export_interval_millis` while testing, and in short-lived scripts flush before exit (`meterProvider.forceFlush()` / `shutdown()`), or your metrics may never be sent 4. Using `posthog.metrics`? The SDK flushes every 10 seconds, call `posthog.metrics.flush()` to send immediately while testing, and check you're not opted out of capturing ## Next steps - **[Logs](/docs/logs.md)**: capture what happened at each point, using the same OpenTelemetry setup - **[Distributed tracing](/docs/distributed-tracing.md)**: follow requests across your services - **[Error tracking](/docs/error-tracking.md)**: turn failures into issues you can assign and resolve ### Still have questions? Ask PostHog AI ### Was this page useful? HelpfulCould be better