Scores API
The Scores API allows you to retrieve score data (evaluations, annotations, and API-ingested scores) from Langfuse for use in custom workflows, evaluation pipelines, and analytics.
If you need aggregated score metrics (e.g., average scores grouped by trace name, user, or time period) rather than individual scores, 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.
This page covers reading scores. Scores are created via POST /api/public/scores or the SDK helpers — see scores via API/SDK. Migrating from the deprecated v2 read endpoints? Jump to Migrating from Scores API v2.
Scores API v3
- HobbyAvailable
- CoreAvailable
- ProAvailable
- EnterpriseAvailable
- Self HostedAvailable
GET /api/public/v3/scoresOn self-hosted deployments, the v3 endpoint is available from Langfuse v3.179.0.
The Scores API queries scores directly instead of joining trace data, which keeps response times predictable at any volume. Responses carry a single typed value field, and results are paginated with a cursor.
Typed value Field
Every score has a single value field whose type follows dataType — no decoding of booleans from 1/0 or looking up category strings:
dataType | value type | Notes |
|---|---|---|
NUMERIC | number | |
BOOLEAN | boolean | |
CATEGORICAL | string | The category |
TEXT | string | |
CORRECTION | string | Empty string if no correction |
If your pipeline handles mixed score types, branch on dataType.
Field Groups
Responses always include a lean core — id, projectId, name, value, dataType, source, timestamp, environment, createdAt, updatedAt — and you opt into additional groups via a comma-separated fields parameter:
?fields=details,subject,annotation| Group | Fields |
|---|---|
| core | Always included (see above) |
details | comment, configId, metadata |
subject | subject (the entity the score is attached to, see below) |
annotation | authorUserId, queueId |
Unknown group names return HTTP 400.
subject Object
Every score is attached to exactly one entity. Request the subject field group to see which one — it is discriminated by kind:
{ "kind": "observation", "id": "obs-1", "traceId": "trace-1" }kind: "trace"—idis the trace IDkind: "observation"—idis the observation ID; includes the parenttraceIdkind: "session"—idis the session IDkind: "experiment"—idis the dataset run ID
Cursor-Based Pagination
- Make your initial request with a
limitparameter (default 50, max 100) - If more results exist, the response includes a
cursorin themetaobject - Pass this cursor via the
cursorparameter in your next request, repeating the same filter parameters as the initial request — the cursor encodes only the read position, not your query - Repeat until no cursor is returned (you've reached the end)
The response contains no total counts — iterate the cursor until it is absent. For recurring full exports to your data warehouse, use the scheduled blob storage export instead of paginating through the API.
Filtering
- Multi-value filters: most filters accept comma-separated lists —
id,name,source,dataType,environment,configId,queueId,authorUserId,traceId,sessionId,observationId,experimentId. Values within one parameter are OR-ed, parameters are AND-ed:name=hallucination,toxicity&source=EVALreturns eval scores named eitherhallucinationortoxicity. - Value filters: use
valuefor exact matches (comma-separated, requires a singledataTypeofNUMERIC,BOOLEAN, orCATEGORICAL) orvalueMin/valueMaxfor inclusive numeric range bounds (requiredataType=NUMERIC). - Mutual exclusivity:
traceId,sessionId, andexperimentIdare mutually exclusive.observationIdrequirestraceIdbecause observation IDs are scoped to a trace. - Case-insensitive enums:
sourceanddataTypeaccept any casing (numericandNUMERICare equivalent). - Timestamp bounds:
fromTimestampis inclusive,toTimestampis exclusive.
Invalid filter combinations are rejected with HTTP 400 rather than silently ignored.
Common Use Cases
Pulling failing evals:
curl \
-H "Authorization: Basic <BASIC AUTH HEADER>" \
"https://cloud.langfuse.com/api/public/v3/scores?name=hallucination,toxicity&dataType=NUMERIC&valueMax=0.5"Getting scores for specific traces, including what they are attached to:
curl \
-H "Authorization: Basic <BASIC AUTH HEADER>" \
"https://cloud.langfuse.com/api/public/v3/scores?traceId=trace-1,trace-2&fields=details,subject"Fetching a single score by ID:
curl \
-H "Authorization: Basic <BASIC AUTH HEADER>" \
"https://cloud.langfuse.com/api/public/v3/scores?id=your-score-id"Paginating through results:
# First request
curl \
-H "Authorization: Basic <BASIC AUTH HEADER>" \
"https://cloud.langfuse.com/api/public/v3/scores?fromTimestamp=2026-06-01T00:00:00Z&limit=100"
# Response includes: "meta": { "limit": 100, "cursor": "eyJsYXN0..." }
# Next request with cursor
curl \
-H "Authorization: Basic <BASIC AUTH HEADER>" \
"https://cloud.langfuse.com/api/public/v3/scores?fromTimestamp=2026-06-01T00:00:00Z&limit=100&cursor=eyJsYXN0..."SDK Access
The generated SDK clients expose the v3 endpoint directly. Score creation uses separate SDK helpers (e.g. create_score in Python) — this client is for reads.
from langfuse import get_client
langfuse = get_client()
# v3 (recommended)
scores = langfuse.api.scores_v3.get_many_v3(
name="hallucination,toxicity",
data_type="NUMERIC",
value_max=0.5,
fields="details,subject",
limit=100,
)
# v2 (deprecated): langfuse.api.scores.get_many(...)Also available as async via langfuse.async_api.scores_v3.get_many_v3(...).
import { LangfuseClient } from "@langfuse/client";
const langfuse = new LangfuseClient();
// v3 (recommended)
const scores = await langfuse.api.scoresV3.getManyV3({
name: "hallucination,toxicity",
dataType: "NUMERIC",
valueMax: 0.5,
fields: "details,subject",
limit: 100,
});
// v2 (deprecated): langfuse.api.scores.getMany(...)Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Number of items per page. Defaults to 50, max 100 (larger values return HTTP 400) |
cursor | string | URL-safe base64 cursor for pagination (from previous response) |
fields | string | Comma-separated field groups to include in addition to core: details, subject, annotation |
id | string | Comma-separated list of score IDs |
name | string | Comma-separated list of score names |
source | string | Comma-separated list of score sources (API, ANNOTATION, EVAL), case-insensitive |
dataType | string | Comma-separated list of data types (NUMERIC, BOOLEAN, CATEGORICAL, TEXT, CORRECTION), case-insensitive. Must be a single value when combined with value, valueMin, or valueMax |
environment | string | Comma-separated list of environments |
configId | string | Comma-separated list of score config IDs |
queueId | string | Comma-separated list of annotation queue IDs |
authorUserId | string | Comma-separated list of author user IDs |
value | string | Comma-separated list of exact values. Requires a single dataType of NUMERIC, BOOLEAN, or CATEGORICAL. For BOOLEAN pass true or false; for NUMERIC each value must be a finite number |
valueMin | number | Inclusive lower bound on the numeric value. Requires dataType=NUMERIC |
valueMax | number | Inclusive upper bound on the numeric value. Requires dataType=NUMERIC |
traceId | string | Comma-separated list of trace IDs. Mutually exclusive with sessionId, experimentId |
sessionId | string | Comma-separated list of session IDs. Mutually exclusive with traceId, observationId, experimentId |
observationId | string | Comma-separated list of observation IDs. Requires traceId |
experimentId | string | Comma-separated list of dataset run IDs (experiment IDs). Mutually exclusive with traceId, sessionId, observationId |
fromTimestamp | datetime | Inclusive lower bound on the score timestamp |
toTimestamp | datetime | Exclusive upper bound on the score timestamp |
Sample Response
With fields=details,subject,annotation:
{
"data": [
{
"id": "score-1",
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
"name": "hallucination",
"value": 0.25,
"dataType": "NUMERIC",
"source": "EVAL",
"timestamp": "2026-06-15T10:30:00Z",
"environment": "default",
"createdAt": "2026-06-15T10:30:01Z",
"updatedAt": "2026-06-15T10:30:01Z",
"comment": "Low hallucination risk",
"configId": null,
"metadata": {},
"authorUserId": null,
"queueId": null,
"subject": {
"kind": "observation",
"id": "support-chat-7-950dc53a-gen",
"traceId": "support-chat-7-950dc53a"
}
},
{
"id": "score-2",
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
"name": "helpful",
"value": true,
"dataType": "BOOLEAN",
"source": "ANNOTATION",
"timestamp": "2026-06-15T11:00:00Z",
"environment": "default",
"createdAt": "2026-06-15T11:00:02Z",
"updatedAt": "2026-06-15T11:00:02Z",
"comment": null,
"configId": "cfg-1",
"metadata": {},
"authorUserId": "user-123",
"queueId": "queue-1",
"subject": {
"kind": "trace",
"id": "support-chat-7-950dc53a"
}
}
],
"meta": {
"limit": 50,
"cursor": "eyJ2IjoxLCJsYXN0VGltZXN0YW1wIjoiMjAyNi0wNi0xNVQxMTowMDowMFoiLCJsYXN0SWQiOiJzY29yZS0yIn0"
}
}API Reference: See the full v3 Scores API Reference for all available parameters, response schemas, and interactive examples.
Migrating from Scores API v2
v2 read deprecation
The v2 score read endpoints are deprecated. Self-hosted deployments on Langfuse v4 and later do not include them — migrate to v3 before upgrading. On Langfuse Cloud they remain available for a transition period.
Full endpoint mapping — only reads change, the write endpoints stay as they are:
| Existing usage | Recommended replacement |
|---|---|
List scores with GET /api/public/v2/scores | Use GET /api/public/v3/scores |
Fetch one score with GET /api/public/v2/scores/{scoreId} | Use GET /api/public/v3/scores?id=<scoreId> — v3 has no get-by-id endpoint; the id filter replaces it |
Create scores with POST /api/public/scores | Unchanged |
Delete scores with DELETE /api/public/scores/{scoreId} | Unchanged |
In the SDKs, replace langfuse.api.scores.get_many(...) (Python) / langfuse.api.scores.getMany(...) (JS/TS) with the v3 client shown under SDK access.
Response Changes
One typed value field. The v2 API split score values across a numeric value and a separate stringValue field. v3 returns a single value typed by dataType (see above):
dataType | v2 behavior | v3 value |
|---|---|---|
NUMERIC | value (number) | number |
BOOLEAN | value (1/0) plus stringValue ("True"/"False") | boolean |
CATEGORICAL | value (numeric category mapping from the score config; not meaningful without one) plus stringValue (category string) | string |
TEXT | stringValue only | string |
CORRECTION | value (always 0) plus stringValue (correction content) | string |
When migrating, read value directly and branch on dataType if your pipeline handles mixed score types.
subject object replaces flat reference fields. v2 score objects carried flat, nullable traceId, observationId, sessionId, and datasetRunId fields. v3 replaces them with the opt-in subject object (request fields=subject); dataset runs appear as kind: "experiment" (v2's datasetRunId).
Re-partitioned field groups. v2 had two field groups, score and trace, both returned by default. v3 always returns the core fields and offers details, subject, and annotation as opt-ins. The v2 trace group (the trace's userId, tags, environment, sessionId) has no v3 equivalent because v3 does not join trace data — see removed capabilities.
Query Changes
- Cursor pagination replaces offset pagination. v2's
pageparameter andtotalItems/totalPagesmeta are gone — passlimitand follow thecursorinstead. operatoris replaced by typed value filters. Usevalue(exact multi-match) orvalueMin/valueMax(numeric range).- Renamed parameters:
scoreIds→id,datasetRunId→experimentId(same comma-separated semantics). - Stricter validation:
traceId/sessionId/experimentIdare mutually exclusive andobservationIdrequirestraceId; invalid combinations return HTTP 400 instead of being silently ignored.
Removed Capabilities and Workarounds
| v2 capability | v3 replacement |
|---|---|
userId / traceTags filters | Removed (HTTP 400) — v3 does not join trace data by design. See workaround below. |
operator parameter | Use value (exact multi-match) or valueMin/valueMax (numeric range). |
Raw JSON filter parameter | Map each condition to the typed query parameters. See metadata note below. |
totalItems / totalPages in meta | Removed by design — iterate the cursor. |
Filtering by trace properties (userId, traceTags):
- Row-level: query the traces API with
userId/tagsfilters first to collect the matching trace IDs, then pass them to v3's multi-valuetraceIdfilter (e.g.traceId=t-1,t-2,t-3). - Aggregates: if you only need aggregate score metrics per user, tag, or other trace-level dimensions, use the Metrics API instead — it supports trace-level dimensions and filters directly.
Filtering by score metadata: the v2 JSON filter parameter supported metadata key-value conditions (stringObject). v3 has no metadata filter — request fields=details and filter on metadata client-side.
Row-count differences after migration: because v2 joins trace data by default, it silently drops trace-level scores whose referenced trace cannot be found (e.g., the trace was deleted or never ingested). v3 queries scores directly and returns every matching score, so v3 can return rows that the same query on v2 did not.
Last edited