Skip to main content
Migrate your Langfuse app to HoneyHive by switching tracing, importing datasets, and recreating the prompts your app uses. These examples use Langfuse Python SDK v4 (from langfuse import observe). If your app imports langfuse.decorators or langfuse.callback, see older Langfuse SDK versions.

Choose a path

Get the project-scoped API key for the HoneyHive project that receives your traces. Go to Settings > Project > API Keys, open the Project tab, and click Create API Key. Copy the key from the dialog and store it as HH_API_KEY - it is only shown once. This flow runs experiments and reads results, so it needs a project API key. To send its traces with an ingestion key instead, also set HH_INGESTION_API_KEY; the SDK uses each key where it applies.
  • Path A: start here if your Python app calls @observe, langfuse.openai, or CallbackHandler.
  • Path B: use this path if your app already emits OpenTelemetry, OpenInference, OpenLLMetry, or OTel GenAI spans. Non-Python apps use this path.

Path A: swap the Python SDK

Replace each Langfuse integration in your Python app.

1. Install HoneyHive

HoneyHive requires Python 3.11 or newer. Run these commands from your app’s directory. If it already has an active virtual environment, skip the first two commands.
Install only the instrumentors your app uses. For a LangChain app, install "honeyhive[openinference-langchain]". See the integrations overview for other libraries.
To see where to initialize the tracer for your environment, including AWS Lambda and long-running servers, see Tracer Initialization.

2. Replace @observe with @trace

OpenAIInstrumentor traces the OpenAI call without @trace. Keep @trace when you want to preserve the Langfuse @observe() function as a parent chain event with its own inputs and output. Run the HoneyHive version and inspect the session. Confirm the parent chain event, the model call under it, and the session user ID and tags. enrich_session() applies to the current HoneyHive session rather than a with block. If you used a Langfuse session_id for a multi-turn conversation, create or reuse a HoneyHive session for that conversation. Keep tracer.flush() at the end of scripts, notebooks, and Lambda handlers. Long-running servers flush on a timer.

3. Replace nested observations

Map each Langfuse span to a chain event, each generation to a model event, and each tool call to a tool event. When you call enrich_span_context(), pass tracer_instance=tracer and set attributes={"honeyhive_event_type": "chain"} (or "model" / "tool"). There is no event_type argument. You can also add @trace to child functions. See custom spans for both patterns.

4. Replace the Langfuse OpenAI client

Initialize the tracer, instrumentor, and standard OpenAI client in that order. OpenInference records each OpenAI call as a model event. See the OpenAI integration for configuration options.
Do not wrap the same client with langfuse.openai and OpenAIInstrumentor. Both would record the call.

5. Replace the LangChain callback

Instrument LangChain once, then remove the callback argument from each invocation. See the LangChain and LangGraph guides for configuration options.
Do not attach a Langfuse CallbackHandler and a HoneyHive LangChainInstrumentor to the same call. Both would record the invocation. To send spans to both products during migration, use two exporters in an OpenTelemetry Collector.

Path B: send OpenTelemetry spans to HoneyHive

Keep your existing instrumentation and point its standard OTLP/HTTP exporter at HoneyHive.

1. Configure the exporter

HoneyHive receives OTLP/HTTP traces at:
Set your HoneyHive API key and the exporter variables. The default US host is api.dp1.us.honeyhive.ai:
The OTLP/HTTP exporter adds /v1/traces to OTEL_EXPORTER_OTLP_ENDPOINT. If your exporter expects a traces-only URL, set the full path:
Use the API host shown in your HoneyHive dashboard if it differs. If your exporter can’t send http/protobuf, send OTLP/HTTP JSON.
OTEL_EXPORTER_* variables do not retarget LangfuseSpanProcessor. Replace that processor with an OTLP/HTTP exporter, or send the spans through an OpenTelemetry Collector.
Langfuse v4 filters spans to LLM-focused instrumentation by default. A generic OTLP exporter can also send HTTP, database, queue, and framework spans. Recreate your existing filter in the SDK or Collector if you want to keep the same span set. To send spans to both products during migration, configure two exporters in your OpenTelemetry Collector.

2. Send a request

Run one request through your app, then open Traces in HoneyHive. Check the trace hierarchy, inputs, outputs, and event types. HoneyHive maps OpenInference and OTel GenAI fields (inputs, outputs, model, tokens, span kind) to native fields. Other OTLP attributes, including langfuse.* attributes, remain in metadata but are not translated into HoneyHive fields. Remap the values you still need, and recreate Langfuse concepts such as public and bookmarked separately. See API mappings for fields that have a HoneyHive equivalent.

Move scores and evaluators

Write Langfuse scores as HoneyHive metrics on the current span or session:
Move each automated or human check to the matching HoneyHive feature: After you import a dataset, run your latest baseline with evaluate(dataset_id=...). Use the same judge prompt, model, and settings for comparison. To keep migrated evaluators and datasets in version control, follow Config as Code and apply them with the HoneyHive CLI or CI.

Move datasets

For text and structured data, export your Langfuse dataset as CSV, then upload it to HoneyHive. Confirm that the row count matches before you run an experiment. For multimodal datasets, export media separately and replace Langfuse media references with values your application can load.
This script creates a new HoneyHive dataset every time it runs. Before you run it, confirm that the destination name is unused or choose a unique name.HoneyHive dataset names allow letters, numbers, spaces, _, -, ', and &. Replace characters such as / if you reuse a Langfuse dataset name.

Move prompts

