A collection of PostHog skills for enhancing AI-assisted workflows. Add this repo as a Claude Code plugin marketplace to get access to all PostHog skills: Then install individual plugins: Or browse available plugins: Copy any skill directory to .claude/skills/ in your project: Any directory under skills/ that contains a .claude-plugin/plugin.json is automatically discovered and added to the market
Add this skill
npx mdskills install PostHog/exploring-llm-costs@PostHog? Sign in with GitHub to claim this listing.Comprehensive LLM cost investigation playbook with clear SQL patterns and ingestion-aware best practices
1---2name: exploring-llm-costs3description: >4 Investigate LLM spend in PostHog — total cost over time, cost by model,5 provider, user, trace, or custom dimension, token and cache-hit economics,6 and cost regressions. Use when the user asks "how much are we spending on7 LLMs?", "which model / user / feature is most expensive?", "why did cost8 spike?", wants to build a cost dashboard or alert, or pastes a trace URL9 and asks about its cost.10---1112# Exploring LLM costs1314PostHog attaches per-call cost metadata to every `$ai_generation` and `$ai_embedding`15event at ingestion time. Every cost question reduces to an aggregation over those16two event types — the interesting variation is only in how you group, filter, and17compare.1819This skill covers the common cost investigations: total spend, breakdowns20(model, provider, user, trace, custom property), token and cache-hit analysis,21regression debugging, and materializing results as insights, dashboards, or alerts.2223## Tools2425| Tool | Purpose |26| ------------------------------- | ------------------------------------------------------------------- |27| `posthog:execute-sql` | Ad-hoc HogQL for any cost aggregation — the workhorse of this skill |28| `posthog:query-llm-traces-list` | List traces with rolled-up cost, token, and error metrics |29| `posthog:query-llm-trace` | Cost breakdown of a single trace across all its events |30| `posthog:read-data-schema` | Discover which custom properties exist for breakdowns |31| `posthog:insight-create` | Materialize a cost chart as a saved insight |32| `posthog:dashboard-create` | Bundle cost insights into a dashboard |33| `posthog:alert-create` | Alert when cost crosses a threshold |3435## Core rules3637Three rules cover most of what goes wrong:3839- **Sum `$ai_total_cost_usd` for rollups, never the components.** Components drop40 request and web-search fees. The UI's cost cells sum `$ai_total_cost_usd`41 over `event IN ('$ai_generation', '$ai_embedding')`; mirror that. Full42 schema and rationale in [cost properties](./references/cost-properties.md).43- **Always include both `$ai_generation` and `$ai_embedding`** in cost queries44 unless the project demonstrably does not use embeddings — missing them silently45 under-counts. `$ai_trace` and `$ai_span` carry no rollup cost; some SDK46 wrappers duplicate `$ai_total_cost_usd` onto `$ai_trace` so don't include47 it in rollups or you'll double-count.48- **Always set a time range.** Cost queries without one scan the full events table.4950`$ai_total_cost_usd` is set at ingestion via one of three paths (passthrough,51custom pricing, automatic lookup). When a cost looks wrong, read52`$ai_cost_model_source` first — see [cost sources](./references/cost-sources.md)53for the precedence rules and a diagnostic query.5455Cache-hit math depends on whether the provider reports cache tokens inclusively56or exclusively of `$ai_input_tokens`. Always branch on the per-event57`$ai_cache_reporting_exclusive` flag, never on provider name — see58[cache accounting](./references/cache-accounting.md) for the exclusive-vs-inclusive59formula.6061`distinct_id` is the canonical user dimension. Customers often attach custom62properties (`feature`, `tenant_id`, `workflow_name`) — discover them with63`posthog:read-data-schema` before grouping. Don't guess names.6465## Workflow: total spend in a window6667```sql68posthog:execute-sql69SELECT round(sum(toFloat(properties.$ai_total_cost_usd)), 4) AS total_cost_usd70FROM events71WHERE event IN ('$ai_generation', '$ai_embedding')72 AND timestamp >= now() - INTERVAL 30 DAY73```7475## Workflow: cost breakdowns7677Every cost question is a variation of the same template — group by a dimension,78aggregate `$ai_total_cost_usd`. See [breakdown patterns](./references/breakdown-patterns.md)79for ready-to-run recipes:8081- Cost over time (daily)82- Cost by model83- Cost by user (top spenders)84- Cost by trace (top expensive traces)85- Cost by custom dimension86- Cost-per-call distribution87- Input vs output vs cache economics8889## Workflow: inspect a single trace's cost9091When the user pastes a trace URL and asks about its cost, fetch the trace and92surface the per-event breakdown:9394```json95posthog:query-llm-trace96{ "traceId": "<trace_id>", "dateRange": {"date_from": "-30d"} }97```9899Sum `$ai_total_cost_usd` across the returned events, grouped by span name or100model, to show which step(s) drove the cost. The trace response already101includes `totalCost` as a convenience.102103## Workflow: debug a cost regression104105"Our LLM bill jumped — why?" is almost always one of: more calls, bigger106prompts, a new model, or a change in cache-hit rate. Work through them in107order — see [regression debugging](./references/regression-debugging.md) for108the 5-step playbook.109110## Workflow: materialize as an insight, dashboard, or alert111112After ad-hoc queries answer the question, persist them as insights, bundle113into a dashboard, or wire up alerts. See [materializing](./references/materializing.md)114for ready-to-run JSON for `posthog:insight-create`, `posthog:dashboard-create`,115and `posthog:alert-create`.116117## Constructing UI links118119- **Dashboard**: `https://app.posthog.com/ai-observability/dashboard`120- **Traces list** (sort by cost): `https://app.posthog.com/ai-observability/traces`121- **Generations list**: `https://app.posthog.com/ai-observability/generations`122- **Users list** (per-user cost): `https://app.posthog.com/ai-observability/users`123- **Single trace**: `https://app.posthog.com/ai-observability/traces/<trace_id>?timestamp=<url_encoded_iso>`124125Always surface a UI link so the user can verify visually.126127## Keeping this skill current128129Provider reporting behavior (which tokens are inclusive vs exclusive,130which costs show up where) shifts over time and can differ between SDK131versions for the same provider. To avoid rot:132133- Branch on event-level flags (`$ai_cache_reporting_exclusive`,134 `$ai_cost_model_source`) rather than hardcoded provider or model names.135 Those flags are ingestion's resolved answer for the specific event and136 are the right source of truth.137- `$ai_total_cost_usd` is always authoritative for rollups — prefer it138 over summing components, which can drift as new cost categories are139 added.140- For anything not covered here (new cost categories, changes to141 pricing lookup, provider additions), run `posthog:docs-search` for142 "calculating costs" or "AI observability" first rather than trusting a143 hardcoded rule in this file.144- If you find this skill contradicting the UI, trust the UI and flag145 the skill for an update.146147## Tips148149- Always set a time range — cost queries without one scan the full events table150- Token, cost, model, and `$ai_trace_id` properties are on `events` — but message _content_ (`$ai_input` / `$ai_output_choices`) lives only on the `posthog.ai_events` table; see the traces skill's [event reference](../exploring-llm-traces/references/events-and-properties.md) if you need content alongside cost151- Always include `$ai_embedding` alongside `$ai_generation` when summing cost; embeddings are cheap per-call but add up at scale152- Costs are written at ingestion (see [Calculating LLM costs](https://posthog.com/docs/ai-observability/calculating-costs)) — if `$ai_total_cost_usd` is missing or zero, read `$ai_cost_model_source` first: `passthrough` means the SDK supplied costs; `custom` means custom token prices; `openrouter` / `manual` mean automatic lookup; missing means the model wasn't matched (unusual custom model, fine-tune). Grep: `countIf(properties.$ai_total_cost_usd IS NULL)` per `(model, source)`153- Custom pricing uses **per-token** prices, not per-million — if a custom-priced model looks ~1M× too expensive or too cheap, that's almost always the bug154- Exclude errored calls from cost totals only when explicitly asked — providers still charge for many error modes, and including them gives the truthful bill155- For per-user totals, exclude rows where `distinct_id = properties.$ai_trace_id` — some SDKs default distinct_id to the trace ID when no user is set156- Cost is additive across `$ai_generation` + `$ai_embedding` events within a trace; summing on `$ai_span` gives zero. `$ai_trace` may carry `$ai_total_cost_usd` from some SDK wrappers — don't include it in rollups or you'll double-count. `$ai_evaluation` events also carry cost but are not part of the stock UI rollups; include them only when the user explicitly wants evaluation spend in the total157- Cache-hit rate depends on `$ai_cache_reporting_exclusive` — branch on the event-level flag rather than on provider or model name. Provider behavior and SDK versions drift; the flag is ingestion's resolved answer for that specific event158- When answering "why is X expensive?", show the cost **and** the token split — the user almost always wants to know whether to shrink prompts, shrink outputs, or switch models159- Before building a custom dashboard, check whether the stock `/ai-observability/dashboard` tiles already answer the question — re-creating them is churn160- For large tenants, materialize common cost queries as insights and reuse via `insight-query`; ad-hoc SQL is fine for one-offs but re-running it on every dashboard load is expensive161162## References163164- [cost properties](./references/cost-properties.md) — full property schema, total-cost rationale, event-set rules165- [cost sources](./references/cost-sources.md) — how costs get set at ingestion plus a diagnostic query166- [cache accounting](./references/cache-accounting.md) — exclusive vs inclusive providers, cache-hit-rate formula167- [breakdown patterns](./references/breakdown-patterns.md) — SQL recipes for every common breakdown168- [regression debugging](./references/regression-debugging.md) — 5-step playbook for cost spikes169- [materializing](./references/materializing.md) — insight, dashboard, and alert JSON170
Full transparency — inspect the skill content before installing.