# Exporting Langfuse LLM metrics to the OpenTelemetry Collector

By Akshay Francis · 2026-11-04
Canonical: https://www.infraspec.dev/blog/exporting-langfuse-llm-metrics-to-opentelemetry-collector

Teams building LLM applications often use Langfuse to trace model calls and track cost, latency, and quality. Their dashboards and alerts, however, usually live in a separate monitoring tool such as SigNoz, Grafana, or Prometheus. To track LLM behavior next to application and infrastructure metrics, they need to get Langfuse metrics into that tool.

In this post, we explain why Langfuse metrics cannot reach these tools out of the box and how we solved it.

## What Langfuse is

Langfuse is an observability platform for LLM applications. It captures each model call, including the input prompt, model output, latency, token usage, and cost. It is available as a managed service or can be self-hosted.

Applications send data to Langfuse through its SDKs or via OpenTelemetry. The resulting traces and observations are available in the Langfuse UI and through its public API.

## Data model

Langfuse organizes data into the following entities:

| Entity | Description |
| --- | --- |
| Trace | A single end-to-end request, for example one user question and the response returned by a chatbot. |
| Observation | A single step within a trace, such as a retrieval from a vector database or a model call. Observations can be nested. Langfuse supports several observation types, including span, agent and tool. |
| Generation | The observation type for an LLM call. It records model details, prompt/completion data, token usage, cost, and timing. |
| Score | An evaluation result attached to a trace or observation. Scores can come from user feedback, LLM-as-a-judge evaluators or manual review. |

## The problem

Teams that run LLM applications in production usually already have a monitoring platform, such as SigNoz, Grafana, or Prometheus, for their services and infrastructure. In many of these setups, an OpenTelemetry Collector collects metrics from each source and forwards them to the platform over OTLP.

Langfuse calculates the metrics that describe LLM behavior, such as model cost, token usage, latency, and evaluation scores. These metrics stay inside Langfuse. The OpenTelemetry Collector has no receiver for Langfuse, so it cannot collect them, and they never reach the monitoring platform where the rest of the system's metrics live.

