Langfuse v4: up to 165× faster · Read more
DocsObservations API

Observations API

The Observations API allows you to retrieve observation data (spans, generations, events) from Langfuse for use in custom workflows, evaluation pipelines, and analytics.

If you need aggregated metrics (e.g., total cost, token counts, or trace volumes grouped by user, model, or time period) rather than individual observations, the Metrics API is designed for this and avoids the need to fetch and aggregate raw data yourself.

For general information about API authentication, base URLs, and SDK access, see the Public API documentation.

The deprecated GET /api/public/traces and GET /api/public/observations endpoints are documented, with migration steps, in Migration of deprecated APIs.

Observations API v2

Where is this feature available?
  • Hobby
    Available
  • Core
    Available
  • Pro
    Available
  • Enterprise
    Available
  • Self Hosted
    Langfuse v4+

Data availability: Data from older SDKs (langfuse-python < 4.7.0, langfuse-js < 5.4.0) or direct OpenTelemetry exporters that do not send x-langfuse-ingestion-version: 4 can be delayed by up to 10 minutes on v2 endpoints. Upgrade to Python SDK v4.7.0+ or JS/TS SDK v5.4.0+, or set that header on your OTEL span exporter to see new data in real time. Details: Versions & Compatibility.

On self-hosted Langfuse v3, use the v1 Observations API instead; see the self-hosted compatibility matrix.

GET /api/public/v2/observations

The v2 Observations API is a redesigned endpoint optimized for high-performance data retrieval. It addresses the performance bottlenecks of the v1 API by minimizing the work Langfuse has to perform per query.

Upgrade from older trace and observation reads

The migration guide maps every deprecated read endpoint (/api/public/traces, /api/public/observations, /api/public/sessions, ...) to its v2 replacement, with parameter mappings and before/after examples. Always include fromStartTime and toStartTime to keep each request bounded.

The v2 Observations API returns observation rows, not full trace objects. Group rows by traceId when you need to reconstruct trace activity, and use Metrics API v2 for aggregate reporting with trace-level dimensions such as traceName, traceRelease, or traceVersion. There is no get-by-id route on v2; for single-observation lookups, pass a URL-encoded filter condition on the id column instead. See the v2 Observations API Reference for the filter schema.

Logical root observations

The v2 API distinguishes physical parentage from logical root status:

  • parentObservationId identifies the physical parent observation. An empty value matches observations without a physical parent.
  • isRootObservation is true when an observation has no physical parent or the SDK explicitly marked it as an application root.

An application-root observation can therefore have isRootObservation: true and a non-null parentObservationId. Use isRootObservation when you want application roots, and parentObservationId when you need to query the physical observation tree.

The logical-root filter assumes one exported application root per trace. If a trace has no exported root in this project — for example, its root lives in another service — isRootObservation = true will not match it; group by traceId when you need complete trace counts. Multiple matching roots indicate an integration issue.

Key Improvements

1. Selective Field Retrieval

The v1 API returns complete rows with all fields (input/output, usage, metadata, etc.), forcing the database to scan every column even when you only need a subset. The v2 API lets you specify which field groups you need as a comma-separated string:

?fields=core,basic,usage

Available Field Groups

GroupFields
coreAlways included: id, traceId, startTime, endTime, projectId, parentObservationId, type
basicname, level, statusMessage, version, environment, bookmarked, public, userId, sessionId, isRootObservation
timecompletionStartTime, createdAt, updatedAt
ioinput, output
metadatametadata
modelprovidedModelName, internalModelId, modelParameters
usageusageDetails, inputUsage, outputUsage, totalUsage, costDetails, inputCost, outputCost, totalCost, usagePricingTierName
promptpromptId, promptName, promptVersion
metricslatency, timeToFirstToken
trace_contexttags, release, traceName

If fields is not specified, core and basic field groups are returned by default. Fields from groups you do not request are absent from the response, not null. The following fields are an exception and are always present but only populated when the field group model is selected: modelId, inputPrice, outputPrice, and totalPrice. Note that inputPrice, outputPrice, and totalPrice are returned as strings (e.g. "0.000005") to preserve decimal precision; cast them to a numeric type in your pipeline.

2. Cursor-Based Pagination

The v1 API uses offset-based pagination (page numbers) which becomes increasingly slow for large datasets. The v2 API uses cursor-based pagination for better and more consistent performance.