Use the Langfuse and HoneyHive Python SDKs to copy each prompt version you still use. HoneyHive stores every template as a message array, so the script converts a Langfuse text prompt to one user message. It preserves the remaining Langfuse configuration values without requiring manual Playground entry. Use this label mapping when you deploy: Langfuse moves the latest label whenever you create a version. HoneyHive environments point to configurations you deploy explicitly, so choose which version reaches dev, staging, or prod.
Set the source prompt and destination configuration:
HH_PROMPT_MODEL is a fallback for Langfuse prompts whose config does not include a model.
The script stores the remaining Langfuse config fields in hyperparameters, including provider-specific fields such as tools, tool_choice, and response schemas. Your application can forward those fields to its model client. To use tools or response formats in the HoneyHive Playground, map them to the native selectedFunctions, functionCallParams, forceFunction, and responseFormat parameters.Run the script once per prompt version. Set HH_PROMPT_ENVS to every HoneyHive environment that version serves, such as staging,prod. Keep one configuration per prompt version instead of creating duplicate configurations with the same name. To promote a different version later, update the env lists on the affected configurations.Langfuse message placeholders have no direct HoneyHive configuration equivalent. The script stops when it finds one so you can insert those runtime messages in application code instead of silently dropping them.
Paste this prompt into your coding agent:
If Langfuse resolves langfuse.get_prompt("my-prompt", label="production") to version 3, find my-prompt-v3 in honeyhive.configurations.list() with prod in its env list:
Pass prompt_text or messages, plus configuration.parameters.hyperparameters, to the corresponding model client.

Check that it worked

  1. Send traffic that covers a normal request, a tool call, and an error.
  2. Open Traces in the project that owns your HH_API_KEY.
  3. Confirm the span hierarchy, model name, token usage, user ID, and custom metadata.
  4. Compare the dataset row count with Langfuse, then run one baseline experiment with evaluate(dataset_id=...).
After these checks pass, remove Langfuse:
Delete Langfuse imports such as observe, get_client, CallbackHandler, and langfuse.openai. Keep tracer.flush() in scripts and other short-lived processes.

Troubleshooting

Traces do not appear

  1. Confirm HH_API_KEY is set and belongs to the HoneyHive project you opened.
  2. Call HoneyHiveTracer.init(...) before instrument() and before you create model clients.
  3. Pass tracer_provider=tracer.provider to the instrumentor.
  4. Set HH_API_URL or pass server_url= to HoneyHiveTracer.init when you use self-hosted HoneyHive.
  5. Call tracer.flush() before a script, notebook, or Lambda handler exits.

OTLP environment variables have no effect

Remove LangfuseSpanProcessor and configure a standard OTLP exporter or OpenTelemetry Collector. OTEL_EXPORTER_* variables do not change where the Langfuse processor sends spans.

Nested spans are missing

Decorate child functions with @trace or wrap blocks in enrich_span_context(). A parent @trace does not create child spans around undecorated calls.

OpenAI calls are duplicated

Remove from langfuse.openai import openai or OpenAI before you enable OpenAIInstrumentor. Use the standard openai package.

LangChain calls appear twice

Remove the Langfuse CallbackHandler after you enable LangChainInstrumentor. Do not attach both to one invocation.

Dataset rows appear in metadata

Make sure DatapointMapping.inputs and ground_truth exactly match keys in each row. HoneyHive stores unmapped keys as metadata.

Appendix

API mappings

Use these tables to replace other Langfuse calls in your app.

Older Langfuse SDK versions

For Langfuse Python SDK v2 and v3, either update to v4 first or replace the older imports directly:

Optional historical backfill

HoneyHive does not provide a Langfuse importer. To backfill selected historical traces, recreate them with the current Sessions and Events APIs. Create new HoneyHive session and event UUIDs, and keep the original Langfuse IDs in metadata. Request fields=core,basic,time,io,metadata,model,usage,trace_context from the Langfuse Observations API v2. The endpoint returns only core,basic by default. Set expandMetadata to a comma-separated list of metadata keys whose values must not be truncated at 200 characters. Bound each request with fromStartTime and toStartTime, then follow meta.cursor until it is empty. Export scores separately with fields=details,subject from the Langfuse Scores API v3, following its cursor until it is empty. The Observations API does not return scores.

Trace to session

The Langfuse Observations API repeats trace context on each observation. Create one HoneyHive session per unique traceId and use fields from the root observation.POST /v1/sessions does not accept external_id. Store the Langfuse trace ID in session metadata.

Generation to model event

The Langfuse Observations API v2 returns input and output as raw strings. Decode valid JSON first. Keep decoded objects as they are. Wrap arrays, scalars, and plain strings in a named field such as {"value": ...}. HoneyHive session and event inputs and outputs must be objects, and event inputs is required.

Observation type to event type

Scores

POST /v1/sessions does not accept top-level metrics or feedback, but POST /v1/events accepts metrics. For an observation-subject score, put the selected numeric or boolean value in the mapped event’s metrics object so HoneyHive can filter and chart it. Preserve the complete score record under metadata.langfuse_scores[], including id, name, value, data_type, subject, comment, and source. For a trace-subject score, keep that record in the mapped session’s metadata. Langfuse session and experiment subjects have no one-to-one HoneyHive backfill target, so preserve their complete subject object and choose the corresponding imported session or experiment workflow. For live traffic, use tracer.enrich_span(metrics=...) or tracer.enrich_session(metrics=...).

Data to recreate

Langfuse public, bookmarked, prompt templates, dataset run history, and annotation queues do not map to historical session or event fields. Recreate the prompts, datasets, and review workflows you still use.

Tracing quickstart

Send a first OpenAI trace with OpenInference

Custom spans

Replace @observe with @trace and context managers

LangChain integration

Instrument LangChain and LangGraph without a callback handler

Run an experiment

Re-run a baseline with evaluate(dataset_id=…) after you import a dataset
Need help migrating from self-hosted Langfuse? Contact us.