![Original diagram showing the gap between Langfuse and the OpenTelemetry Collector](https://www.infraspec.dev/__l5e/assets-v1/13531bbf-8fc1-4177-856f-32dbc9bcdaa4/langfuse-collector-gap.jpeg)

The OpenTelemetry Collector has no way to read metrics from Langfuse

Our team ran into this gap with SigNoz, which we use for dashboards and alerts. We wanted LLM cost and latency on the same dashboards, and under the same alerting rules, as our service metrics. To get there, we first needed a way to read Langfuse metrics programmatically. Langfuse provides this through its Metrics API.

## How the Langfuse Metrics API works

The Metrics API returns the same aggregated numbers that power the Langfuse dashboards. It has a single endpoint, GET /api/public/v2/metrics. Each request describes what to calculate in a JSON query, passed in the query parameter. A query has five parts:

| Part | Purpose | Example |
| --- | --- | --- |
| view | The data to read from | observations |
| dimensions | The fields to group results by | model name |
| metrics | What to calculate, as a measure and an aggregation | sum of totalCost |
| filters | Optional conditions to narrow the data | only LLM calls ( type = GENERATION ) |
| fromTimestamp, toTimestamp | The time range to cover | one day |

Requests are authenticated with the public and secret key of a Langfuse project. The following example requests the total LLM cost per model for one day:

```
curl -G -u "pk-lf-...:sk-lf-..." \
"https://cloud.langfuse.com/api/public/v2/metrics" \
--data-urlencode 'query={
  "view": "observations",
  "dimensions": [{"field": "providedModelName"}],
  "metrics": [{"measure": "totalCost", "aggregation": "sum"}],
  "filters": [{"column": "type", "operator": "=", "value":
    "GENERATION", "type": "string"}],
  "fromTimestamp": "2026-10-15T00:00:00Z",
 "toTimestamp": "2026-10-16T00:00:00Z"
}'
```

The API returns the result as a list of JSON rows. Each row contains the grouping values and one field for each calculated metric, named <aggregation>_<measure>, such as sum_totalCost:

```
{
  "data": [
    {"providedModelName": "gpt-4o", "sum_totalCost": 0.4213},
    {"providedModelName": "gpt-4o-mini", "sum_totalCost": 0.0388}
  ]
}
```

This gives us the data we need. The next question is whether the OpenTelemetry Collector can read it.

## How the OpenTelemetry Collector collects metrics

To collect metrics from a service over HTTP, the OpenTelemetry Collector uses the Prometheus receiver. The receiver scrapes a /metrics endpoint at a fixed interval with a plain GET request. It expects the response in the Prometheus text format, where each line has a metric name, a set of labels, and a value:

```
# HELP langfuse_total_cost_usd Total cost in USD in the window.
# TYPE langfuse_total_cost_usd gauge
langfuse_total_cost_usd{model="gpt-4o"} 0.4213
langfuse_total_cost_usd{model="gpt-4o-mini"} 0.0388
```

This example holds the same data as the JSON response above, in the format the Collector can read.

## The gap

The Metrics API and the Prometheus receiver expect different things:

|  | Langfuse Metrics API | Prometheus receiver |
| --- | --- | --- |
| Request | A JSON query in each request | A plain GET /metrics with no parameters |
| Time range | Set by the caller in every request | Not used; the receiver reads current values |
| Authentication | Basic auth with a key pair per Langfuse project | Usually none |
| Response | JSON rows | Prometheus text format |

As a result, the Collector cannot scrape the Metrics API directly, and Langfuse does not currently expose these metrics in Prometheus format. Native Prometheus support has been requested by the community in langfuse#2508 and langfuse#3551.

To connect the two, we needed a service that sits between Langfuse and the Collector. The service has to:

1. Query the Metrics API on a schedule, using each project's API keys.
1. Convert the JSON rows into metrics with names, labels, and values.
1. Serve those metrics on a /metrics endpoint for the Collector to scrape.

We built this service as a custom exporter. The next section explains how it works.

## How the exporter works

The Langfuse Metrics API Prometheus Exporter is a small Python service with no third-party dependencies. It takes two inputs: a list of Langfuse projects with their API keys, and a list of queries that defines which metrics to export.

The exporter queries the Langfuse Metrics API, caches the result and serves it to the OpenTelemetry Collector, which forwards it to SigNoz.

![Original exporter architecture from the PDF](https://www.infraspec.dev/__l5e/assets-v1/66cf0ba1-dfc2-4267-af1b-873fc07dc9fd/langfuse-exporter-flow.jpeg)

The exporter sits between the Langfuse Metrics API and the OpenTelemetry Collector

The exporter works in three steps:

1. **Query Langfuse on a schedule.** Every 60 seconds by default, the exporter runs each configured query for each Langfuse project. Because the Metrics API always needs a time range, each query asks for a rolling window that ends at the current time, such as the last 24 hours. The result is a value such as "cost in the last 24 hours", refreshed every minute.
1. **Convert the results to metrics.** Each row in the response becomes one metric sample. The grouping fields, such as model or environment, become labels, and the aggregated number becomes the metric value. The example below shows this conversion.
1. **Serve the latest results.** The exporter keeps the latest results in memory and serves them on /metrics. When the Collector scrapes the endpoint, the exporter returns these cached results.

For example, a Metrics API row for cost per environment, trace name, and model:

```
{"environment": "production", "traceName": "answer-question",
  "providedModelName": "gpt-4o", "sum_totalCost": 0.4213}
```

becomes this sample:

```
langfuse_total_cost_usd{project="chatbot",window="86400s",environment="production",trace_name="answer-question",model="gpt-4o"} 0.4213
```

The metrics to export are defined in a configuration file, queries.json, rather than in code. Each query in the file describes one Metrics API request: what to group by, what to measure, and the metric name and labels to publish the result under. The exporter ships with a default set of queries that covers the most common LLM metrics: cost, token usage, latency, errors, and evaluation scores. To export a different metric, you add a query to the file.

The exporter also publishes metrics about its own health. After each query, it records langfuse_exporter_query_success, set to 1 if the query succeeded and 0 if it failed. If one query fails, the others keep running, so you can alert on this metric to catch a failing query before its data goes missing from your dashboards.

## Results

Once the Collector scrapes the exporter, Langfuse metrics show up in SigNoz like any other metric. The dashboard below is an example built from them. It shows request volume, latency, cost, and token usage for each application, along with the health of the exporter's own queries.

![SigNoz dashboard built from Langfuse metrics exported by the exporter](https://www.infraspec.dev/__l5e/assets-v1/9402e414-5a95-4c0f-ab4a-5ce4f1dd32ee/langfuse-dashboard.jpeg)

A SigNoz dashboard built from Langfuse metrics exported by the exporter

You can extend it by adding or modifying queries in `queries.json`, then build your own panels and alerts on the new metrics.

## Limitations

The exporter has a few constraints.

- **Up to 1,000 rows per query.** The Metrics API returns at most 1,000 rows per query, and each row represents one unique combination of the fields you group by. Grouping by a field with a few values, such as model or environment, keeps the result small. Grouping by a field with thousands of values, such as user ID or session ID, can exceed the limit. When that happens, the extra rows are dropped and the exporter logs a warning. Group by fields with a small, stable set of values.
- **Fixed time windows.** Each value is an aggregate over a rolling window, such as the last 24 hours, and is exported as a gauge. You cannot apply rate() or increase() to these values, or re-aggregate them over a different time range in your backend. Configure each window you need in the exporter instead.
- **API load grows with configuration.** Each refresh makes one API call per project, window, and query. With 7 queries, 2 projects, and 1 window, that is 14 calls per refresh. Adding windows or projects multiplies this number.

## Conclusion

Langfuse calculates the metrics that matter for LLM applications: cost, token usage, latency, errors, and evaluation scores. However, it exposes them only through a JSON API that the OpenTelemetry Collector cannot scrape. Our exporter bridges this gap. It queries the Langfuse Metrics API on a schedule, converts the results into Prometheus metrics, and serves them for the Collector to scrape.

With the exporter in place, LLM metrics sit in SigNoz next to our service and infrastructure metrics, on the same dashboards and under the same alerting rules. When an alert fires, SigNoz shows what changed, and Langfuse shows the traces behind it.

The exporter is available on GitHub. The README covers setup, configuration, custom queries, and deployment with Docker and Kubernetes.

## References

- Langfuse Metrics API: the API the exporter queries
- Langfuse observability data model: traces, observations, and generations
- **Prometheus receiver for the OpenTelemetry Collector**: how the Collector scrapes the exporter
- **Prometheus exposition formats**: the text format the exporter produces
- **Langfuse Metrics API Prometheus Exporter**: source code