How it works:

  1. Make your initial request with a limit parameter
  2. If more results exist, the response includes a cursor in the meta object
  3. Pass this cursor via the cursor parameter in your next request to continue where you left off
  4. Repeat until no cursor is returned (you've reached the end)

Results are always sorted by startTime descending (newest first).

Example response with cursor:

{
  "data": [
    {"id": "obs-1", "traceId": "trace-1", "name": "llm-call", ...},
    {"id": "obs-2", "traceId": "trace-1", "name": "embedding", ...}
  ],
  "meta": {
    "cursor": "eyJsYXN0U3RhcnRUaW1lIjoiMjAyNS0xMi0xNVQxMDozMDowMFoiLCJsYXN0SWQiOiJvYnMtMTAwIn0="
  }
}

When the response has no cursor in meta (or meta.cursor is null), you've retrieved all matching observations.

3. Optimized I/O Handling

The v1 API always attempts to parse input/output as JSON which can be expensive. The v2 API returns I/O as raw strings; parse them in your pipeline when you need JSON. The parseIoAsJson parameter is deprecated: omit it or set it to false; setting it to true returns a 400 error.

4. Higher Limits

Featurev1v2
Default limit5050
Maximum limit1001,000

On Langfuse Cloud, requests to the v2 Observations API count toward the general per-organization API rate limit (see the API limits FAQ). Self-hosted instances have no enforced rate limits.

Common Use Cases

Polling for recent observations:

curl \
  -H "Authorization: Basic <BASIC AUTH HEADER>" \
  "https://cloud.langfuse.com/api/public/v2/observations?fromStartTime=2025-12-15T00:00:00Z&toStartTime=2025-12-16T00:00:00Z&limit=10"

Getting observations for a specific trace:

curl \
  -H "Authorization: Basic <BASIC AUTH HEADER>" \
  "https://cloud.langfuse.com/api/public/v2/observations?fields=core,basic,usage&traceId=your-trace-id"

Paginating through results:

# First request
curl \
  -H "Authorization: Basic <BASIC AUTH HEADER>" \
  "https://cloud.langfuse.com/api/public/v2/observations?fromStartTime=2025-12-01T00:00:00Z&limit=100"

# Response includes: "meta": { "cursor": "eyJsYXN0..." }

# Next request with cursor
curl \
  -H "Authorization: Basic <BASIC AUTH HEADER>" \
  "https://cloud.langfuse.com/api/public/v2/observations?fromStartTime=2025-12-01T00:00:00Z&limit=100&cursor=eyJsYXN0..."

Parameters

ParameterTypeDescription
fieldsstringComma-separated list of field groups to include. Defaults to core,basic
limitintegerNumber of items per page. Defaults to 50, max 1,000
cursorstringBase64-encoded cursor for pagination (from previous response)
fromStartTimedatetimeRetrieve observations with startTime on or after this datetime
toStartTimedatetimeRetrieve observations with startTime before this datetime
traceIdstringFilter by trace ID
namestringFilter by observation name
typestringFilter by observation type (GENERATION, SPAN, EVENT)
userIdstringFilter by user ID
levelstringFilter by log level (DEBUG, DEFAULT, WARNING, ERROR)
parentObservationIdstringFilter by physical parent observation ID. An empty value matches observations without a physical parent.
isRootObservationbooleanFilter by logical root status. Matches observations without a physical parent and SDK-marked application roots.
environmentstringFilter by environment
versionstringFilter by version tag
parseIoAsJsonbooleanDeprecated: omit or set to false; true returns a 400 error
filterstringJSON array of filter conditions (takes precedence over query params)

Filter logical roots

Use the first-class query parameter when you want all logical roots:

curl \
  -H "Authorization: Basic <BASIC AUTH HEADER>" \
  "https://cloud.langfuse.com/api/public/v2/observations?isRootObservation=true&fromStartTime=2025-12-15T00:00:00Z&toStartTime=2025-12-16T00:00:00Z"

The advanced filter parameter supports the same field with boolean = and <> operators. For example, the decoded JSON value for filter can be:

[
  {
    "type": "boolean",
    "column": "isRootObservation",
    "operator": "=",
    "value": true
  }
]

Sample Response

With all fields included

{
  "data": [
    {
      "id": "support-chat-7-950dc53a-gen",
      "traceId": "support-chat-7-950dc53a",
      "startTime": "2025-12-17T16:09:00.875Z",
      "projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
      "parentObservationId": null,
      "isRootObservation": true,
      "type": "GENERATION",
      "endTime": "2025-12-17T16:09:01.456Z",
      "name": "llm-generation",
      "level": "DEFAULT",
      "statusMessage": "",
      "version": "",
      "environment": "default",
      "bookmarked": false,
      "public": false,
      "completionStartTime": "2025-12-17T16:09:00.995Z",
      "createdAt": "2025-12-17T16:09:00.875Z",
      "updatedAt": "2025-12-17T16:09:01.456Z",
      "input": "{\"messages\":[{\"role\":\"user\",\"content\":\"Perfect.\"}]}",
      "output": "{\"role\":\"assistant\",\"content\":\"You're all set. Have a great day!\"}",
      "metadata": {},
      "providedModelName": "gpt-4o",
      "internalModelId": "clm1a2b3c4d5e6f7g8h9i0j1",
      "modelParameters": {
        "temperature": 0.2
      },
      "usageDetails": {
        "input": 98,
        "output": 68,
        "total": 166
      },
      "inputUsage": 98,
      "outputUsage": 68,
      "totalUsage": 166,
      "costDetails": {
        "input": 0.00049,
        "output": 0.00204,
        "total": 0.00253
      },
      "inputCost": 0.00049,
      "outputCost": 0.00204,
      "totalCost": 0.00253,
      "promptId": "",
      "promptName": "",
      "promptVersion": null,
      "latency": 0.581,
      "timeToFirstToken": 0.12,
      "userId": "",
      "sessionId": "support-chat-session",
      "modelId": "clm1a2b3c4d5e6f7g8h9i0j1",
      "inputPrice": "0.000005",
      "outputPrice": "0.00003",
      "totalPrice": null,
      "usagePricingTierName": null,
      "tags": ["support", "chat"],
      "release": "v1.4.2",
      "traceName": "support-chat"
    }
  ],
  "meta": {
    "cursor": "eyJsYXN0U3RhcnRUaW1lVG8iOiIyMDI1LTEyLTE3VDE2OjA5OjAwLjg3NVoiLCJsYXN0VHJhY2VJZCI6InN1cHBvcnQtY2hhdC03LTk1MGRjNTNhIiwibGFzdElkIjoic3VwcG9ydC1jaGF0LTctOTUwZGM1M2EtZ2VuIn0="
  }
}

API Reference: See the full v2 Observations API Reference for all available parameters, response schemas, and interactive examples.


Was this page helpful?

Last edited