diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py
index c958219d5..9ec280087 100644
--- a/langfuse/api/__init__.py
+++ b/langfuse/api/__init__.py
@@ -117,6 +117,7 @@
MembershipsResponse,
MethodNotAllowedError,
MetricsResponse,
+ MetricsV2Response,
Model,
ModelPrice,
ModelUsageUnit,
@@ -128,6 +129,8 @@
ObservationLevel,
ObservationType,
Observations,
+ ObservationsV2Meta,
+ ObservationsV2Response,
ObservationsView,
ObservationsViews,
OpenAiCompletionUsageSchema,
@@ -234,8 +237,10 @@
llm_connections,
media,
metrics,
+ metrics_v_2,
models,
observations,
+ observations_v_2,
opentelemetry,
organizations,
projects,
@@ -367,6 +372,7 @@
"MembershipsResponse",
"MethodNotAllowedError",
"MetricsResponse",
+ "MetricsV2Response",
"Model",
"ModelPrice",
"ModelUsageUnit",
@@ -378,6 +384,8 @@
"ObservationLevel",
"ObservationType",
"Observations",
+ "ObservationsV2Meta",
+ "ObservationsV2Response",
"ObservationsView",
"ObservationsViews",
"OpenAiCompletionUsageSchema",
@@ -484,8 +492,10 @@
"llm_connections",
"media",
"metrics",
+ "metrics_v_2",
"models",
"observations",
+ "observations_v_2",
"opentelemetry",
"organizations",
"projects",
diff --git a/langfuse/api/client.py b/langfuse/api/client.py
index 646279b5a..09674e979 100644
--- a/langfuse/api/client.py
+++ b/langfuse/api/client.py
@@ -28,8 +28,13 @@
)
from .resources.media.client import AsyncMediaClient, MediaClient
from .resources.metrics.client import AsyncMetricsClient, MetricsClient
+from .resources.metrics_v_2.client import AsyncMetricsV2Client, MetricsV2Client
from .resources.models.client import AsyncModelsClient, ModelsClient
from .resources.observations.client import AsyncObservationsClient, ObservationsClient
+from .resources.observations_v_2.client import (
+ AsyncObservationsV2Client,
+ ObservationsV2Client,
+)
from .resources.opentelemetry.client import (
AsyncOpentelemetryClient,
OpentelemetryClient,
@@ -137,8 +142,12 @@ def __init__(
self.ingestion = IngestionClient(client_wrapper=self._client_wrapper)
self.llm_connections = LlmConnectionsClient(client_wrapper=self._client_wrapper)
self.media = MediaClient(client_wrapper=self._client_wrapper)
+ self.metrics_v_2 = MetricsV2Client(client_wrapper=self._client_wrapper)
self.metrics = MetricsClient(client_wrapper=self._client_wrapper)
self.models = ModelsClient(client_wrapper=self._client_wrapper)
+ self.observations_v_2 = ObservationsV2Client(
+ client_wrapper=self._client_wrapper
+ )
self.observations = ObservationsClient(client_wrapper=self._client_wrapper)
self.opentelemetry = OpentelemetryClient(client_wrapper=self._client_wrapper)
self.organizations = OrganizationsClient(client_wrapper=self._client_wrapper)
@@ -242,8 +251,12 @@ def __init__(
client_wrapper=self._client_wrapper
)
self.media = AsyncMediaClient(client_wrapper=self._client_wrapper)
+ self.metrics_v_2 = AsyncMetricsV2Client(client_wrapper=self._client_wrapper)
self.metrics = AsyncMetricsClient(client_wrapper=self._client_wrapper)
self.models = AsyncModelsClient(client_wrapper=self._client_wrapper)
+ self.observations_v_2 = AsyncObservationsV2Client(
+ client_wrapper=self._client_wrapper
+ )
self.observations = AsyncObservationsClient(client_wrapper=self._client_wrapper)
self.opentelemetry = AsyncOpentelemetryClient(
client_wrapper=self._client_wrapper
diff --git a/langfuse/api/reference.md b/langfuse/api/reference.md
index 66c008bb7..b84696be4 100644
--- a/langfuse/api/reference.md
+++ b/langfuse/api/reference.md
@@ -2946,6 +2946,232 @@ client.media.get_upload_url(
+
+
+
+
+## MetricsV2
+client.metrics_v_2.metrics(...)
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance.
+
+## V2 Differences
+- Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported)
+- Direct access to tags and release fields on observations
+- Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view
+- High cardinality dimensions are not supported and will return a 400 error (see below)
+
+For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api).
+
+## Available Views
+
+### observations
+Query observation-level data (spans, generations, events).
+
+**Dimensions:**
+- `environment` - Deployment environment (e.g., production, staging)
+- `type` - Type of observation (SPAN, GENERATION, EVENT)
+- `name` - Name of the observation
+- `level` - Logging level of the observation
+- `version` - Version of the observation
+- `tags` - User-defined tags
+- `release` - Release version
+- `traceName` - Name of the parent trace (backwards-compatible)
+- `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release)
+- `traceVersion` - Version of the parent trace (backwards-compatible, maps to version)
+- `providedModelName` - Name of the model used
+- `promptName` - Name of the prompt used
+- `promptVersion` - Version of the prompt used
+- `startTimeMonth` - Month of start_time in YYYY-MM format
+
+**Measures:**
+- `count` - Total number of observations
+- `latency` - Observation latency (milliseconds)
+- `streamingLatency` - Generation latency from completion start to end (milliseconds)
+- `inputTokens` - Sum of input tokens consumed
+- `outputTokens` - Sum of output tokens produced
+- `totalTokens` - Sum of all tokens consumed
+- `outputTokensPerSecond` - Output tokens per second
+- `tokensPerSecond` - Total tokens per second
+- `inputCost` - Input cost (USD)
+- `outputCost` - Output cost (USD)
+- `totalCost` - Total cost (USD)
+- `timeToFirstToken` - Time to first token (milliseconds)
+- `countScores` - Number of scores attached to the observation
+
+### scores-numeric
+Query numeric and boolean score data.
+
+**Dimensions:**
+- `environment` - Deployment environment
+- `name` - Name of the score (e.g., accuracy, toxicity)
+- `source` - Origin of the score (API, ANNOTATION, EVAL)
+- `dataType` - Data type (NUMERIC, BOOLEAN)
+- `configId` - Identifier of the score config
+- `timestampMonth` - Month in YYYY-MM format
+- `timestampDay` - Day in YYYY-MM-DD format
+- `value` - Numeric value of the score
+- `traceName` - Name of the parent trace
+- `tags` - Tags
+- `traceRelease` - Release version
+- `traceVersion` - Version
+- `observationName` - Name of the associated observation
+- `observationModelName` - Model name of the associated observation
+- `observationPromptName` - Prompt name of the associated observation
+- `observationPromptVersion` - Prompt version of the associated observation
+
+**Measures:**
+- `count` - Total number of scores
+- `value` - Score value (for aggregations)
+
+### scores-categorical
+Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`.
+
+**Measures:**
+- `count` - Total number of scores
+
+## High Cardinality Dimensions
+The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues.
+Use them in filters instead.
+
+**observations view:**
+- `id` - Use traceId filter to narrow down results
+- `traceId` - Use traceId filter instead
+- `userId` - Use userId filter instead
+- `sessionId` - Use sessionId filter instead
+- `parentObservationId` - Use parentObservationId filter instead
+
+**scores-numeric / scores-categorical views:**
+- `id` - Use specific filters to narrow down results
+- `traceId` - Use traceId filter instead
+- `userId` - Use userId filter instead
+- `sessionId` - Use sessionId filter instead
+- `observationId` - Use observationId filter instead
+
+## Aggregations
+Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram`
+
+## Time Granularities
+Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month`
+- `auto` bins the data into approximately 50 buckets based on the time range
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from langfuse.client import FernLangfuse
+
+client = FernLangfuse(
+ x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
+ x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
+ x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
+ username="YOUR_USERNAME",
+ password="YOUR_PASSWORD",
+ base_url="https://yourhost.com/path/to/api",
+)
+client.metrics_v_2.metrics(
+ query="query",
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**query:** `str`
+
+JSON string containing the query parameters with the following structure:
+```json
+{
+ "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical"
+ "dimensions": [ // Optional. Default: []
+ {
+ "field": string // Field to group by (see available dimensions above)
+ }
+ ],
+ "metrics": [ // Required. At least one metric must be provided
+ {
+ "measure": string, // What to measure (see available measures above)
+ "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram"
+ }
+ ],
+ "filters": [ // Optional. Default: []
+ {
+ "column": string, // Column to filter on (any dimension field)
+ "operator": string, // Operator based on type:
+ // - datetime: ">", "<", ">=", "<="
+ // - string: "=", "contains", "does not contain", "starts with", "ends with"
+ // - stringOptions: "any of", "none of"
+ // - arrayOptions: "any of", "none of", "all of"
+ // - number: "=", ">", "<", ">=", "<="
+ // - stringObject/numberObject: same as string/number with required "key"
+ // - boolean: "=", "<>"
+ // - null: "is null", "is not null"
+ "value": any, // Value to compare against
+ "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
+ "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering)
+ }
+ ],
+ "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time
+ "granularity": string // One of "auto", "minute", "hour", "day", "week", "month"
+ },
+ "fromTimestamp": string, // Required. ISO datetime string for start of time range
+ "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp)
+ "orderBy": [ // Optional. Default: null
+ {
+ "field": string, // Field to order by (dimension or metric alias)
+ "direction": string // "asc" or "desc"
+ }
+ ],
+ "config": { // Optional. Query-specific configuration
+ "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10
+ "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100
+ }
+}
+```
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
@@ -3377,6 +3603,318 @@ client.models.delete(
+
+
+
+
+## ObservationsV2
+client.observations_v_2.get_many(...)
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Get a list of observations with cursor-based pagination and flexible field selection.
+
+## Cursor-based Pagination
+This endpoint uses cursor-based pagination for efficient traversal of large datasets.
+The cursor is returned in the response metadata and should be passed in subsequent requests
+to retrieve the next page of results.
+
+## Field Selection
+Use the `fields` parameter to control which observation fields are returned:
+- `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type
+- `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId
+- `time` - completionStartTime, createdAt, updatedAt
+- `io` - input, output
+- `metadata` - metadata
+- `model` - providedModelName, internalModelId, modelParameters
+- `usage` - usageDetails, costDetails, totalCost
+- `prompt` - promptId, promptName, promptVersion
+- `metrics` - latency, timeToFirstToken
+
+If not specified, `core` and `basic` field groups are returned.
+
+## Filters
+Multiple filtering options are available via query parameters or the structured `filter` parameter.
+When using the `filter` parameter, it takes precedence over individual query parameter filters.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from langfuse.client import FernLangfuse
+
+client = FernLangfuse(
+ x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
+ x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
+ x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
+ username="YOUR_USERNAME",
+ password="YOUR_PASSWORD",
+ base_url="https://yourhost.com/path/to/api",
+)
+client.observations_v_2.get_many()
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**fields:** `typing.Optional[str]`
+
+Comma-separated list of field groups to include in the response.
+Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics.
+If not specified, `core` and `basic` field groups are returned.
+Example: "basic,usage,model"
+
+
+
+
+
+-
+
+**limit:** `typing.Optional[int]` — Number of items to return per page. Maximum 1000, default 50.
+
+
+
+
+
+-
+
+**cursor:** `typing.Optional[str]` — Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page.
+
+
+
+
+
+-
+
+**parse_io_as_json:** `typing.Optional[bool]`
+
+Set to `true` to parse input/output fields as JSON, or `false` to return raw strings.
+Defaults to `false` if not provided.
+
+
+
+
+
+-
+
+**name:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**user_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**type:** `typing.Optional[str]` — Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL")
+
+
+
+
+
+-
+
+**trace_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**level:** `typing.Optional[ObservationLevel]` — Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR").
+
+
+
+
+
+-
+
+**parent_observation_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**environment:** `typing.Optional[typing.Union[str, typing.Sequence[str]]]` — Optional filter for observations where the environment is one of the provided values.
+
+
+
+
+
+-
+
+**from_start_time:** `typing.Optional[dt.datetime]` — Retrieve only observations with a start_time on or after this datetime (ISO 8601).
+
+
+
+
+
+-
+
+**to_start_time:** `typing.Optional[dt.datetime]` — Retrieve only observations with a start_time before this datetime (ISO 8601).
+
+
+
+
+
+-
+
+**version:** `typing.Optional[str]` — Optional filter to only include observations with a certain version.
+
+
+
+
+
+-
+
+**filter:** `typing.Optional[str]`
+
+JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...).
+
+## Filter Structure
+Each filter condition has the following structure:
+```json
+[
+ {
+ "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
+ "column": string, // Required. Column to filter on (see available columns below)
+ "operator": string, // Required. Operator based on type:
+ // - datetime: ">", "<", ">=", "<="
+ // - string: "=", "contains", "does not contain", "starts with", "ends with"
+ // - stringOptions: "any of", "none of"
+ // - categoryOptions: "any of", "none of"
+ // - arrayOptions: "any of", "none of", "all of"
+ // - number: "=", ">", "<", ">=", "<="
+ // - stringObject: "=", "contains", "does not contain", "starts with", "ends with"
+ // - numberObject: "=", ">", "<", ">=", "<="
+ // - boolean: "=", "<>"
+ // - null: "is null", "is not null"
+ "value": any, // Required (except for null type). Value to compare against. Type depends on filter type
+ "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata
+ }
+]
+```
+
+## Available Columns
+
+### Core Observation Fields
+- `id` (string) - Observation ID
+- `type` (string) - Observation type (SPAN, GENERATION, EVENT)
+- `name` (string) - Observation name
+- `traceId` (string) - Associated trace ID
+- `startTime` (datetime) - Observation start time
+- `endTime` (datetime) - Observation end time
+- `environment` (string) - Environment tag
+- `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR)
+- `statusMessage` (string) - Status message
+- `version` (string) - Version tag
+- `userId` (string) - User ID
+- `sessionId` (string) - Session ID
+
+### Trace-Related Fields
+- `traceName` (string) - Name of the parent trace
+- `traceTags` (arrayOptions) - Tags from the parent trace
+- `tags` (arrayOptions) - Alias for traceTags
+
+### Performance Metrics
+- `latency` (number) - Latency in seconds (calculated: end_time - start_time)
+- `timeToFirstToken` (number) - Time to first token in seconds
+- `tokensPerSecond` (number) - Output tokens per second
+
+### Token Usage
+- `inputTokens` (number) - Number of input tokens
+- `outputTokens` (number) - Number of output tokens
+- `totalTokens` (number) - Total tokens (alias: `tokens`)
+
+### Cost Metrics
+- `inputCost` (number) - Input cost in USD
+- `outputCost` (number) - Output cost in USD
+- `totalCost` (number) - Total cost in USD
+
+### Model Information
+- `model` (string) - Provided model name (alias: `providedModelName`)
+- `promptName` (string) - Associated prompt name
+- `promptVersion` (number) - Associated prompt version
+
+### Structured Data
+- `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys.
+
+## Filter Examples
+```json
+[
+ {
+ "type": "string",
+ "column": "type",
+ "operator": "=",
+ "value": "GENERATION"
+ },
+ {
+ "type": "number",
+ "column": "latency",
+ "operator": ">=",
+ "value": 2.5
+ },
+ {
+ "type": "stringObject",
+ "column": "metadata",
+ "key": "environment",
+ "operator": "=",
+ "value": "production"
+ }
+]
+```
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
diff --git a/langfuse/api/resources/__init__.py b/langfuse/api/resources/__init__.py
index d91362a28..1095119a7 100644
--- a/langfuse/api/resources/__init__.py
+++ b/langfuse/api/resources/__init__.py
@@ -13,8 +13,10 @@
llm_connections,
media,
metrics,
+ metrics_v_2,
models,
observations,
+ observations_v_2,
opentelemetry,
organizations,
projects,
@@ -178,8 +180,10 @@
PatchMediaBody,
)
from .metrics import MetricsResponse
+from .metrics_v_2 import MetricsV2Response
from .models import CreateModelRequest, PaginatedModels
from .observations import Observations, ObservationsViews
+from .observations_v_2 import ObservationsV2Meta, ObservationsV2Response
from .opentelemetry import (
OtelAttribute,
OtelAttributeValue,
@@ -388,6 +392,7 @@
"MembershipsResponse",
"MethodNotAllowedError",
"MetricsResponse",
+ "MetricsV2Response",
"Model",
"ModelPrice",
"ModelUsageUnit",
@@ -399,6 +404,8 @@
"ObservationLevel",
"ObservationType",
"Observations",
+ "ObservationsV2Meta",
+ "ObservationsV2Response",
"ObservationsView",
"ObservationsViews",
"OpenAiCompletionUsageSchema",
@@ -505,8 +512,10 @@
"llm_connections",
"media",
"metrics",
+ "metrics_v_2",
"models",
"observations",
+ "observations_v_2",
"opentelemetry",
"organizations",
"projects",
diff --git a/langfuse/api/resources/metrics_v_2/__init__.py b/langfuse/api/resources/metrics_v_2/__init__.py
new file mode 100644
index 000000000..a8c9304a6
--- /dev/null
+++ b/langfuse/api/resources/metrics_v_2/__init__.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+from .types import MetricsV2Response
+
+__all__ = ["MetricsV2Response"]
diff --git a/langfuse/api/resources/metrics_v_2/client.py b/langfuse/api/resources/metrics_v_2/client.py
new file mode 100644
index 000000000..4628c4d61
--- /dev/null
+++ b/langfuse/api/resources/metrics_v_2/client.py
@@ -0,0 +1,461 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.pydantic_utilities import pydantic_v1
+from ...core.request_options import RequestOptions
+from ..commons.errors.access_denied_error import AccessDeniedError
+from ..commons.errors.error import Error
+from ..commons.errors.method_not_allowed_error import MethodNotAllowedError
+from ..commons.errors.not_found_error import NotFoundError
+from ..commons.errors.unauthorized_error import UnauthorizedError
+from .types.metrics_v_2_response import MetricsV2Response
+
+
+class MetricsV2Client:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def metrics(
+ self, *, query: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> MetricsV2Response:
+ """
+ Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance.
+
+ ## V2 Differences
+ - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported)
+ - Direct access to tags and release fields on observations
+ - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view
+ - High cardinality dimensions are not supported and will return a 400 error (see below)
+
+ For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api).
+
+ ## Available Views
+
+ ### observations
+ Query observation-level data (spans, generations, events).
+
+ **Dimensions:**
+ - `environment` - Deployment environment (e.g., production, staging)
+ - `type` - Type of observation (SPAN, GENERATION, EVENT)
+ - `name` - Name of the observation
+ - `level` - Logging level of the observation
+ - `version` - Version of the observation
+ - `tags` - User-defined tags
+ - `release` - Release version
+ - `traceName` - Name of the parent trace (backwards-compatible)
+ - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release)
+ - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version)
+ - `providedModelName` - Name of the model used
+ - `promptName` - Name of the prompt used
+ - `promptVersion` - Version of the prompt used
+ - `startTimeMonth` - Month of start_time in YYYY-MM format
+
+ **Measures:**
+ - `count` - Total number of observations
+ - `latency` - Observation latency (milliseconds)
+ - `streamingLatency` - Generation latency from completion start to end (milliseconds)
+ - `inputTokens` - Sum of input tokens consumed
+ - `outputTokens` - Sum of output tokens produced
+ - `totalTokens` - Sum of all tokens consumed
+ - `outputTokensPerSecond` - Output tokens per second
+ - `tokensPerSecond` - Total tokens per second
+ - `inputCost` - Input cost (USD)
+ - `outputCost` - Output cost (USD)
+ - `totalCost` - Total cost (USD)
+ - `timeToFirstToken` - Time to first token (milliseconds)
+ - `countScores` - Number of scores attached to the observation
+
+ ### scores-numeric
+ Query numeric and boolean score data.
+
+ **Dimensions:**
+ - `environment` - Deployment environment
+ - `name` - Name of the score (e.g., accuracy, toxicity)
+ - `source` - Origin of the score (API, ANNOTATION, EVAL)
+ - `dataType` - Data type (NUMERIC, BOOLEAN)
+ - `configId` - Identifier of the score config
+ - `timestampMonth` - Month in YYYY-MM format
+ - `timestampDay` - Day in YYYY-MM-DD format
+ - `value` - Numeric value of the score
+ - `traceName` - Name of the parent trace
+ - `tags` - Tags
+ - `traceRelease` - Release version
+ - `traceVersion` - Version
+ - `observationName` - Name of the associated observation
+ - `observationModelName` - Model name of the associated observation
+ - `observationPromptName` - Prompt name of the associated observation
+ - `observationPromptVersion` - Prompt version of the associated observation
+
+ **Measures:**
+ - `count` - Total number of scores
+ - `value` - Score value (for aggregations)
+
+ ### scores-categorical
+ Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`.
+
+ **Measures:**
+ - `count` - Total number of scores
+
+ ## High Cardinality Dimensions
+ The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues.
+ Use them in filters instead.
+
+ **observations view:**
+ - `id` - Use traceId filter to narrow down results
+ - `traceId` - Use traceId filter instead
+ - `userId` - Use userId filter instead
+ - `sessionId` - Use sessionId filter instead
+ - `parentObservationId` - Use parentObservationId filter instead
+
+ **scores-numeric / scores-categorical views:**
+ - `id` - Use specific filters to narrow down results
+ - `traceId` - Use traceId filter instead
+ - `userId` - Use userId filter instead
+ - `sessionId` - Use sessionId filter instead
+ - `observationId` - Use observationId filter instead
+
+ ## Aggregations
+ Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram`
+
+ ## Time Granularities
+ Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month`
+ - `auto` bins the data into approximately 50 buckets based on the time range
+
+ Parameters
+ ----------
+ query : str
+ JSON string containing the query parameters with the following structure:
+ ```json
+ {
+ "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical"
+ "dimensions": [ // Optional. Default: []
+ {
+ "field": string // Field to group by (see available dimensions above)
+ }
+ ],
+ "metrics": [ // Required. At least one metric must be provided
+ {
+ "measure": string, // What to measure (see available measures above)
+ "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram"
+ }
+ ],
+ "filters": [ // Optional. Default: []
+ {
+ "column": string, // Column to filter on (any dimension field)
+ "operator": string, // Operator based on type:
+ // - datetime: ">", "<", ">=", "<="
+ // - string: "=", "contains", "does not contain", "starts with", "ends with"
+ // - stringOptions: "any of", "none of"
+ // - arrayOptions: "any of", "none of", "all of"
+ // - number: "=", ">", "<", ">=", "<="
+ // - stringObject/numberObject: same as string/number with required "key"
+ // - boolean: "=", "<>"
+ // - null: "is null", "is not null"
+ "value": any, // Value to compare against
+ "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
+ "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering)
+ }
+ ],
+ "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time
+ "granularity": string // One of "auto", "minute", "hour", "day", "week", "month"
+ },
+ "fromTimestamp": string, // Required. ISO datetime string for start of time range
+ "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp)
+ "orderBy": [ // Optional. Default: null
+ {
+ "field": string, // Field to order by (dimension or metric alias)
+ "direction": string // "asc" or "desc"
+ }
+ ],
+ "config": { // Optional. Query-specific configuration
+ "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10
+ "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100
+ }
+ }
+ ```
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ MetricsV2Response
+
+ Examples
+ --------
+ from langfuse.client import FernLangfuse
+
+ client = FernLangfuse(
+ x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
+ x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
+ x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
+ username="YOUR_USERNAME",
+ password="YOUR_PASSWORD",
+ base_url="https://yourhost.com/path/to/api",
+ )
+ client.metrics_v_2.metrics(
+ query="query",
+ )
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "api/public/v2/metrics",
+ method="GET",
+ params={"query": query},
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return pydantic_v1.parse_obj_as(MetricsV2Response, _response.json()) # type: ignore
+ if _response.status_code == 400:
+ raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 403:
+ raise AccessDeniedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 405:
+ raise MethodNotAllowedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 404:
+ raise NotFoundError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, body=_response.text)
+ raise ApiError(status_code=_response.status_code, body=_response_json)
+
+
+class AsyncMetricsV2Client:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def metrics(
+ self, *, query: str, request_options: typing.Optional[RequestOptions] = None
+ ) -> MetricsV2Response:
+ """
+ Get metrics from the Langfuse project using a query object. V2 endpoint with optimized performance.
+
+ ## V2 Differences
+ - Supports `observations`, `scores-numeric`, and `scores-categorical` views only (traces view not supported)
+ - Direct access to tags and release fields on observations
+ - Backwards-compatible: traceName, traceRelease, traceVersion dimensions are still available on observations view
+ - High cardinality dimensions are not supported and will return a 400 error (see below)
+
+ For more details, see the [Metrics API documentation](https://langfuse.com/docs/metrics/features/metrics-api).
+
+ ## Available Views
+
+ ### observations
+ Query observation-level data (spans, generations, events).
+
+ **Dimensions:**
+ - `environment` - Deployment environment (e.g., production, staging)
+ - `type` - Type of observation (SPAN, GENERATION, EVENT)
+ - `name` - Name of the observation
+ - `level` - Logging level of the observation
+ - `version` - Version of the observation
+ - `tags` - User-defined tags
+ - `release` - Release version
+ - `traceName` - Name of the parent trace (backwards-compatible)
+ - `traceRelease` - Release version of the parent trace (backwards-compatible, maps to release)
+ - `traceVersion` - Version of the parent trace (backwards-compatible, maps to version)
+ - `providedModelName` - Name of the model used
+ - `promptName` - Name of the prompt used
+ - `promptVersion` - Version of the prompt used
+ - `startTimeMonth` - Month of start_time in YYYY-MM format
+
+ **Measures:**
+ - `count` - Total number of observations
+ - `latency` - Observation latency (milliseconds)
+ - `streamingLatency` - Generation latency from completion start to end (milliseconds)
+ - `inputTokens` - Sum of input tokens consumed
+ - `outputTokens` - Sum of output tokens produced
+ - `totalTokens` - Sum of all tokens consumed
+ - `outputTokensPerSecond` - Output tokens per second
+ - `tokensPerSecond` - Total tokens per second
+ - `inputCost` - Input cost (USD)
+ - `outputCost` - Output cost (USD)
+ - `totalCost` - Total cost (USD)
+ - `timeToFirstToken` - Time to first token (milliseconds)
+ - `countScores` - Number of scores attached to the observation
+
+ ### scores-numeric
+ Query numeric and boolean score data.
+
+ **Dimensions:**
+ - `environment` - Deployment environment
+ - `name` - Name of the score (e.g., accuracy, toxicity)
+ - `source` - Origin of the score (API, ANNOTATION, EVAL)
+ - `dataType` - Data type (NUMERIC, BOOLEAN)
+ - `configId` - Identifier of the score config
+ - `timestampMonth` - Month in YYYY-MM format
+ - `timestampDay` - Day in YYYY-MM-DD format
+ - `value` - Numeric value of the score
+ - `traceName` - Name of the parent trace
+ - `tags` - Tags
+ - `traceRelease` - Release version
+ - `traceVersion` - Version
+ - `observationName` - Name of the associated observation
+ - `observationModelName` - Model name of the associated observation
+ - `observationPromptName` - Prompt name of the associated observation
+ - `observationPromptVersion` - Prompt version of the associated observation
+
+ **Measures:**
+ - `count` - Total number of scores
+ - `value` - Score value (for aggregations)
+
+ ### scores-categorical
+ Query categorical score data. Same dimensions as scores-numeric except uses `stringValue` instead of `value`.
+
+ **Measures:**
+ - `count` - Total number of scores
+
+ ## High Cardinality Dimensions
+ The following dimensions cannot be used as grouping dimensions in v2 metrics API as they can cause performance issues.
+ Use them in filters instead.
+
+ **observations view:**
+ - `id` - Use traceId filter to narrow down results
+ - `traceId` - Use traceId filter instead
+ - `userId` - Use userId filter instead
+ - `sessionId` - Use sessionId filter instead
+ - `parentObservationId` - Use parentObservationId filter instead
+
+ **scores-numeric / scores-categorical views:**
+ - `id` - Use specific filters to narrow down results
+ - `traceId` - Use traceId filter instead
+ - `userId` - Use userId filter instead
+ - `sessionId` - Use sessionId filter instead
+ - `observationId` - Use observationId filter instead
+
+ ## Aggregations
+ Available aggregation functions: `sum`, `avg`, `count`, `max`, `min`, `p50`, `p75`, `p90`, `p95`, `p99`, `histogram`
+
+ ## Time Granularities
+ Available granularities for timeDimension: `auto`, `minute`, `hour`, `day`, `week`, `month`
+ - `auto` bins the data into approximately 50 buckets based on the time range
+
+ Parameters
+ ----------
+ query : str
+ JSON string containing the query parameters with the following structure:
+ ```json
+ {
+ "view": string, // Required. One of "observations", "scores-numeric", "scores-categorical"
+ "dimensions": [ // Optional. Default: []
+ {
+ "field": string // Field to group by (see available dimensions above)
+ }
+ ],
+ "metrics": [ // Required. At least one metric must be provided
+ {
+ "measure": string, // What to measure (see available measures above)
+ "aggregation": string // How to aggregate: "sum", "avg", "count", "max", "min", "p50", "p75", "p90", "p95", "p99", "histogram"
+ }
+ ],
+ "filters": [ // Optional. Default: []
+ {
+ "column": string, // Column to filter on (any dimension field)
+ "operator": string, // Operator based on type:
+ // - datetime: ">", "<", ">=", "<="
+ // - string: "=", "contains", "does not contain", "starts with", "ends with"
+ // - stringOptions: "any of", "none of"
+ // - arrayOptions: "any of", "none of", "all of"
+ // - number: "=", ">", "<", ">=", "<="
+ // - stringObject/numberObject: same as string/number with required "key"
+ // - boolean: "=", "<>"
+ // - null: "is null", "is not null"
+ "value": any, // Value to compare against
+ "type": string, // Data type: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
+ "key": string // Required only for stringObject/numberObject types (e.g., metadata filtering)
+ }
+ ],
+ "timeDimension": { // Optional. Default: null. If provided, results will be grouped by time
+ "granularity": string // One of "auto", "minute", "hour", "day", "week", "month"
+ },
+ "fromTimestamp": string, // Required. ISO datetime string for start of time range
+ "toTimestamp": string, // Required. ISO datetime string for end of time range (must be after fromTimestamp)
+ "orderBy": [ // Optional. Default: null
+ {
+ "field": string, // Field to order by (dimension or metric alias)
+ "direction": string // "asc" or "desc"
+ }
+ ],
+ "config": { // Optional. Query-specific configuration
+ "bins": number, // Optional. Number of bins for histogram aggregation (1-100), default: 10
+ "row_limit": number // Optional. Maximum number of rows to return (1-1000), default: 100
+ }
+ }
+ ```
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ MetricsV2Response
+
+ Examples
+ --------
+ import asyncio
+
+ from langfuse.client import AsyncFernLangfuse
+
+ client = AsyncFernLangfuse(
+ x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
+ x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
+ x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
+ username="YOUR_USERNAME",
+ password="YOUR_PASSWORD",
+ base_url="https://yourhost.com/path/to/api",
+ )
+
+
+ async def main() -> None:
+ await client.metrics_v_2.metrics(
+ query="query",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "api/public/v2/metrics",
+ method="GET",
+ params={"query": query},
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return pydantic_v1.parse_obj_as(MetricsV2Response, _response.json()) # type: ignore
+ if _response.status_code == 400:
+ raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 403:
+ raise AccessDeniedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 405:
+ raise MethodNotAllowedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 404:
+ raise NotFoundError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, body=_response.text)
+ raise ApiError(status_code=_response.status_code, body=_response_json)
diff --git a/langfuse/api/resources/metrics_v_2/types/__init__.py b/langfuse/api/resources/metrics_v_2/types/__init__.py
new file mode 100644
index 000000000..b77cf3d4d
--- /dev/null
+++ b/langfuse/api/resources/metrics_v_2/types/__init__.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+from .metrics_v_2_response import MetricsV2Response
+
+__all__ = ["MetricsV2Response"]
diff --git a/langfuse/api/resources/metrics_v_2/types/metrics_v_2_response.py b/langfuse/api/resources/metrics_v_2/types/metrics_v_2_response.py
new file mode 100644
index 000000000..ff0a475ea
--- /dev/null
+++ b/langfuse/api/resources/metrics_v_2/types/metrics_v_2_response.py
@@ -0,0 +1,47 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+from ....core.datetime_utils import serialize_datetime
+from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1
+
+
+class MetricsV2Response(pydantic_v1.BaseModel):
+ data: typing.List[typing.Dict[str, typing.Any]] = pydantic_v1.Field()
+ """
+ The metrics data. Each item in the list contains the metric values and dimensions requested in the query.
+ Format varies based on the query parameters.
+ Histograms will return an array with [lower, upper, height] tuples.
+ """
+
+ def json(self, **kwargs: typing.Any) -> str:
+ kwargs_with_defaults: typing.Any = {
+ "by_alias": True,
+ "exclude_unset": True,
+ **kwargs,
+ }
+ return super().json(**kwargs_with_defaults)
+
+ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
+ kwargs_with_defaults_exclude_unset: typing.Any = {
+ "by_alias": True,
+ "exclude_unset": True,
+ **kwargs,
+ }
+ kwargs_with_defaults_exclude_none: typing.Any = {
+ "by_alias": True,
+ "exclude_none": True,
+ **kwargs,
+ }
+
+ return deep_union_pydantic_dicts(
+ super().dict(**kwargs_with_defaults_exclude_unset),
+ super().dict(**kwargs_with_defaults_exclude_none),
+ )
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic_v1.Extra.allow
+ json_encoders = {dt.datetime: serialize_datetime}
diff --git a/langfuse/api/resources/observations_v_2/__init__.py b/langfuse/api/resources/observations_v_2/__init__.py
new file mode 100644
index 000000000..a04697f31
--- /dev/null
+++ b/langfuse/api/resources/observations_v_2/__init__.py
@@ -0,0 +1,5 @@
+# This file was auto-generated by Fern from our API Definition.
+
+from .types import ObservationsV2Meta, ObservationsV2Response
+
+__all__ = ["ObservationsV2Meta", "ObservationsV2Response"]
diff --git a/langfuse/api/resources/observations_v_2/client.py b/langfuse/api/resources/observations_v_2/client.py
new file mode 100644
index 000000000..e6796fd46
--- /dev/null
+++ b/langfuse/api/resources/observations_v_2/client.py
@@ -0,0 +1,558 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+from json.decoder import JSONDecodeError
+
+from ...core.api_error import ApiError
+from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
+from ...core.datetime_utils import serialize_datetime
+from ...core.pydantic_utilities import pydantic_v1
+from ...core.request_options import RequestOptions
+from ..commons.errors.access_denied_error import AccessDeniedError
+from ..commons.errors.error import Error
+from ..commons.errors.method_not_allowed_error import MethodNotAllowedError
+from ..commons.errors.not_found_error import NotFoundError
+from ..commons.errors.unauthorized_error import UnauthorizedError
+from ..commons.types.observation_level import ObservationLevel
+from .types.observations_v_2_response import ObservationsV2Response
+
+
+class ObservationsV2Client:
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ def get_many(
+ self,
+ *,
+ fields: typing.Optional[str] = None,
+ limit: typing.Optional[int] = None,
+ cursor: typing.Optional[str] = None,
+ parse_io_as_json: typing.Optional[bool] = None,
+ name: typing.Optional[str] = None,
+ user_id: typing.Optional[str] = None,
+ type: typing.Optional[str] = None,
+ trace_id: typing.Optional[str] = None,
+ level: typing.Optional[ObservationLevel] = None,
+ parent_observation_id: typing.Optional[str] = None,
+ environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
+ from_start_time: typing.Optional[dt.datetime] = None,
+ to_start_time: typing.Optional[dt.datetime] = None,
+ version: typing.Optional[str] = None,
+ filter: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ObservationsV2Response:
+ """
+ Get a list of observations with cursor-based pagination and flexible field selection.
+
+ ## Cursor-based Pagination
+ This endpoint uses cursor-based pagination for efficient traversal of large datasets.
+ The cursor is returned in the response metadata and should be passed in subsequent requests
+ to retrieve the next page of results.
+
+ ## Field Selection
+ Use the `fields` parameter to control which observation fields are returned:
+ - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type
+ - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId
+ - `time` - completionStartTime, createdAt, updatedAt
+ - `io` - input, output
+ - `metadata` - metadata
+ - `model` - providedModelName, internalModelId, modelParameters
+ - `usage` - usageDetails, costDetails, totalCost
+ - `prompt` - promptId, promptName, promptVersion
+ - `metrics` - latency, timeToFirstToken
+
+ If not specified, `core` and `basic` field groups are returned.
+
+ ## Filters
+ Multiple filtering options are available via query parameters or the structured `filter` parameter.
+ When using the `filter` parameter, it takes precedence over individual query parameter filters.
+
+ Parameters
+ ----------
+ fields : typing.Optional[str]
+ Comma-separated list of field groups to include in the response.
+ Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics.
+ If not specified, `core` and `basic` field groups are returned.
+ Example: "basic,usage,model"
+
+ limit : typing.Optional[int]
+ Number of items to return per page. Maximum 1000, default 50.
+
+ cursor : typing.Optional[str]
+ Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page.
+
+ parse_io_as_json : typing.Optional[bool]
+ Set to `true` to parse input/output fields as JSON, or `false` to return raw strings.
+ Defaults to `false` if not provided.
+
+ name : typing.Optional[str]
+
+ user_id : typing.Optional[str]
+
+ type : typing.Optional[str]
+ Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL")
+
+ trace_id : typing.Optional[str]
+
+ level : typing.Optional[ObservationLevel]
+ Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR").
+
+ parent_observation_id : typing.Optional[str]
+
+ environment : typing.Optional[typing.Union[str, typing.Sequence[str]]]
+ Optional filter for observations where the environment is one of the provided values.
+
+ from_start_time : typing.Optional[dt.datetime]
+ Retrieve only observations with a start_time on or after this datetime (ISO 8601).
+
+ to_start_time : typing.Optional[dt.datetime]
+ Retrieve only observations with a start_time before this datetime (ISO 8601).
+
+ version : typing.Optional[str]
+ Optional filter to only include observations with a certain version.
+
+ filter : typing.Optional[str]
+ JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...).
+
+ ## Filter Structure
+ Each filter condition has the following structure:
+ ```json
+ [
+ {
+ "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
+ "column": string, // Required. Column to filter on (see available columns below)
+ "operator": string, // Required. Operator based on type:
+ // - datetime: ">", "<", ">=", "<="
+ // - string: "=", "contains", "does not contain", "starts with", "ends with"
+ // - stringOptions: "any of", "none of"
+ // - categoryOptions: "any of", "none of"
+ // - arrayOptions: "any of", "none of", "all of"
+ // - number: "=", ">", "<", ">=", "<="
+ // - stringObject: "=", "contains", "does not contain", "starts with", "ends with"
+ // - numberObject: "=", ">", "<", ">=", "<="
+ // - boolean: "=", "<>"
+ // - null: "is null", "is not null"
+ "value": any, // Required (except for null type). Value to compare against. Type depends on filter type
+ "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata
+ }
+ ]
+ ```
+
+ ## Available Columns
+
+ ### Core Observation Fields
+ - `id` (string) - Observation ID
+ - `type` (string) - Observation type (SPAN, GENERATION, EVENT)
+ - `name` (string) - Observation name
+ - `traceId` (string) - Associated trace ID
+ - `startTime` (datetime) - Observation start time
+ - `endTime` (datetime) - Observation end time
+ - `environment` (string) - Environment tag
+ - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR)
+ - `statusMessage` (string) - Status message
+ - `version` (string) - Version tag
+ - `userId` (string) - User ID
+ - `sessionId` (string) - Session ID
+
+ ### Trace-Related Fields
+ - `traceName` (string) - Name of the parent trace
+ - `traceTags` (arrayOptions) - Tags from the parent trace
+ - `tags` (arrayOptions) - Alias for traceTags
+
+ ### Performance Metrics
+ - `latency` (number) - Latency in seconds (calculated: end_time - start_time)
+ - `timeToFirstToken` (number) - Time to first token in seconds
+ - `tokensPerSecond` (number) - Output tokens per second
+
+ ### Token Usage
+ - `inputTokens` (number) - Number of input tokens
+ - `outputTokens` (number) - Number of output tokens
+ - `totalTokens` (number) - Total tokens (alias: `tokens`)
+
+ ### Cost Metrics
+ - `inputCost` (number) - Input cost in USD
+ - `outputCost` (number) - Output cost in USD
+ - `totalCost` (number) - Total cost in USD
+
+ ### Model Information
+ - `model` (string) - Provided model name (alias: `providedModelName`)
+ - `promptName` (string) - Associated prompt name
+ - `promptVersion` (number) - Associated prompt version
+
+ ### Structured Data
+ - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys.
+
+ ## Filter Examples
+ ```json
+ [
+ {
+ "type": "string",
+ "column": "type",
+ "operator": "=",
+ "value": "GENERATION"
+ },
+ {
+ "type": "number",
+ "column": "latency",
+ "operator": ">=",
+ "value": 2.5
+ },
+ {
+ "type": "stringObject",
+ "column": "metadata",
+ "key": "environment",
+ "operator": "=",
+ "value": "production"
+ }
+ ]
+ ```
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ObservationsV2Response
+
+ Examples
+ --------
+ from langfuse.client import FernLangfuse
+
+ client = FernLangfuse(
+ x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
+ x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
+ x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
+ username="YOUR_USERNAME",
+ password="YOUR_PASSWORD",
+ base_url="https://yourhost.com/path/to/api",
+ )
+ client.observations_v_2.get_many()
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "api/public/v2/observations",
+ method="GET",
+ params={
+ "fields": fields,
+ "limit": limit,
+ "cursor": cursor,
+ "parseIoAsJson": parse_io_as_json,
+ "name": name,
+ "userId": user_id,
+ "type": type,
+ "traceId": trace_id,
+ "level": level,
+ "parentObservationId": parent_observation_id,
+ "environment": environment,
+ "fromStartTime": serialize_datetime(from_start_time)
+ if from_start_time is not None
+ else None,
+ "toStartTime": serialize_datetime(to_start_time)
+ if to_start_time is not None
+ else None,
+ "version": version,
+ "filter": filter,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return pydantic_v1.parse_obj_as(
+ ObservationsV2Response, _response.json()
+ ) # type: ignore
+ if _response.status_code == 400:
+ raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 403:
+ raise AccessDeniedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 405:
+ raise MethodNotAllowedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 404:
+ raise NotFoundError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, body=_response.text)
+ raise ApiError(status_code=_response.status_code, body=_response_json)
+
+
+class AsyncObservationsV2Client:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def get_many(
+ self,
+ *,
+ fields: typing.Optional[str] = None,
+ limit: typing.Optional[int] = None,
+ cursor: typing.Optional[str] = None,
+ parse_io_as_json: typing.Optional[bool] = None,
+ name: typing.Optional[str] = None,
+ user_id: typing.Optional[str] = None,
+ type: typing.Optional[str] = None,
+ trace_id: typing.Optional[str] = None,
+ level: typing.Optional[ObservationLevel] = None,
+ parent_observation_id: typing.Optional[str] = None,
+ environment: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
+ from_start_time: typing.Optional[dt.datetime] = None,
+ to_start_time: typing.Optional[dt.datetime] = None,
+ version: typing.Optional[str] = None,
+ filter: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ObservationsV2Response:
+ """
+ Get a list of observations with cursor-based pagination and flexible field selection.
+
+ ## Cursor-based Pagination
+ This endpoint uses cursor-based pagination for efficient traversal of large datasets.
+ The cursor is returned in the response metadata and should be passed in subsequent requests
+ to retrieve the next page of results.
+
+ ## Field Selection
+ Use the `fields` parameter to control which observation fields are returned:
+ - `core` - Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type
+ - `basic` - name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId
+ - `time` - completionStartTime, createdAt, updatedAt
+ - `io` - input, output
+ - `metadata` - metadata
+ - `model` - providedModelName, internalModelId, modelParameters
+ - `usage` - usageDetails, costDetails, totalCost
+ - `prompt` - promptId, promptName, promptVersion
+ - `metrics` - latency, timeToFirstToken
+
+ If not specified, `core` and `basic` field groups are returned.
+
+ ## Filters
+ Multiple filtering options are available via query parameters or the structured `filter` parameter.
+ When using the `filter` parameter, it takes precedence over individual query parameter filters.
+
+ Parameters
+ ----------
+ fields : typing.Optional[str]
+ Comma-separated list of field groups to include in the response.
+ Available groups: core, basic, time, io, metadata, model, usage, prompt, metrics.
+ If not specified, `core` and `basic` field groups are returned.
+ Example: "basic,usage,model"
+
+ limit : typing.Optional[int]
+ Number of items to return per page. Maximum 1000, default 50.
+
+ cursor : typing.Optional[str]
+ Base64-encoded cursor for pagination. Use the cursor from the previous response to get the next page.
+
+ parse_io_as_json : typing.Optional[bool]
+ Set to `true` to parse input/output fields as JSON, or `false` to return raw strings.
+ Defaults to `false` if not provided.
+
+ name : typing.Optional[str]
+
+ user_id : typing.Optional[str]
+
+ type : typing.Optional[str]
+ Filter by observation type (e.g., "GENERATION", "SPAN", "EVENT", "AGENT", "TOOL", "CHAIN", "RETRIEVER", "EVALUATOR", "EMBEDDING", "GUARDRAIL")
+
+ trace_id : typing.Optional[str]
+
+ level : typing.Optional[ObservationLevel]
+ Optional filter for observations with a specific level (e.g. "DEBUG", "DEFAULT", "WARNING", "ERROR").
+
+ parent_observation_id : typing.Optional[str]
+
+ environment : typing.Optional[typing.Union[str, typing.Sequence[str]]]
+ Optional filter for observations where the environment is one of the provided values.
+
+ from_start_time : typing.Optional[dt.datetime]
+ Retrieve only observations with a start_time on or after this datetime (ISO 8601).
+
+ to_start_time : typing.Optional[dt.datetime]
+ Retrieve only observations with a start_time before this datetime (ISO 8601).
+
+ version : typing.Optional[str]
+ Optional filter to only include observations with a certain version.
+
+ filter : typing.Optional[str]
+ JSON string containing an array of filter conditions. When provided, this takes precedence over query parameter filters (userId, name, type, level, environment, fromStartTime, ...).
+
+ ## Filter Structure
+ Each filter condition has the following structure:
+ ```json
+ [
+ {
+ "type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
+ "column": string, // Required. Column to filter on (see available columns below)
+ "operator": string, // Required. Operator based on type:
+ // - datetime: ">", "<", ">=", "<="
+ // - string: "=", "contains", "does not contain", "starts with", "ends with"
+ // - stringOptions: "any of", "none of"
+ // - categoryOptions: "any of", "none of"
+ // - arrayOptions: "any of", "none of", "all of"
+ // - number: "=", ">", "<", ">=", "<="
+ // - stringObject: "=", "contains", "does not contain", "starts with", "ends with"
+ // - numberObject: "=", ">", "<", ">=", "<="
+ // - boolean: "=", "<>"
+ // - null: "is null", "is not null"
+ "value": any, // Required (except for null type). Value to compare against. Type depends on filter type
+ "key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata
+ }
+ ]
+ ```
+
+ ## Available Columns
+
+ ### Core Observation Fields
+ - `id` (string) - Observation ID
+ - `type` (string) - Observation type (SPAN, GENERATION, EVENT)
+ - `name` (string) - Observation name
+ - `traceId` (string) - Associated trace ID
+ - `startTime` (datetime) - Observation start time
+ - `endTime` (datetime) - Observation end time
+ - `environment` (string) - Environment tag
+ - `level` (string) - Log level (DEBUG, DEFAULT, WARNING, ERROR)
+ - `statusMessage` (string) - Status message
+ - `version` (string) - Version tag
+ - `userId` (string) - User ID
+ - `sessionId` (string) - Session ID
+
+ ### Trace-Related Fields
+ - `traceName` (string) - Name of the parent trace
+ - `traceTags` (arrayOptions) - Tags from the parent trace
+ - `tags` (arrayOptions) - Alias for traceTags
+
+ ### Performance Metrics
+ - `latency` (number) - Latency in seconds (calculated: end_time - start_time)
+ - `timeToFirstToken` (number) - Time to first token in seconds
+ - `tokensPerSecond` (number) - Output tokens per second
+
+ ### Token Usage
+ - `inputTokens` (number) - Number of input tokens
+ - `outputTokens` (number) - Number of output tokens
+ - `totalTokens` (number) - Total tokens (alias: `tokens`)
+
+ ### Cost Metrics
+ - `inputCost` (number) - Input cost in USD
+ - `outputCost` (number) - Output cost in USD
+ - `totalCost` (number) - Total cost in USD
+
+ ### Model Information
+ - `model` (string) - Provided model name (alias: `providedModelName`)
+ - `promptName` (string) - Associated prompt name
+ - `promptVersion` (number) - Associated prompt version
+
+ ### Structured Data
+ - `metadata` (stringObject/numberObject/categoryOptions) - Metadata key-value pairs. Use `key` parameter to filter on specific metadata keys.
+
+ ## Filter Examples
+ ```json
+ [
+ {
+ "type": "string",
+ "column": "type",
+ "operator": "=",
+ "value": "GENERATION"
+ },
+ {
+ "type": "number",
+ "column": "latency",
+ "operator": ">=",
+ "value": 2.5
+ },
+ {
+ "type": "stringObject",
+ "column": "metadata",
+ "key": "environment",
+ "operator": "=",
+ "value": "production"
+ }
+ ]
+ ```
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ObservationsV2Response
+
+ Examples
+ --------
+ import asyncio
+
+ from langfuse.client import AsyncFernLangfuse
+
+ client = AsyncFernLangfuse(
+ x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
+ x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
+ x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
+ username="YOUR_USERNAME",
+ password="YOUR_PASSWORD",
+ base_url="https://yourhost.com/path/to/api",
+ )
+
+
+ async def main() -> None:
+ await client.observations_v_2.get_many()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "api/public/v2/observations",
+ method="GET",
+ params={
+ "fields": fields,
+ "limit": limit,
+ "cursor": cursor,
+ "parseIoAsJson": parse_io_as_json,
+ "name": name,
+ "userId": user_id,
+ "type": type,
+ "traceId": trace_id,
+ "level": level,
+ "parentObservationId": parent_observation_id,
+ "environment": environment,
+ "fromStartTime": serialize_datetime(from_start_time)
+ if from_start_time is not None
+ else None,
+ "toStartTime": serialize_datetime(to_start_time)
+ if to_start_time is not None
+ else None,
+ "version": version,
+ "filter": filter,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return pydantic_v1.parse_obj_as(
+ ObservationsV2Response, _response.json()
+ ) # type: ignore
+ if _response.status_code == 400:
+ raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
+ if _response.status_code == 401:
+ raise UnauthorizedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 403:
+ raise AccessDeniedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 405:
+ raise MethodNotAllowedError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ if _response.status_code == 404:
+ raise NotFoundError(
+ pydantic_v1.parse_obj_as(typing.Any, _response.json())
+ ) # type: ignore
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, body=_response.text)
+ raise ApiError(status_code=_response.status_code, body=_response_json)
diff --git a/langfuse/api/resources/observations_v_2/types/__init__.py b/langfuse/api/resources/observations_v_2/types/__init__.py
new file mode 100644
index 000000000..b62504c61
--- /dev/null
+++ b/langfuse/api/resources/observations_v_2/types/__init__.py
@@ -0,0 +1,6 @@
+# This file was auto-generated by Fern from our API Definition.
+
+from .observations_v_2_meta import ObservationsV2Meta
+from .observations_v_2_response import ObservationsV2Response
+
+__all__ = ["ObservationsV2Meta", "ObservationsV2Response"]
diff --git a/langfuse/api/resources/observations_v_2/types/observations_v_2_meta.py b/langfuse/api/resources/observations_v_2/types/observations_v_2_meta.py
new file mode 100644
index 000000000..d720db59b
--- /dev/null
+++ b/langfuse/api/resources/observations_v_2/types/observations_v_2_meta.py
@@ -0,0 +1,49 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+from ....core.datetime_utils import serialize_datetime
+from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1
+
+
+class ObservationsV2Meta(pydantic_v1.BaseModel):
+ """
+ Metadata for cursor-based pagination
+ """
+
+ cursor: typing.Optional[str] = pydantic_v1.Field(default=None)
+ """
+ Base64-encoded cursor to use for retrieving the next page. If not present, there are no more results.
+ """
+
+ def json(self, **kwargs: typing.Any) -> str:
+ kwargs_with_defaults: typing.Any = {
+ "by_alias": True,
+ "exclude_unset": True,
+ **kwargs,
+ }
+ return super().json(**kwargs_with_defaults)
+
+ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
+ kwargs_with_defaults_exclude_unset: typing.Any = {
+ "by_alias": True,
+ "exclude_unset": True,
+ **kwargs,
+ }
+ kwargs_with_defaults_exclude_none: typing.Any = {
+ "by_alias": True,
+ "exclude_none": True,
+ **kwargs,
+ }
+
+ return deep_union_pydantic_dicts(
+ super().dict(**kwargs_with_defaults_exclude_unset),
+ super().dict(**kwargs_with_defaults_exclude_none),
+ )
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic_v1.Extra.allow
+ json_encoders = {dt.datetime: serialize_datetime}
diff --git a/langfuse/api/resources/observations_v_2/types/observations_v_2_response.py b/langfuse/api/resources/observations_v_2/types/observations_v_2_response.py
new file mode 100644
index 000000000..fdea2c3c3
--- /dev/null
+++ b/langfuse/api/resources/observations_v_2/types/observations_v_2_response.py
@@ -0,0 +1,55 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+from ....core.datetime_utils import serialize_datetime
+from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1
+from .observations_v_2_meta import ObservationsV2Meta
+
+
+class ObservationsV2Response(pydantic_v1.BaseModel):
+ """
+ Response containing observations with field-group-based filtering and cursor-based pagination.
+
+ The `data` array contains observation objects with only the requested field groups included.
+ Use the `cursor` in `meta` to retrieve the next page of results.
+ """
+
+ data: typing.List[typing.Dict[str, typing.Any]] = pydantic_v1.Field()
+ """
+ Array of observation objects. Fields included depend on the `fields` parameter in the request.
+ """
+
+ meta: ObservationsV2Meta
+
+ def json(self, **kwargs: typing.Any) -> str:
+ kwargs_with_defaults: typing.Any = {
+ "by_alias": True,
+ "exclude_unset": True,
+ **kwargs,
+ }
+ return super().json(**kwargs_with_defaults)
+
+ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
+ kwargs_with_defaults_exclude_unset: typing.Any = {
+ "by_alias": True,
+ "exclude_unset": True,
+ **kwargs,
+ }
+ kwargs_with_defaults_exclude_none: typing.Any = {
+ "by_alias": True,
+ "exclude_none": True,
+ **kwargs,
+ }
+
+ return deep_union_pydantic_dicts(
+ super().dict(**kwargs_with_defaults_exclude_unset),
+ super().dict(**kwargs_with_defaults_exclude_none),
+ )
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic_v1.Extra.allow
+ json_encoders = {dt.datetime: serialize_datetime}