---
title: Observations API
sidebarTitle: Observations API
description: Retrieve observations from Langfuse with high-performance v2 endpoints featuring cursor-based pagination and selective field retrieval.
---

# 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](/docs/metrics/features/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](/docs/api-and-data-platform/features/public-api).

The deprecated `GET /api/public/traces` and `GET /api/public/observations` endpoints are documented, with migration steps, in [Migration of deprecated APIs](/faq/all/deprecated-api-migration).

## Observations API v2 [#v2]

**Where is this feature available?**

| Plan | Availability |
| --- | --- |
| 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 15 minutes on v2 endpoints. Upgrade to [Python SDK v4.7.0+](/docs/observability/sdk/upgrade-path/python-v3-to-v4) or [JS/TS SDK v5.4.0+](/docs/observability/sdk/upgrade-path/js-v4-to-v5), or [set that header on your OTEL span exporter](/integrations/native/opentelemetry#real-time-ingestion) to see new data in real time. Details: [Versions & Compatibility](/docs/compatibility#faq-delay).

On self-hosted Langfuse v3, use the [v1 Observations API](https://api.reference.langfuse.com/#tag/observations/GET/api/public/observations) instead; see the [self-hosted compatibility matrix](/self-hosting/upgrade/versioning#sdk-server).

```
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 [#upgrade-from-older-reads]

The [migration guide](/faq/all/deprecated-api-migration) 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](/docs/metrics/features/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](https://api.reference.langfuse.com/#tag/observations/GET/api/public/v2/observations) for the filter schema.

### Logical root observations [#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

| Group           | Fields                                                                                                                 |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `core`          | Always included: id, traceId, startTime, endTime, projectId, parentObservationId, type                                 |
| `basic`         | name, level, statusMessage, version, environment, bookmarked, public, userId, sessionId, isRootObservation             |
| `time`          | completionStartTime, createdAt, updatedAt                                                                              |
| `io`            | input, output                                                                                                          |
| `metadata`      | metadata                                                                                                               |
| `model`         | model, internalModelId, modelParameters                                                                                |
| `usage`         | usageDetails, inputUsage, outputUsage, totalUsage, costDetails, inputCost, outputCost, totalCost, usagePricingTierName |
| `prompt`        | promptId, promptName, promptVersion                                                                                    |
| `metrics`       | latency, timeToFirstToken                                                                                              |
| `trace_context` | tags, 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:**

```json
{
  "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**

| Feature       | v1  | v2    |
| ------------- | --- | ----- |
| Default limit | 50  | 50    |
| Maximum limit | 100 | 1,000 |

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

### Common Use Cases

**Polling for recent observations:**

```bash
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:**

```bash
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:**

```bash
# 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

| Parameter             | Type     | Description                                                                                                     |
| --------------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `fields`              | string   | Comma-separated list of field groups to include. Defaults to `core,basic`                                       |
| `limit`               | integer  | Number of items per page. Defaults to 50, max 1,000                                                             |
| `cursor`              | string   | Base64-encoded cursor for pagination (from previous response)                                                   |
| `fromStartTime`       | datetime | Retrieve observations with startTime on or after this datetime                                                  |
| `toStartTime`         | datetime | Retrieve observations with startTime before this datetime                                                       |
| `traceId`             | string   | Filter by trace ID                                                                                              |
| `name`                | string   | Filter by observation name                                                                                      |
| `type`                | string   | Filter by observation type (GENERATION, SPAN, EVENT)                                                            |
| `userId`              | string   | Filter by user ID                                                                                               |
| `level`               | string   | Filter by log level (DEBUG, DEFAULT, WARNING, ERROR)                                                            |
| `parentObservationId` | string   | Filter by physical parent observation ID. An empty value matches observations without a physical parent.        |
| `isRootObservation`   | boolean  | Filter by logical root status. Matches observations without a physical parent and SDK-marked application roots. |
| `environment`         | string   | Filter by environment                                                                                           |
| `version`             | string   | Filter by version tag                                                                                           |
| `parseIoAsJson`       | boolean  | Deprecated: omit or set to `false`; `true` returns a `400` error                                                |
| `filter`              | string   | JSON array of filter conditions (takes precedence over query params)                                            |

#### Filter logical roots

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

```bash
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:

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

### Sample Response

With all fields included

```json
{
  "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": {},
      "model": "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](https://api.reference.langfuse.com/#tag/observationsv2/GET/api/public/v2/observations) for all available parameters, response schemas, and interactive examples.

<!-- agent-instructions -->

---

## Agent Instructions

This page is part of the [Langfuse](https://langfuse.com) documentation, published as plain Markdown for AI agents. Every page is available as Markdown by appending `.md` to its URL, or by sending an `Accept: text/markdown` header. This page: `https://langfuse.com/docs/api-and-data-platform/features/observations-api.md`.

### Querying these docs

If the answer is not on this page, query the documentation instead of guessing:

- **Semantic search** across all Langfuse docs, returning an answer with the relevant pages and excerpts. Ask a specific, self-contained question:

  ```bash
  curl -sG "https://langfuse.com/api/search-docs" --data-urlencode "query=How do I trace a LangGraph agent?"
  ```

- **Index of every page**: <https://langfuse.com/llms.txt>, with per-section indexes [llms-docs.txt](https://langfuse.com/llms-docs.txt), [llms-integrations.txt](https://langfuse.com/llms-integrations.txt), and [llms-self-hosting.txt](https://langfuse.com/llms-self-hosting.txt).

### Before writing Langfuse code

- **Install the [Langfuse Agent Skill](https://langfuse.com/docs/api-and-data-platform/features/agent-skill).** It encodes Langfuse's own best practices for instrumentation, prompt management, and evaluation, and materially improves results.
- **Read [What does a good trace look like?](https://langfuse.com/docs/observability/best-practices.md)** before instrumenting an application.
- **Verify endpoints, parameters, and response fields** against the [API reference](https://api.reference.langfuse.com) instead of inferring them from code examples.
- **Use the [Langfuse CLI](https://langfuse.com/docs/api-and-data-platform/features/cli)** (`npx langfuse-cli api <resource> <action>`) to read or write traces, prompts, datasets, and scores from the terminal.

Found an error in these docs? Please open an issue at <https://github.com/langfuse/langfuse-docs/issues>.
