Java client for the Langfuse API providing access to observability and analytics features for LLM applications
The Ingestion API provides batched ingestion of tracing data for Langfuse. This is the primary API for tracing operations, supporting event deduplication and multiple event types in a single batch. The API handles traces, observations (spans, events, generations), and scores.
Note: The recommended approach for tracing is via OpenTelemetry instrumentation rather than direct Ingestion API usage, as it provides more detailed and standardized tracing without requiring manual batching and update handling.
Client for batched ingestion of tracing events.
/**
* Batched ingestion for Langfuse Tracing
*
* Features:
* - Event deduplication using event IDs
* - Multiple event types in a single batch
* - Batch size limit: 3.5 MB
* - Returns 207 Multi-Status with individual error details
* instead of 4xx for input errors
*
* @param request Batch of ingestion events
* @param requestOptions Optional request configuration
* @return Response with success/error details for each event
*/
IngestionResponse batch(IngestionRequest request);
IngestionResponse batch(IngestionRequest request, RequestOptions requestOptions);Usage Example:
import com.langfuse.client.LangfuseClient;
import com.langfuse.client.resources.ingestion.requests.IngestionRequest;
import com.langfuse.client.resources.ingestion.types.*;
import com.langfuse.client.resources.commons.types.CreateScoreValue;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
LangfuseClient client = LangfuseClient.builder()
.url("https://cloud.langfuse.com")
.credentials("pk-lf-...", "sk-lf-...")
.build();
// Create a batch of events
// Note: All event types use staged builders requiring id(), timestamp(), body() in that order
IngestionRequest request = IngestionRequest.builder()
.batch(List.of(
// Trace event - staged builder: id() -> timestamp() -> body() -> metadata() (optional) -> build()
IngestionEvent.traceCreate(TraceEvent.builder()
.id("event-trace-123") // Event ID for deduplication (required first)
.timestamp(OffsetDateTime.now().toString()) // Event timestamp as String ISO 8601 (required second)
.body(TraceBody.builder() // Event body (required third)
.id("trace-123")
.name("User Query")
.userId("user-456")
.input(Map.of("query", "What is AI?"))
.timestamp(OffsetDateTime.now())
.build())
.metadata(Map.of("source", "api")) // Optional event metadata
.build()),
// Span event - staged builder: id() -> timestamp() -> body() -> metadata() (optional) -> build()
IngestionEvent.spanCreate(CreateSpanEvent.builder()
.id("event-span-123") // Event ID for deduplication (required first)
.timestamp(OffsetDateTime.now().plusSeconds(1).toString()) // Event timestamp as String ISO 8601 (required second)
.body(CreateSpanBody.builder() // Event body (required third)
.id("span-123")
.traceId("trace-123")
.name("LLM Call")
.startTime(OffsetDateTime.now().plusSeconds(1))
.endTime(OffsetDateTime.now().plusSeconds(3))
.input(Map.of("prompt", "What is AI?"))
.output(Map.of("response", "AI is..."))
.build())
.build()),
// Score event - staged builder: id() -> timestamp() -> body() -> metadata() (optional) -> build()
IngestionEvent.scoreCreate(ScoreEvent.builder()
.id("event-score-123") // Event ID for deduplication (required first)
.timestamp(OffsetDateTime.now().plusSeconds(2).toString()) // Event timestamp as String ISO 8601 (required second)
.body(ScoreBody.builder() // Score body uses staged builder: name() -> value() -> other fields -> build()
.name("quality") // Required first: score name
.value(CreateScoreValue.of(0.95)) // Required second: score value as CreateScoreValue union
.id("score-123") // Optional: score ID (available in _FinalStage)
.traceId("trace-123") // Optional: trace ID
.build())
.build())
))
.build();
// Send batch
IngestionResponse response = client.ingestion().batch(request);
// Check results
for (IngestionSuccess success : response.getSuccesses()) {
System.out.println("Success: " + success.getId());
}
for (IngestionError error : response.getErrors()) {
System.err.println("Error: " + error.getId() + " - " + error.getMessage());
}/**
* Batch ingestion request containing multiple events
*/
public final class IngestionRequest {
List<IngestionEvent> getBatch();
Optional<Object> getMetadata();
static Builder builder();
}Union type representing all possible event types.
/**
* Union type for ingestion events
* Supports all event types for tracing
*/
public final class IngestionEvent {
// Factory methods for creating typed events
static IngestionEvent traceCreate(TraceEvent value);
static IngestionEvent scoreCreate(ScoreEvent value);
static IngestionEvent observationCreate(CreateObservationEvent value);
static IngestionEvent observationUpdate(UpdateObservationEvent value);
static IngestionEvent eventCreate(CreateEventEvent value);
static IngestionEvent spanCreate(CreateSpanEvent value);
static IngestionEvent spanUpdate(UpdateSpanEvent value);
static IngestionEvent generationCreate(CreateGenerationEvent value);
static IngestionEvent generationUpdate(UpdateGenerationEvent value);
static IngestionEvent sdkLog(SdkLogEvent value);
// Visitor pattern for type-safe access
<T> T visit(Visitor<T> visitor);
}Create or update a trace.
/**
* Event for creating/updating a trace
* Type: "trace-create"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(TraceBody) - Trace data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class TraceEvent {
String getId(); // Event ID for deduplication
String getTimestamp(); // Event timestamp as String (ISO 8601)
Optional<Object> getMetadata(); // Optional SDK metadata
TraceBody getBody();
static IdStage builder(); // Returns staged builder starting with id()
}Create a score for a trace or observation.
/**
* Event for creating a score
* Type: "score-create"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(ScoreBody) - Score data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class ScoreEvent {
String getId(); // Event ID for deduplication
String getTimestamp(); // Event timestamp as String (ISO 8601)
Optional<Object> getMetadata(); // Optional SDK metadata
ScoreBody getBody();
static IdStage builder(); // Returns staged builder starting with id()
}Observations represent spans, events, and generations in traces.
/**
* Event for creating an observation
* Type: "observation-create"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(ObservationBody) - Observation data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class CreateObservationEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
ObservationBody getBody();
static IdStage builder();
}
/**
* Event for updating an observation
* Type: "observation-update"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(OptionalObservationBody) - Observation data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class UpdateObservationEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
OptionalObservationBody getBody();
static IdStage builder();
}Spans represent operations or sub-processes within a trace.
/**
* Event for creating a span
* Type: "span-create"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(CreateSpanBody) - Span data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class CreateSpanEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
CreateSpanBody getBody();
static IdStage builder();
}
/**
* Event for updating a span
* Type: "span-update"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(UpdateSpanBody) - Span data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class UpdateSpanEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
UpdateSpanBody getBody();
static IdStage builder();
}Events represent point-in-time occurrences within a trace.
/**
* Event for creating an event
* Type: "event-create"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(CreateEventBody) - Event data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class CreateEventEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
CreateEventBody getBody();
static IdStage builder();
}Generations represent LLM generation calls within a trace.
/**
* Event for creating a generation
* Type: "generation-create"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(CreateGenerationBody) - Generation data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class CreateGenerationEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
CreateGenerationBody getBody();
static IdStage builder();
}
/**
* Event for updating a generation
* Type: "generation-update"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(UpdateGenerationBody) - Generation data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class UpdateGenerationEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
UpdateGenerationBody getBody();
static IdStage builder();
}SDK log messages for debugging.
/**
* Event for SDK log messages
* Type: "sdk-log"
*
* Staged Builder Pattern (required order):
* 1. id(String) - Event ID for deduplication
* 2. timestamp(String) - Event timestamp (ISO 8601 format)
* 3. body(SdkLogBody) - Log message data
* 4. metadata(Object) - Optional SDK metadata
* 5. build() - Build the event
*/
public final class SdkLogEvent {
String getId();
String getTimestamp();
Optional<Object> getMetadata();
SdkLogBody getBody();
static IdStage builder();
}/**
* Trace data for trace events
*/
public final class TraceBody {
Optional<String> getId(); // Unique trace ID
Optional<OffsetDateTime> getTimestamp(); // Timestamp as OffsetDateTime
Optional<String> getName();
Optional<String> getUserId();
Optional<Object> getMetadata();
Optional<String> getRelease();
Optional<String> getVersion();
Optional<String> getSessionId();
Optional<Object> getInput();
Optional<Object> getOutput();
Optional<List<String>> getTags();
Optional<String> getEnvironment();
Optional<Boolean> getPublic(); // Make trace publicly accessible
static Builder builder();
}/**
* Score data for score events
* Uses staged builder: name() -> value() -> optional fields -> build()
*/
public final class ScoreBody {
Optional<String> getId(); // Unique score ID
Optional<String> getTraceId();
String getName(); // Required: score name
CreateScoreValue getValue(); // Required: Union type (String, Integer, or Double)
Optional<String> getSessionId();
Optional<String> getObservationId();
Optional<String> getDatasetRunId();
Optional<String> getComment();
Optional<Object> getMetadata();
Optional<ScoreDataType> getDataType(); // NUMERIC, CATEGORICAL, BOOLEAN
Optional<String> getConfigId();
Optional<String> getEnvironment();
static NameStage builder(); // Returns staged builder starting with name()
}/**
* Observation data (for spans, events, generations)
*/
public final class ObservationBody {
Optional<String> getId(); // Unique observation ID
Optional<String> getTraceId();
ObservationType getType(); // SPAN, EVENT, GENERATION (required)
Optional<String> getName();
Optional<OffsetDateTime> getStartTime(); // Start time as OffsetDateTime
Optional<OffsetDateTime> getEndTime(); // End time as OffsetDateTime
Optional<OffsetDateTime> getCompletionStartTime(); // Completion start time as OffsetDateTime
Optional<String> getModel();
Optional<Map<String, MapValue>> getModelParameters();
Optional<Object> getInput();
Optional<Object> getOutput();
Optional<Usage> getUsage(); // Token/cost usage (from commons.types)
Optional<Object> getMetadata();
Optional<ObservationLevel> getLevel(); // DEBUG, DEFAULT, WARNING, ERROR
Optional<String> getStatusMessage();
Optional<String> getParentObservationId();
Optional<String> getVersion();
Optional<String> getEnvironment();
static Builder builder();
}/**
* Observation data with all optional fields (for updates)
* Contains a subset of ObservationBody fields, all optional
*/
public final class OptionalObservationBody {
Optional<String> getTraceId();
Optional<String> getName();
Optional<OffsetDateTime> getStartTime(); // Start time as OffsetDateTime
Optional<Object> getMetadata();
Optional<Object> getInput();
Optional<Object> getOutput();
Optional<ObservationLevel> getLevel();
Optional<String> getStatusMessage();
Optional<String> getParentObservationId();
Optional<String> getVersion();
Optional<String> getEnvironment();
static Builder builder();
}/**
* Span-specific body
* Shares fields with ObservationBody through interface implementation
*/
public final class CreateSpanBody {
// Has all ObservationBody fields
static Builder builder();
}
/**
* Update span body
* Shares fields with OptionalObservationBody through interface implementation
*/
public final class UpdateSpanBody {
static Builder builder();
}
/**
* Event-specific body
* Shares fields with ObservationBody through interface implementation
*/
public final class CreateEventBody {
static Builder builder();
}
/**
* Update event body
* Shares fields with OptionalObservationBody through interface implementation
*/
public final class UpdateEventBody {
static Builder builder();
}
/**
* Generation-specific body
* Shares fields with ObservationBody through interface implementation
*/
public final class CreateGenerationBody {
static Builder builder();
}
/**
* Update generation body
* Shares fields with OptionalObservationBody through interface implementation
*/
public final class UpdateGenerationBody {
static Builder builder();
}/**
* SDK log message data
*/
public final class SdkLogBody {
String getLog(); // Log message content
static Builder builder();
}Union type for different usage formats.
/**
* Union type for usage information
* Supports OpenAI usage formats and custom usage details
*/
public final class IngestionUsage {
static IngestionUsage openAiUsage(OpenAiUsage value);
static IngestionUsage usageDetails(UsageDetails value);
<T> T visit(Visitor<T> visitor);
}OpenAI-style usage with token counts.
/**
* OpenAI usage with token counts
*/
public final class OpenAiUsage {
Optional<Integer> getPromptTokens();
Optional<Integer> getCompletionTokens();
Optional<Integer> getTotalTokens();
static Builder builder();
}/**
* OpenAI completion usage schema
*/
public final class OpenAiCompletionUsageSchema {
Optional<Integer> getPromptTokens();
Optional<Integer> getCompletionTokens();
Optional<Integer> getTotalTokens();
static Builder builder();
}/**
* Extended OpenAI response usage schema
*/
public final class OpenAiResponseUsageSchema {
Optional<Integer> getPromptTokens();
Optional<Integer> getCompletionTokens();
Optional<Integer> getTotalTokens();
// Extended fields for detailed token breakdown
static Builder builder();
}/**
* Union type for detailed usage information
* Use static factory methods to create instances
*/
public final class UsageDetails {
// Factory methods for creating UsageDetails
static UsageDetails of(Map<String, Integer> value); // Custom usage map
static UsageDetails of(OpenAiCompletionUsageSchema value); // OpenAI completion usage
static UsageDetails of(OpenAiResponseUsageSchema value); // OpenAI response usage
// Visitor pattern for type-safe access
<T> T visit(Visitor<T> visitor);
}/**
* Response from batch ingestion
* HTTP 207 Multi-Status with individual event results
*/
public final class IngestionResponse {
List<IngestionSuccess> getSuccesses();
List<IngestionError> getErrors();
static Builder builder();
}/**
* Successful ingestion of an event
*/
public final class IngestionSuccess {
String getId(); // Event ID
int getStatus(); // HTTP status (200, 201, etc.)
static Builder builder();
}/**
* Failed ingestion of an event
*/
public final class IngestionError {
String getId(); // Event ID
int getStatus(); // HTTP status (400, 422, etc.)
String getMessage(); // Error message
Optional<String> getError(); // Additional error details
static Builder builder();
}/**
* Type of observation
*/
public enum ObservationType {
SPAN,
EVENT,
GENERATION
}/**
* Log level for observations
*/
public enum ObservationLevel {
DEBUG,
DEFAULT,
WARNING,
ERROR
}/**
* Unit for usage measurement
*/
public enum ModelUsageUnit {
CHARACTERS,
TOKENS,
REQUESTS,
IMAGES,
SECONDS
}/**
* Data type for scores
*/
public enum ScoreDataType {
NUMERIC,
CATEGORICAL,
BOOLEAN
}import com.langfuse.client.LangfuseClient;
import com.langfuse.client.resources.ingestion.requests.IngestionRequest;
import com.langfuse.client.resources.ingestion.types.*;
import com.langfuse.client.resources.commons.types.CreateScoreValue;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
public class TracingExample {
public static void main(String[] args) {
LangfuseClient client = LangfuseClient.builder()
.url("https://cloud.langfuse.com")
.credentials("pk-lf-...", "sk-lf-...")
.build();
String traceId = "trace-" + System.currentTimeMillis();
String spanId = "span-" + System.currentTimeMillis();
String generationId = "gen-" + System.currentTimeMillis();
IngestionRequest request = IngestionRequest.builder()
.batch(List.of(
// 1. Create trace - Note: Staged builder requires id() -> timestamp() -> body()
IngestionEvent.traceCreate(TraceEvent.builder()
.id("event-" + traceId) // Event ID for deduplication
.timestamp(OffsetDateTime.now().toString()) // Event timestamp as String (ISO 8601)
.body(TraceBody.builder() // Trace body data
.id(traceId)
.name("Chat Completion")
.userId("user-123")
.sessionId("session-456")
.input(Map.of("message", "Hello, AI!"))
.timestamp(OffsetDateTime.now())
.tags(List.of("chat", "production"))
.build())
.build()),
// 2. Create span for preprocessing - Staged builder: id() -> timestamp() -> body()
IngestionEvent.spanCreate(CreateSpanEvent.builder()
.id("event-" + spanId) // Event ID for deduplication
.timestamp(OffsetDateTime.now().toString()) // Event timestamp as String (ISO 8601)
.body(CreateSpanBody.builder() // Span body data
.id(spanId)
.traceId(traceId)
.name("Preprocess Input")
.startTime(OffsetDateTime.now())
.endTime(OffsetDateTime.now().plusSeconds(1))
.input(Map.of("raw", "Hello, AI!"))
.output(Map.of("processed", "hello ai"))
.build())
.build()),
// 3. Create generation for LLM call - Staged builder: id() -> timestamp() -> body()
IngestionEvent.generationCreate(CreateGenerationEvent.builder()
.id("event-" + generationId) // Event ID for deduplication
.timestamp(OffsetDateTime.now().plusSeconds(1).toString()) // Event timestamp as String (ISO 8601)
.body(CreateGenerationBody.builder() // Generation body data
.id(generationId)
.traceId(traceId)
.parentObservationId(spanId)
.name("OpenAI Chat")
.model("gpt-4")
.startTime(OffsetDateTime.now().plusSeconds(1))
.endTime(OffsetDateTime.now().plusSeconds(3))
.input(Map.of("messages", List.of(
Map.of("role", "user", "content", "Hello, AI!")
)))
.output(Map.of("content", "Hello! How can I help you?"))
.usage(IngestionUsage.openAiUsage(
OpenAiUsage.builder()
.promptTokens(10)
.completionTokens(8)
.totalTokens(18)
.build()
))
.build())
.build()),
// 4. Add quality score - Staged builder: id() -> timestamp() -> body()
IngestionEvent.scoreCreate(ScoreEvent.builder()
.id("event-score-" + System.currentTimeMillis()) // Event ID for deduplication
.timestamp(OffsetDateTime.now().plusSeconds(3).toString()) // Event timestamp as String (ISO 8601)
.body(ScoreBody.builder() // Score body uses staged builder: name() -> value() -> optional fields
.name("quality") // Required first: score name
.value(CreateScoreValue.of(0.95)) // Required second: score value
.id("score-" + System.currentTimeMillis()) // Optional: available in _FinalStage
.traceId(traceId) // Optional: trace ID
.dataType(ScoreDataType.NUMERIC) // Optional: data type
.build())
.build())
))
.build();
// Send batch
IngestionResponse response = client.ingestion().batch(request);
// Handle results
System.out.println("Successes: " + response.getSuccesses().size());
System.out.println("Errors: " + response.getErrors().size());
for (IngestionError error : response.getErrors()) {
System.err.println("Failed event " + error.getId() + ": " + error.getMessage());
}
}
}traceId and parentObservationId to build trace hierarchyInstall with Tessl CLI
npx tessl i tessl/maven-com-langfuse--langfuse-java