Python v4 → v5
v5 removes APIs that v4 marked as deprecated and makes a few breaking changes of its own. It needs Python 3.11+ and Langfuse Cloud or self-hosted Langfuse v4.59.0 or later. Check Are you affected?; if nothing there applies, upgrade and you're done. Type checkers flag deprecated v4 calls (pyright reportDeprecated, mypy --enable-error-code deprecated); v4 didn't warn at runtime.
pip install --upgrade "langfuse>=5"Are you affected?
You only need to change something if one of these applies to you.
New in v5:
- You self-host Langfuse v3, or v4 older than v4.59.0 → v5 doesn't support it. Upgrade Langfuse, or stay on Python SDK v4. See Minimum versions.
- You run Python 3.10,
openai<1, orlangchain-core<1→ Minimum versions - You call
api.scores.createorapi.legacy.score_v1.*directly →api.scores.createtakes a list of scores - You call
api.scores_v3.get_many_v3→ Score reads move toapi.scores.get_many - You call
run_batched_evaluation()→ Batched evaluation reads observations only - You read metadata span attributes in
mask_otel_spansor a custom exporter, run experiments withsample_ratebelow 1, or a proxy rejects gzip-compressed requests → Under the hood
Previously deprecated in v4, now removed:
- You call deprecated
langfuse.api.*methods, for exampleapi.trace.get,api.sessions,api.legacy.*,api.ingestion, orapi.scores.get_many→ Deprecated Public API methods removed - You call
set_current_trace_io()orspan.set_trace_io()→ Trace input/output setters removed - You call
get_dataset_runs(),get_dataset_run(), ordelete_dataset_run()→ Dataset run helpers removed - You pass
blocked_instrumentation_scopes→blocked_instrumentation_scopesremoved
If none of these apply, upgrade and you're done.
Breaking changes
These are new in v5. Ordered by how many users they affect.
Minimum versions
Affects: self-hosted Langfuse v3 or older than v4.59.0, Python 3.10, and the OpenAI and LangChain integrations on older libraries.
| Requirement | v5 | If not met |
|---|---|---|
| Langfuse | Cloud, or self-hosted v4.59.0 or later | Unsupported. Upgrade Langfuse first, or stay on Python SDK v4 |
| Python | >=3.11 | pip install langfuse installs the latest v4 |
| OpenAI | openai>=1.0 | langfuse.openai no longer supports pre-1.0 SDKs |
| LangChain | langchain-core>=1 | ModuleNotFoundError when importing langfuse.langchain |
Self-hosting Langfuse v3? Upgrade to Langfuse v4 to use the new SDKs and get the latest features. Until then, stay on Python SDK v4. See the self-hosted compatibility matrix.
pip install --upgrade "langfuse>=5" "langchain-core>=1"The LangChain CallbackHandler now imports only from langchain_core, so you no longer need the langchain meta-package. Its import path and usage are unchanged.
api.scores.create takes a list of scores
Affects: direct calls to langfuse.api.scores.create(...) or langfuse.api.legacy.score_v1.*. create_score(), span.score(), and span.score_trace() are unchanged.
Breaks: TypeError for the old api.scores.create keyword arguments, and AttributeError for api.legacy.score_v1.
api.scores.create()takes a list ofCreateScoreRequestobjects asrequest=. If some scores are rejected, the response includesaccepted/rejectedcounts anderrors.api.legacy.score_v1.create(...)is gone. Useapi.scores.create(request=[...]).api.legacy.score_v1.delete(score_id=...)moves toapi.scores.delete(score_id=...).
# v4
langfuse.api.scores.create(name="accuracy", value=0.9, trace_id="trace-id")
langfuse.api.legacy.score_v1.delete(score_id="score-id")
# v5
from langfuse.api import CreateScoreRequest
langfuse.api.scores.create(
request=[CreateScoreRequest(name="accuracy", value=0.9, trace_id="trace-id")]
)
langfuse.api.scores.delete(score_id="score-id")Score reads move to api.scores.get_many
Affects: calls to langfuse.api.scores_v3.get_many_v3(...) or langfuse.async_api.scores_v3.get_many_v3(...).
Breaks: AttributeError, because api.scores_v3 no longer exists. There is no alias.
api.scores.get_many now does what api.scores_v3.get_many_v3 did, with the same arguments and response. If you called api.scores.get_many(score_ids=...) in v4, its response changes too; filter by id= instead (comma-separated IDs).
# v4
scores = langfuse.api.scores_v3.get_many_v3(trace_id="trace-id", fields="subject")
# v5
scores = langfuse.api.scores.get_many(trace_id="trace-id", fields="subject")Batched evaluation reads observations only
Affects: calls to langfuse.run_batched_evaluation().
Breaks (no prior deprecation): TypeError for scope, fetch_trace_fields, and _additional_trace_tags, and ValueError when resuming with a v4 token or with a different filter.
The function now reads from the v2 observations API and evaluates observations only.
scopeis removed. To evaluate whole traces, filter for the observations that represent them, for example bynameortype.fetch_trace_fieldsis renamed tofields(default"core,basic,io,metadata"). Field groups:core,basic,time,io,metadata,model,usage,prompt,metrics,trace_context.filtermust be a v2 filter JSON array.- The mapper receives an observation (
item), whoseinputandoutputare raw strings. _additional_trace_tagsis removed.- v4 resume tokens can't be reused.
# v4
langfuse.run_batched_evaluation(
scope="traces",
mapper=trace_mapper,
filter='{"tags": ["production"]}',
fetch_trace_fields="core,io",
evaluators=[accuracy],
)
# v5
langfuse.run_batched_evaluation(
mapper=observation_mapper, # receives item=ObservationV2
filter='[{"type": "string", "column": "type", "operator": "=", "value": "GENERATION"}]',
fields="core,basic,io,model",
evaluators=[accuracy],
)Previously deprecated, now removed
v4 already marked each of these as deprecated and named the replacement. If you moved to the replacement, nothing changes.
Deprecated Public API methods removed
Affects: calls to the methods below through langfuse.api.*.
Breaks: AttributeError. Each method was marked deprecated in v4, with its replacement named. The api.legacy namespace is gone. For details, see the SDK method quick reference.
| Removed | Replacement |
|---|---|
api.trace.get(...), api.trace.list(...) | api.observations.get_many(trace_id=..., ...) |
api.sessions.* | api.observations.get_many(session_id=..., ...) |
api.legacy.observations_v1.* | api.observations.get_many(...) |
api.legacy.metrics_v1.metrics(...) | api.metrics.metrics(...) |
api.scores.get_many(score_ids=...), api.scores.get_by_id(...) | api.scores.get_many(id=...) |
api.dataset_run_items.* | api.experiments.list_items(...) or the experiment runner |
api.datasets.get_run(...), .get_runs(...), .delete_run(...) | See Dataset run helpers removed |
api.ingestion.batch(...) | Tracing via OpenTelemetry (the SDK default), api.scores.create(request=[...]) for scores |
api.trace keeps delete and delete_multiple. api.datasets keeps list, get, and create. api.scores.get_many keeps its name but now behaves like scores_v3.get_many_v3; see Score reads move to api.scores.get_many.
# v4
trace = langfuse.api.trace.get("trace-id")
# v5
observations = langfuse.api.observations.get_many(trace_id="trace-id", fields="core,basic,io")Trace input/output setters removed
Affects: calls to set_current_trace_io() or span.set_trace_io(), or code that sets the trace-level input/output attributes.
Breaks: AttributeError. Both methods were documented as deprecated in v4. Trace input and output now come from the root observation, so set them there.
# v4
with langfuse.start_as_current_observation(as_type="span", name="request") as span:
langfuse.set_current_trace_io(input=query, output=result)
# v5
with langfuse.start_as_current_observation(as_type="span", name="request") as span:
span.update(input=query, output=result)
# or, inside the active root span: langfuse.update_current_span(input=..., output=...)Use propagate_attributes() for other trace attributes. set_current_trace_as_public() and span.set_trace_as_public() are unchanged. If you still use trace-level LLM-as-a-judge evaluators, migrate them to observation-level evaluators.
Dataset run helpers removed
Affects: calls to get_dataset_runs(), get_dataset_run(), or delete_dataset_run().
Breaks: AttributeError. The endpoints behind these helpers were deprecated in v4. Use the experiments API instead; "dataset run" and "experiment" mean the same thing.
| Removed | Replacement |
|---|---|
langfuse.get_dataset_runs(dataset_name=...) | langfuse.api.experiments.list(dataset_id=..., from_start_time=...) |
langfuse.get_dataset_run(dataset_name=..., run_name=...) | langfuse.api.experiments.list(...) and .list_items(...) |
langfuse.delete_dataset_run(dataset_name=..., run_name=...) | List the experiment items, then langfuse.api.trace.delete_multiple(...) |
See Dataset runs → Experiments for details.
blocked_instrumentation_scopes removed
Affects: code that passes blocked_instrumentation_scopes to Langfuse(...).
Breaks: TypeError. v4 raised a DeprecationWarning that pointed to should_export_span. Combine should_export_span with the default filter:
# v4
langfuse = Langfuse(blocked_instrumentation_scopes=["sqlite", "requests"])
# v5
from langfuse.span_filter import is_default_export_span
blocked = {"sqlite", "requests"}
langfuse = Langfuse(
should_export_span=lambda span: is_default_export_span(span)
and (span.instrumentation_scope is None or span.instrumentation_scope.name not in blocked)
)New features
These need no code changes.
- Experiment links.
result.experiment_idandresult.experiment_urllink directly to the experiment comparison view. - Typed metadata filters. For data sent with v5, numeric and boolean metadata keeps its type, and nested objects can be filtered by dotted path, such as
a.b = 1. Data sent with v4 keeps its previous shape. - Batch score creation.
api.scores.create()accepts a list ofCreateScoreRequestobjects. - Experiment records. Run evaluations are stored for local-data runs too. Dataset versions are recorded on items as
langfuse.experiment.item.version. - Lighter LangChain dependency. The
CallbackHandlerneeds onlylangchain-core.
Under the hood
These need no code changes unless you depend on the old behavior.
- Scores are sent in batches to
POST /api/public/scoresinstead of the legacy ingestion endpoint.create_score(),span.score(),span.score_trace(),flush_at, andflush_intervalwork as before. If the server accepts only part of a batch (HTTP 207), the SDK logs the accepted and rejected counts and errors and doesn't retry the rejected scores. Other 4xx responses are logged and not retried. Network errors, 429, and 5xx are retried with backoff. - Metadata values are JSON-encoded on the span, strings included. The
langfuse.observation.metadata.<key>andlangfuse.trace.metadata.<key>attributes hold'"prod"'for the stringprodand'5'for the number5. Langfuse decodes them only for spans from Python SDK v5 or later, which is what keeps value types intact. Ifmask_otel_spansor a custom exporter reads or writes these attributes, decode withjson.loads()and write replacements withjson.dumps(). Propagated metadata values can be any JSON value; the 200-character limit applies to the encoded value, so a string can have at most 198 characters. Saved metadata filters written for the old, untyped shape may not match new data. - Experiments are recorded through OpenTelemetry.
run_experiment()sends experiment and item attributes on its spans instead of callingPOST /api/public/dataset-run-items. The runner doesn't special-case sampling: withsample_ratebelow 1, sampled-out items and their scores don't reach Langfuse, while run-level scores are always stored. Use a client with the defaultsample_rate=1.0for complete experiments. - Span batches are gzip-compressed. If a proxy rejects compressed requests, set
Langfuse(otel_compression="none")orLANGFUSE_OTEL_COMPRESSION=none. The standardOTEL_EXPORTER_OTLP_TRACES_COMPRESSIONandOTEL_EXPORTER_OTLP_COMPRESSIONvariables are honored too. A customspan_exporteris not changed. - The default span filter's GenAI check requires the
gen_ai.prefix, dot included, so attributes likegen_ai_customno longer count.
Migration checklist
Work through this list, or paste it into your coding agent. Skip items that don't apply.
- Use Python 3.11+ and Langfuse Cloud or self-hosted Langfuse v4.59.0+. On self-hosted v3, stay on v4.
pip install --upgrade "langfuse>=5". With integrations:openai>=1,langchain-core>=1.api.scores.create(name=..., value=..., ...)→api.scores.create(request=[CreateScoreRequest(...)]), and handle partial rejections (rejected> 0).api.legacy.score_v1.create(...)→api.scores.create(request=[...]);api.legacy.score_v1.delete(score_id=...)→api.scores.delete(score_id=...).api.scores_v3.get_many_v3(...)→api.scores.get_many(...)(same forasync_api).- v2 score reads:
api.scores.get_many(score_ids=...)→api.scores.get_many(id=...);api.scores.get_by_id(...)→api.scores.get_many(id=...). run_batched_evaluation(): removescopeand_additional_trace_tags;fetch_trace_fields→fields; use a v2 filter JSON array; mapper receivesObservationV2; discard v4 resume tokens.api.trace.get/api.trace.list/api.sessions.*/api.legacy.observations_v1.*→api.observations.get_many(...).api.legacy.metrics_v1.metrics(...)→api.metrics.metrics(...).api.dataset_run_items.*,api.datasets.get_run(s),get_dataset_run(s)()→api.experiments.list(...)/api.experiments.list_items(...).delete_dataset_run()/api.datasets.delete_run()→ list experiment items, thenapi.trace.delete_multiple(trace_ids=[...]).api.ingestion.batch(...)→ SDK tracing (OpenTelemetry) for traces,api.scores.create(request=[...])for scores.set_current_trace_io()/span.set_trace_io()→span.update(input=..., output=...)on the root observation.blocked_instrumentation_scopes=[...]→should_export_spancombined withis_default_export_span.mask_otel_spansor custom exporters touchinglangfuse.*.metadata.<key>attributes:json.loads()values, writejson.dumps()values.- Experiments: run with
sample_rate=1.0. - Proxy rejects gzip: set
otel_compression="none"orLANGFUSE_OTEL_COMPRESSION=none.
Last updated on