Spans#

class Spans(client, *, _guard=None)#

Bases: object

Provides methods for interacting with span resources.

This class offers both regular and DataFrame-based methods for retrieving, logging, and managing spans and their annotations.

Examples

Non-DataFrame methods:

from phoenix.client import Client
client = Client()

# Get spans as list
spans = client.spans.get_spans(
    project_identifier="my-project",
    limit=100
)

# Get span annotations as list
annotations = client.spans.get_span_annotations(
    span_ids=["span1", "span2"],
    project_identifier="my-project"
)

# Log spans
spans = [
    {
        "id": "1",
        "name": "test",
        "context": {"trace_id": "123", "span_id": "456"},
    }
]
result = client.spans.log_spans(
    project_identifier="my-project",
    spans=spans
)
print(f"Queued {result['total_queued']} spans")

DataFrame methods:

from phoenix.client.types.spans import SpanQuery

# Get spans as DataFrame
query = SpanQuery().select(annotations["relevance"])
df = client.spans.get_spans_dataframe(query=query)

# Get span annotations as DataFrame
annotations_df = client.spans.get_span_annotations_dataframe(
    span_ids=["span1", "span2"],
    project_identifier="my-project"
)

# Delete a span
client.spans.delete(span_identifier="abc123def456")
add_document_annotation(*, span_id, document_position, annotation_name, annotator_kind='HUMAN', label=None, score=None, explanation=None, metadata=None, sync=False)#

Add a single span document annotation.

Parameters:
  • span_id (str) – The ID of the span to annotate.

  • document_position (int) – The 0-based index of the document within the span.

  • annotation_name (str) – The name of the annotation.

  • annotator_kind (Literal["LLM", "CODE", "HUMAN"]) – The kind of annotator used for the annotation. Must be one of “LLM”, “CODE”, or “HUMAN”. Defaults to “HUMAN”.

  • label (Optional[str]) – The label assigned by the annotation.

  • score (Optional[float]) – The score assigned by the annotation.

  • explanation (Optional[str]) – Explanation of the annotation result.

  • metadata (Optional[dict[str, Any]]) – Additional metadata for the annotation.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation ID. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, the inserted span document

annotation containing an ID. If sync is False, None.

Return type:

Optional[InsertedSpanDocumentAnnotation]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the response is invalid or if at least one of label, score, or explanation is not provided.

Example:

from phoenix.client import Client
client = Client()

# Add a single document annotation with sync response
annotation = client.spans.add_document_annotation(
    span_id="abc123",
    document_position=0,
    annotation_name="relevance",
    label="relevant",
    score=0.9,
    explanation="The document is relevant to the query.",
    sync=True,
)
add_span_annotation(*, span_id, annotation_name, annotator_kind='HUMAN', label=None, score=None, explanation=None, metadata=None, identifier=None, sync=False)#

Add a single span annotation.

Parameters:
  • span_id (str) – The ID of the span to annotate.

  • annotation_name (str) – The name of the annotation.

  • annotator_kind (Literal["LLM", "CODE", "HUMAN"]) – The kind of annotator used for the annotation. Must be one of “LLM”, “CODE”, or “HUMAN”. Defaults to “HUMAN”.

  • label (Optional[str]) – The label assigned by the annotation.

  • score (Optional[float]) – The score assigned by the annotation.

  • explanation (Optional[str]) – Explanation of the annotation result.

  • metadata (Optional[dict[str, Any]]) – Additional metadata for the annotation.

  • identifier (Optional[str]) – An optional identifier for the annotation. Each annotation is uniquely identified by the combination of name, span_id, and identifier (where a null identifier is equivalent to an empty string). If an annotation with the same name, span_id, and identifier already exists, it will be updated. Using a non-empty identifier allows you to have multiple annotations with the same name and span_id. Most of the time, you can leave this as None - it will also update the record with identifier=”” if it exists.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation ID. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, the inserted span annotation

containing an ID. If sync is False, None.

Return type:

Optional[InsertedSpanAnnotation]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the response is invalid or if at least one of label, score, or explanation is not provided.

Example:

from phoenix.client import Client
client = Client()

# Add a single annotation with sync response
annotation = client.spans.add_span_annotation(
    span_id="abc123",
    annotation_name="sentiment",
    label="positive",
    score=0.9,
    explanation="The text expresses a positive sentiment.",
    sync=True,
)
add_span_note(*, span_id, note)#

Add a note to a span.

Notes are append-only: each call creates a new note with an auto-generated UUIDv4 identifier, so multiple notes accumulate on the same span. Structured annotations, by contrast, are keyed by (name, span_id, identifier) — to keep multiple structured annotations with the same name on a span, supply distinct identifiers; otherwise re-writing the same name overwrites the existing one.

Parameters:
  • span_id (str) – The OpenTelemetry span ID of the span to add the note to.

  • note (str) – The text content of the note.

Returns:

The inserted span annotation containing the ID.

Return type:

InsertedSpanAnnotation

Raises:
  • ValueError – If span_id or note is empty (after stripping whitespace).

  • httpx.HTTPStatusError – If the span is not found (404) or other API errors.

  • httpx.HTTPError – If the request fails.

Example:

from phoenix.client import Client
client = Client()

# Add a note to a span
result = client.spans.add_span_note(
    span_id="abc123def456",
    note="This span shows interesting behavior.",
)
print(f"Note created with ID: {result['id']}")
delete(*, span_identifier, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Deletes a single span by identifier.

Important: This operation deletes ONLY the specified span itself and does NOT delete its descendants/children. All child spans will remain in the trace and become orphaned (their parent_id will point to a non-existent span).

Behavior:
  • Deletes only the target span (preserves all descendant spans)

  • If this was the last span in the trace, the trace record is also deleted

  • If the deleted span had a parent, its cumulative metrics (error count, token counts) are subtracted from all ancestor spans in the chain

Note

This operation is irreversible and may create orphaned spans.

Parameters:
  • span_identifier (str) – The span identifier: either a relay GlobalID or OpenTelemetry span_id.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Raises:
  • httpx.HTTPStatusError – If the span is not found (404) or other API errors.

  • httpx.TimeoutException – If the request times out.

Example:

from phoenix.client import Client
client = Client()

# Delete by OpenTelemetry span_id
client.spans.delete_span(span_identifier="051581bf3cb55c13")

# Delete by Phoenix Global ID
client.spans.delete(span_identifier="U3BhbjoxMjM=")
get_span_annotations(*, span_ids=None, spans=None, project_identifier, include_annotation_names=None, exclude_annotation_names=None, limit=1000, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Fetches span annotations and returns them as a list of SpanAnnotation objects.

Exactly one of span_ids or spans should be provided.

Parameters:
  • span_ids (Optional[Iterable[str]]) – An iterable of span IDs.

  • spans (Optional[Iterable[v1.Span]]) – A list of Span objects (typically returned by get_spans).

  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • include_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to include. If provided, only annotations with these names will be returned.

  • exclude_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to exclude from results. Defaults to [“note”] to exclude note annotations, which are reserved for notes added via the UI.

  • limit (int) – Maximum number of annotations returned per request page. Defaults to 1000.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A list of SpanAnnotation objects.

Return type:

list[SpanAnnotation]

Raises:
  • ValueError – If not exactly one of span_ids or spans is provided.

  • httpx.HTTPStatusError – If the API returns an error response.

get_span_annotations_dataframe(*, spans_dataframe=None, span_ids=None, spans=None, project_identifier='default', include_annotation_names=None, exclude_annotation_names=None, limit=1000, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Fetches span annotations and returns them as a pandas DataFrame.

Exactly one of spans_dataframe, span_ids, or spans should be provided.

Parameters:
  • spans_dataframe (Optional[pd.DataFrame]) – A DataFrame (typically returned by get_spans_dataframe) with a context.span_id or span_id column.

  • span_ids (Optional[Iterable[str]]) – An iterable of span IDs.

  • spans (Optional[Iterable[v1.Span]]) – A list of Span objects (typically returned by get_spans).

  • project_identifier (str) – The project identifier (name or ID) used in the API path. Defaults to “default”.

  • include_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to include. If provided, only annotations with these names will be returned.

  • exclude_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to exclude from results. Defaults to [“note”] to exclude note annotations, which are reserved for notes added via the UI.

  • limit (int) – Maximum number of annotations returned per request page. Defaults to 1000.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A DataFrame where each row corresponds to a single span annotation.

Return type:

pd.DataFrame

Raises:
  • ValueError – If not exactly one of spans_dataframe, span_ids, or spans is provided, or if the context.span_id or span_id column is missing from spans_dataframe.

  • ImportError – If pandas is not installed.

  • httpx.HTTPStatusError – If the API returns an error response.

get_spans(*, project_identifier, start_time=None, end_time=None, trace_ids=None, parent_id=None, name=None, span_kind=None, status_code=None, attributes=None, limit=100, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Retrieves spans with simple filtering options.

Parameters:
  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • start_time (Optional[datetime]) – Optional start time for filtering (inclusive lower bound).

  • end_time (Optional[datetime]) – Optional end time for filtering (exclusive upper bound).

  • trace_ids (Optional[Sequence[str]]) – Optional list of trace IDs to filter by. Requires Phoenix server >= 13.9.0.

  • parent_id (Optional[str]) – Optional parent span ID to filter by. Use “null” to get root spans only.

  • name (Optional[Union[str, Sequence[str]]]) – Optional span name(s) to filter by. Requires Phoenix server >= 13.15.0.

  • span_kind (Optional[Union[str, Sequence[str]]]) – Optional span kind(s) to filter by (e.g. LLM, CHAIN, TOOL). Requires Phoenix server >= 13.15.0.

  • status_code (Optional[Union[str, Sequence[str]]]) – Optional status code(s) to filter by (e.g. OK, ERROR, UNSET). Requires Phoenix server >= 13.15.0.

  • attributes (Optional[dict[str, Union[str, int, float, bool]]]) – Optional dictionary of attribute key-value pairs to filter by; multiple entries are AND-ed together. The Python type of each value selects how the stored attribute is matched: a str matches a stored string; int, float, and bool match the corresponding native type. To match a stored string whose contents look like a number or boolean (e.g. a user ID stored as "12345"), pass it as a Python str. Requires Phoenix server >= 14.9.0.

  • limit (int) – Maximum number of spans to return. Defaults to 100.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A list of Span objects.

Return type:

list[v1.Span]

Raises:
  • httpx.HTTPStatusError – If the API returns an error response.

  • ValueError – If a float value in attributes is non-finite (nan or inf).

get_spans_dataframe(*, query=None, start_time=None, end_time=None, limit=1000, root_spans_only=None, project_identifier=None, project_name=None, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Retrieves spans based on the provided filter conditions.

Parameters:
  • query (Optional[SpanQuery]) – A SpanQuery object defining the query criteria.

  • start_time (Optional[datetime]) – Optional start time for filtering.

  • end_time (Optional[datetime]) – Optional end time for filtering.

  • limit (int) – Maximum number of spans to return. Defaults to 1000.

  • root_spans_only (Optional[bool]) – Whether to return only root spans.

  • project_name (Optional[str]) – Optional project name to filter by. Deprecated, use project_identifier to also specify by the project id.

  • project_identifier (Optional[str]) – Optional project identifier (name or id) to filter by.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A pandas DataFrame containing the retrieved spans.

Return type:

pd.DataFrame

Raises:

ImportError – If pandas is not installed.

log_document_annotations(*, document_annotations, sync=False)#

Log multiple span document annotations.

Parameters:
  • document_annotations (list[SpanDocumentAnnotationData]) – A list of span document annotation data objects.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of inserted

span document annotations containing IDs. If sync is False, None.

Return type:

Optional[list[InsertedSpanDocumentAnnotation]]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If span_document_annotations is empty.

Example:

from phoenix.client import Client
from phoenix.client.resources.spans import SpanDocumentAnnotationData
client = Client()

# Log multiple document annotations
annotations = [
    SpanDocumentAnnotationData(
        name="relevance",
        span_id="span-123",
        document_position=0,
        annotator_kind="HUMAN",
        result={"label": "relevant", "score": 0.9}
    ),
    SpanDocumentAnnotationData(
        name="accuracy",
        span_id="span-123",
        document_position=1,
        annotator_kind="LLM",
        result={"label": "accurate", "score": 0.8}
    ),
]
client.spans.log_document_annotations(
    span_document_annotations=annotations
)
log_document_annotations_dataframe(*, dataframe, annotation_name=None, annotator_kind=None, sync=False)#

Log multiple span document annotations from a pandas DataFrame.

This method allows you to create multiple span document annotations at once by providing the data in a pandas DataFrame. The DataFrame can either include name or annotation_name columns (but not both) and annotator_kind column, or you can specify global values for all rows. The data is processed in chunks of 100 rows for efficient batch processing.

Parameters:
  • dataframe (pd.DataFrame) – A pandas DataFrame containing the annotation data. Must include either a “name” or “annotation_name” column (but not both) or provide a global annotation_name parameter. Similarly, must include an “annotator_kind” column or provide a global annotator_kind. The span_id and document_position can be either columns in the DataFrame or span_id will be taken from the DataFrame index. Optional columns include: “label”, “score”, “explanation”, and “metadata”.

  • annotation_name (Optional[str]) – The name to use for all annotations. If provided, this name will be used for all rows in the DataFrame. Cannot be used if the DataFrame contains a “name” or “annotation_name” column.

  • annotator_kind (Optional[Literal["LLM", "CODE", "HUMAN"]]) – The annotator kind to use for all annotations. If provided, this kind will be used for all rows in the DataFrame. Cannot be used if the DataFrame contains an “annotator_kind” column.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of inserted

span document annotations containing IDs. If sync is False, None.

Return type:

Optional[list[InsertedSpanDocumentAnnotation]]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the DataFrame is invalid or empty.

Example:

import pandas as pd
from phoenix.client import Client
client = Client()

# Log document annotations from DataFrame
df = pd.DataFrame({
    "name": ["relevance", "accuracy"],
    "span_id": ["span_123", "span_456"],
    "document_position": [0, 1],
    "annotator_kind": ["HUMAN", "LLM"],
    "label": ["relevant", "accurate"],
    "score": [0.9, 0.8]
})
client.spans.log_document_annotations_dataframe(dataframe=df)
log_span_annotations(*, span_annotations, sync=False)#

Log multiple span annotations.

Parameters:
  • span_annotations (Iterable[SpanAnnotationData]) – An iterable of span annotation data to log. Each annotation must include at least a span_id, name, and annotator_kind, and at least one of label, score, or explanation.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of inserted span annotations, each containing an ID. If sync is False, None.

Return type:

Optional[list[InsertedSpanAnnotation]]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the response is invalid or if the input is invalid.

log_span_annotations_dataframe(*, dataframe, annotator_kind=None, annotation_name=None, sync=False)#

Log multiple span annotations from a pandas DataFrame.

This method allows you to create multiple span annotations at once by providing the data in a pandas DataFrame. The DataFrame can either include name or annotation_name columns (but not both) and annotator_kind column, or you can specify global values for all rows. The data is processed in chunks of 100 rows for efficient batch processing.

Parameters:
  • dataframe (pd.DataFrame) – A pandas DataFrame containing the annotation data. Must include either a “name” or “annotation_name” column (but not both) or provide a global annotation_name parameter. Similarly, must include an “annotator_kind” column or provide a global annotator_kind. The span_id can be either a column in the DataFrame or will be taken from the DataFrame index. Optional columns include: “label”, “score”, “explanation”, “metadata”, and “identifier”.

  • annotator_kind (Optional[Literal["LLM", "CODE", "HUMAN"]]) – The kind of annotator used for all annotations. If provided, this value will be used for all rows and the DataFrame does not need to include an “annotator_kind” column. Must be one of “LLM”, “CODE”, or “HUMAN”.

  • annotation_name (Optional[str]) – The name to use for all annotations. If provided, this value will be used for all rows and the DataFrame does not need to include a “name” or “annotation_name” column.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of all inserted span

annotations. If sync is False, None.

Return type:

Optional[list[InsertedSpanAnnotation]]

Raises:
  • ImportError – If pandas is not installed.

  • ValueError – If the DataFrame is missing required columns, if both “name” and “annotation_name” columns are present, or if no valid annotation data is provided.

Example:

import pandas as pd
from phoenix.client import Client
client = Client()

# Using name and annotator_kind from DataFrame with span_id column
df1 = pd.DataFrame({
    "name": ["sentiment", "toxicity"],
    "span_id": ["span_123", "span_456"],
    "annotator_kind": ["HUMAN", "LLM"],
    "label": ["positive", "low"],
    "score": [0.9, 0.1]
})
client.spans.log_span_annotations_dataframe(dataframe=df1)

# Using annotation_name and annotator_kind from DataFrame
df2 = pd.DataFrame({
    "annotation_name": ["sentiment", "toxicity"],
    "span_id": ["span_789", "span_012"],
    "annotator_kind": ["HUMAN", "LLM"],
    "label": ["positive", "low"],
    "score": [0.9, 0.1]
})
client.spans.log_span_annotations_dataframe(dataframe=df2)

# Using global name and annotator_kind with span_id from index
df3 = pd.DataFrame({
    "label": ["positive", "low"]
}, index=["span_345", "span_678"])
client.spans.log_span_annotations_dataframe(
    dataframe=df3,
    annotation_name="sentiment",  # applies to all rows
    annotator_kind="HUMAN"  # applies to all rows
)
log_spans(*, project_identifier, spans, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Logs spans to a project.

If any spans are invalid or duplicates, no spans will be logged and a SpanCreationError will be raised with details about the failed spans.

Parameters:
  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • spans (Sequence[v1.Span]) – A sequence of Span objects to log.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A CreateSpansResponseBody with statistics about

the operation. When successful, total_queued will equal total_received.

Return type:

v1.CreateSpansResponseBody

Raises:
  • SpanCreationError – If any spans failed validation (invalid or duplicates).

  • httpx.HTTPStatusError – If the API returns an unexpected error response.

  • httpx.TimeoutException – If the request times out.

log_spans_dataframe(*, project_identifier, spans_dataframe, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Logs spans to a project from a pandas DataFrame.

If any spans are invalid or duplicates, no spans will be logged and a SpanCreationError will be raised with details about the failed spans.

Parameters:
  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • spans_dataframe (pd.DataFrame) – A pandas DataFrame with a context.span_id or span_id column.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A CreateSpansResponseBody with statistics about

the operation. When successful, total_queued will equal total_received.

Return type:

v1.CreateSpansResponseBody

Raises:
  • SpanCreationError – If any spans failed validation (invalid or duplicates).

  • httpx.HTTPStatusError – If the API returns an unexpected error response.

  • httpx.TimeoutException – If the request times out.

class AsyncSpans(client, *, _guard=None)#

Bases: object

Provides async methods for interacting with span resources.

This class offers both regular and DataFrame-based async methods for retrieving, logging, and managing spans and their annotations.

Examples

Non-DataFrame methods:

from phoenix.client import AsyncClient
client = AsyncClient()

# Get spans as list
spans = await client.spans.get_spans(
    project_identifier="my-project",
    limit=100
)

# Get span annotations as list
annotations = await client.spans.get_span_annotations(
    span_ids=["span1", "span2"],
    project_identifier="my-project"
)

# Log spans
spans = [
    {
        "id": "1",
        "name": "test",
        "context": {"trace_id": "123", "span_id": "456"},
    }
]
result = await client.spans.log_spans(
    project_identifier="my-project",
    spans=spans
)
print(f"Queued {result['total_queued']} spans")

DataFrame methods:

from phoenix.client.types.spans import SpanQuery

# Get spans as DataFrame
query = SpanQuery().select(annotations["relevance"])
df = await client.spans.get_spans_dataframe(query=query)

# Get span annotations as DataFrame
annotations_df = await client.spans.get_span_annotations_dataframe(
    span_ids=["span1", "span2"],
    project_identifier="my-project"
)

# Delete a span
await client.spans.delete(span_identifier="abc123def456")
async add_document_annotation(*, span_id, document_position, annotation_name, annotator_kind='HUMAN', label=None, score=None, explanation=None, metadata=None, sync=False)#

Add a single span document annotation asynchronously.

Parameters:
  • span_id (str) – The ID of the span to annotate.

  • document_position (int) – The 0-based index of the document within the span.

  • annotation_name (str) – The name of the annotation.

  • annotator_kind (Literal["LLM", "CODE", "HUMAN"]) – The kind of annotator used for the annotation. Must be one of “LLM”, “CODE”, or “HUMAN”. Defaults to “HUMAN”.

  • label (Optional[str]) – The label assigned by the annotation.

  • score (Optional[float]) – The score assigned by the annotation.

  • explanation (Optional[str]) – Explanation of the annotation result.

  • metadata (Optional[dict[str, Any]]) – Additional metadata for the annotation.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation ID. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, the inserted span document

annotation containing an ID. If sync is False, None.

Return type:

Optional[InsertedSpanDocumentAnnotation]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the response is invalid or if at least one of label, score, or explanation is not provided.

Example:

from phoenix.client import AsyncClient
async_client = AsyncClient()

# Add a single document annotation with sync response
annotation = await async_client.spans.add_document_annotation(
    span_id="abc123",
    document_position=0,
    annotation_name="relevance",
    label="relevant",
    score=0.9,
    explanation="The document is relevant to the query.",
    sync=True,
)
async add_span_annotation(*, span_id, annotation_name, annotator_kind='HUMAN', label=None, score=None, explanation=None, metadata=None, identifier=None, sync=False)#

Add a single span annotation asynchronously.

Parameters:
  • span_id (str) – The ID of the span to annotate.

  • annotation_name (str) – The name of the annotation.

  • annotator_kind (Literal["LLM", "CODE", "HUMAN"]) – The kind of annotator used for the annotation. Must be one of “LLM”, “CODE”, or “HUMAN”. Defaults to “HUMAN”.

  • label (Optional[str]) – The label assigned by the annotation.

  • score (Optional[float]) – The score assigned by the annotation.

  • explanation (Optional[str]) – Explanation of the annotation result.

  • metadata (Optional[dict[str, Any]]) – Additional metadata for the annotation.

  • identifier (Optional[str]) – An optional identifier for the annotation. Each annotation is uniquely identified by the combination of name, span_id, and identifier (where a null identifier is equivalent to an empty string). If an annotation with the same name, span_id, and identifier already exists, it will be updated. Using a non-empty identifier allows you to have multiple annotations with the same name and span_id. Most of the time, you can leave this as None - it will also update the record with identifier=”” if it exists.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation ID. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, the inserted span annotation containing an ID. If sync is False, None.

Return type:

Optional[InsertedSpanAnnotation]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the response is invalid or if at least one of label, score, or explanation is not provided.

Example:

from phoenix.client import AsyncClient
async_client = AsyncClient()

# Add a single annotation with sync response
annotation = await async_client.spans.add_span_annotation(
    span_id="abc123",
    annotation_name="sentiment",
    label="positive",
    score=0.9,
    explanation="The text expresses a positive sentiment.",
    sync=True,
)
async add_span_note(*, span_id, note)#

Add a note to a span asynchronously.

Notes are append-only: each call creates a new note with an auto-generated UUIDv4 identifier, so multiple notes accumulate on the same span. Structured annotations, by contrast, are keyed by (name, span_id, identifier) — to keep multiple structured annotations with the same name on a span, supply distinct identifiers; otherwise re-writing the same name overwrites the existing one.

Parameters:
  • span_id (str) – The OpenTelemetry span ID of the span to add the note to.

  • note (str) – The text content of the note.

Returns:

The inserted span annotation containing the ID.

Return type:

InsertedSpanAnnotation

Raises:
  • ValueError – If span_id or note is empty (after stripping whitespace).

  • httpx.HTTPStatusError – If the span is not found (404) or other API errors.

  • httpx.HTTPError – If the request fails.

Example:

from phoenix.client import AsyncClient
client = AsyncClient()

# Add a note to a span
result = await client.spans.add_span_note(
    span_id="abc123def456",
    note="This span shows interesting behavior.",
)
print(f"Note created with ID: {result['id']}")
async delete(*, span_identifier, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Deletes a single span by identifier.

Important: This operation deletes ONLY the specified span itself and does NOT delete its descendants/children. All child spans will remain in the trace and become orphaned (their parent_id will point to a non-existent span).

Behavior:
  • Deletes only the target span (preserves all descendant spans)

  • If this was the last span in the trace, the trace record is also deleted

  • If the deleted span had a parent, its cumulative metrics (error count, token counts) are subtracted from all ancestor spans in the chain

Note

This operation is irreversible and may create orphaned spans.

Parameters:
  • span_identifier (str) – The span identifier: either a relay GlobalID or OpenTelemetry span_id.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Raises:
  • httpx.HTTPStatusError – If the span is not found (404) or other API errors.

  • httpx.TimeoutException – If the request times out.

Examples:

from phoenix.client import AsyncClient
client = AsyncClient()

# Delete by OpenTelemetry span_id
await client.spans.delete(span_identifier="abc123def456")

# Delete by Phoenix Global ID
await client.spans.delete(span_identifier="U3BhbjoxMjM=")
async get_span_annotations(*, span_ids=None, spans=None, project_identifier, include_annotation_names=None, exclude_annotation_names=None, limit=1000, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Fetches span annotations and returns them as a list of SpanAnnotation objects.

Exactly one of span_ids or spans should be provided.

Parameters:
  • span_ids (Optional[Iterable[str]]) – An iterable of span IDs.

  • spans (Optional[Iterable[v1.Span]]) – A list of Span objects (typically returned by get_spans).

  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • include_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to include. If provided, only annotations with these names will be returned.

  • exclude_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to exclude from results. Defaults to [“note”] to exclude note annotations, which are reserved for notes added via the UI.

  • limit (int) – Maximum number of annotations returned per request page. Defaults to 1000.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A list of SpanAnnotation objects.

Return type:

list[SpanAnnotation]

Raises:
  • ValueError – If not exactly one of span_ids or spans is provided.

  • httpx.HTTPStatusError – If the API returns an error response.

async get_span_annotations_dataframe(*, spans_dataframe=None, span_ids=None, spans=None, project_identifier, include_annotation_names=None, exclude_annotation_names=None, limit=1000, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Fetches span annotations and returns them as a pandas DataFrame.

Exactly one of spans_dataframe, span_ids, or spans should be provided.

Parameters:
  • spans_dataframe (Optional[pd.DataFrame]) – A DataFrame (typically returned by get_spans_dataframe) with a context.span_id or span_id column.

  • span_ids (Optional[Iterable[str]]) – An iterable of span IDs.

  • spans (Optional[Iterable[v1.Span]]) – A list of Span objects (typically returned by get_spans).

  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • include_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to include. If provided, only annotations with these names will be returned.

  • exclude_annotation_names (Optional[Sequence[str]]) – Optional list of annotation names to exclude from results. Defaults to [“note”] to exclude note annotations, which are reserved for notes added via the UI.

  • limit (int) – Maximum number of annotations returned per request page. Defaults to 1000.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A DataFrame where each row corresponds to a single span annotation.

Return type:

pd.DataFrame

Raises:
  • ValueError – If not exactly one of spans_dataframe, span_ids, or spans is provided, or if the context.span_id or span_id column is missing from spans_dataframe.

  • ImportError – If pandas is not installed.

  • httpx.HTTPStatusError – If the API returns an error response.

async get_spans(*, project_identifier, start_time=None, end_time=None, trace_ids=None, parent_id=None, name=None, span_kind=None, status_code=None, attributes=None, limit=100, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Retrieves spans with simple filtering options.

Parameters:
  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • start_time (Optional[datetime]) – Optional start time for filtering (inclusive lower bound).

  • end_time (Optional[datetime]) – Optional end time for filtering (exclusive upper bound).

  • trace_ids (Optional[Sequence[str]]) – Optional list of trace IDs to filter by. Requires Phoenix server >= 13.9.0.

  • parent_id (Optional[str]) – Optional parent span ID to filter by. Use “null” to get root spans only.

  • name (Optional[Union[str, Sequence[str]]]) – Optional span name(s) to filter by. Requires Phoenix server >= 13.15.0.

  • span_kind (Optional[Union[str, Sequence[str]]]) – Optional span kind(s) to filter by (e.g. LLM, CHAIN, TOOL). Requires Phoenix server >= 13.15.0.

  • status_code (Optional[Union[str, Sequence[str]]]) – Optional status code(s) to filter by (e.g. OK, ERROR, UNSET). Requires Phoenix server >= 13.15.0.

  • attributes (Optional[dict[str, Union[str, int, float, bool]]]) – Optional dictionary of attribute key-value pairs to filter by; multiple entries are AND-ed together. The Python type of each value selects how the stored attribute is matched: a str matches a stored string; int, float, and bool match the corresponding native type. To match a stored string whose contents look like a number or boolean (e.g. a user ID stored as "12345"), pass it as a Python str. Requires Phoenix server >= 14.9.0.

  • limit (int) – Maximum number of spans to return. Defaults to 100.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A list of Span objects.

Return type:

list[v1.Span]

Raises:
  • httpx.HTTPStatusError – If the API returns an error response.

  • ValueError – If a float value in attributes is non-finite (nan or inf).

async get_spans_dataframe(*, query=None, start_time=None, end_time=None, limit=1000, root_spans_only=None, project_name=None, project_identifier=None, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Retrieves spans based on the provided filter conditions.

Parameters:
  • query (Optional[SpanQuery]) – A SpanQuery object defining the query criteria.

  • start_time (Optional[datetime]) – Optional start time for filtering.

  • end_time (Optional[datetime]) – Optional end time for filtering.

  • limit (int) – Maximum number of spans to return. Defaults to 1000.

  • root_spans_only (Optional[bool]) – Whether to return only root spans.

  • project_name (Optional[str]) – Optional project name to filter by. Deprecated, use project_identifier to also specify by the project id.

  • project_identifier (Optional[str]) – Optional project identifier (name or id) to filter by.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A pandas DataFrame containing the retrieved spans.

Return type:

pd.DataFrame

Raises:

ImportError – If pandas is not installed.

async log_document_annotations(*, document_annotations, sync=False)#

Log multiple span document annotations asynchronously.

Parameters:
  • document_annotations (list[SpanDocumentAnnotationData]) – A list of span document annotation data objects.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of inserted

span document annotations containing IDs. If sync is False, None.

Return type:

Optional[list[InsertedSpanDocumentAnnotation]]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If span_document_annotations is empty.

Example:

from phoenix.client import AsyncClient
from phoenix.client.resources.spans import SpanDocumentAnnotationData
async_client = AsyncClient()

# Log multiple document annotations
annotations = [
    SpanDocumentAnnotationData(
        name="relevance",
        span_id="span-123",
        document_position=0,
        annotator_kind="HUMAN",
        result={"label": "relevant", "score": 0.9}
    ),
    SpanDocumentAnnotationData(
        name="accuracy",
        span_id="span-123",
        document_position=1,
        annotator_kind="LLM",
        result={"label": "accurate", "score": 0.8}
    ),
]
await async_client.spans.log_document_annotations(
    span_document_annotations=annotations
)
async log_document_annotations_dataframe(*, dataframe, annotation_name=None, annotator_kind=None, sync=False)#

Log multiple span document annotations from a pandas DataFrame asynchronously.

This method allows you to create multiple span document annotations at once by providing the data in a pandas DataFrame. The DataFrame can either include name or annotation_name columns (but not both) and annotator_kind column, or you can specify global values for all rows. The data is processed in chunks of 100 rows for efficient batch processing.

Parameters:
  • dataframe (pd.DataFrame) – A pandas DataFrame containing the annotation data. Must include either a “name” or “annotation_name” column (but not both) or provide a global annotation_name parameter. Similarly, must include an “annotator_kind” column or provide a global annotator_kind. The span_id and document_position can be either columns in the DataFrame or span_id will be taken from the DataFrame index. Optional columns include: “label”, “score”, “explanation”, “metadata”, and “identifier”.

  • annotation_name (Optional[str]) – The name to use for all annotations. If provided, this name will be used for all rows in the DataFrame. Cannot be used if the DataFrame contains a “name” or “annotation_name” column.

  • annotator_kind (Optional[Literal["LLM", "CODE", "HUMAN"]]) – The annotator kind to use for all annotations. If provided, this kind will be used for all rows in the DataFrame. Cannot be used if the DataFrame contains an “annotator_kind” column.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of inserted

span document annotations containing IDs. If sync is False, None.

Return type:

Optional[list[InsertedSpanDocumentAnnotation]]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the DataFrame is invalid or empty.

Example:

import pandas as pd
from phoenix.client import AsyncClient
async_client = AsyncClient()

# Log document annotations from DataFrame
df = pd.DataFrame({
    "name": ["relevance", "accuracy"],
    "span_id": ["span_123", "span_456"],
    "document_position": [0, 1],
    "annotator_kind": ["HUMAN", "LLM"],
    "label": ["relevant", "accurate"],
    "score": [0.9, 0.8]
})
await async_client.spans.log_document_annotations_dataframe(dataframe=df)
async log_span_annotations(*, span_annotations, sync=False)#

Log multiple span annotations asynchronously.

Parameters:
  • span_annotations (Iterable[SpanAnnotationData]) – An iterable of span annotation data to log. Each annotation must include at least a span_id, name, and annotator_kind, and at least one of label, score, or explanation.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of inserted span annotations, each containing an ID. If sync is False, None.

Return type:

Optional[list[InsertedSpanAnnotation]]

Raises:
  • httpx.HTTPError – If the request fails.

  • ValueError – If the response is invalid or if the input is invalid.

Example:

from phoenix.client import AsyncClient
from phoenix.client.resources.spans import SpanAnnotationData
async_client = AsyncClient()

# Create span annotation data objects using dictionaries
annotation1 =  SpanAnnotationData(
    name="sentiment",
    span_id="72dda197b0e1b3ef",
    annotator_kind="HUMAN",
    result={"label": "positive", "score": 0.9},
)

annotation2 =  SpanAnnotationData(
    name="sentiment",
    span_id="72dda197b0e1b3ef",
    annotator_kind="HUMAN",
    result={"label": "negative", "score": 0.1},
)

# Log multiple annotations at once
await async_client.spans.log_span_annotations(
    span_annotations=[annotation1, annotation2],
)
async log_span_annotations_dataframe(*, dataframe, annotation_name=None, annotator_kind=None, sync=False)#

Log multiple span annotations from a pandas DataFrame asynchronously.

This method allows you to create multiple span annotations at once by providing the data in a pandas DataFrame. The DataFrame can either include name or annotation_name columns (but not both) and annotator_kind column, or you can specify global values for all rows. The data is processed in chunks of 100 rows for efficient batch processing.

Parameters:
  • dataframe (pd.DataFrame) – A pandas DataFrame containing the annotation data. Must include either a “name” or “annotation_name” column (but not both) or provide a global annotation_name parameter. Similarly, must include an “annotator_kind” column or provide a global annotator_kind. The span_id can be either a column in the DataFrame or will be taken from the DataFrame index. Optional columns include: “label”, “score”, “explanation”, “metadata”, and “identifier”.

  • annotation_name (Optional[str]) – The name to use for all annotations. If provided, this value will be used for all rows and the DataFrame does not need to include a “name” or “annotation_name” column.

  • annotator_kind (Optional[Literal["LLM", "CODE", "HUMAN"]]) – The kind of annotator used for all annotations. If provided, this value will be used for all rows and the DataFrame does not need to include an “annotator_kind” column. Must be one of “LLM”, “CODE”, or “HUMAN”.

  • sync (bool) – If True, the request will be fulfilled synchronously and the response will contain the inserted annotation IDs. If False, the request will be processed asynchronously. Defaults to False.

Returns:

If sync is True, a list of all inserted span annotations. If sync is False, None.

Return type:

Optional[list[dict]]

Raises:
  • ImportError – If pandas is not installed.

  • ValueError – If the DataFrame is missing required columns or if no valid annotation data is provided.

Example:

import pandas as pd
from phoenix.client import AsyncClient
async_client = AsyncClient()

# Using name and annotator_kind from DataFrame with span_id column
df1 = pd.DataFrame({
    "name": ["sentiment", "toxicity"],
    "span_id": ["span_123", "span_456"],
    "annotator_kind": ["HUMAN", "LLM"],
    "label": ["positive", "low"],
    "score": [0.9, 0.1]
})
await async_client.spans.log_span_annotations_dataframe(dataframe=df1)

# Using global name and annotator_kind with span_id from index
df2 = pd.DataFrame({
    "label": ["positive", "low"]
}, index=["span_345", "span_678"])
await async_client.spans.log_span_annotations_dataframe(
    dataframe=df2,
    annotation_name="sentiment",  # applies to all rows
    annotator_kind="HUMAN"  # applies to all rows
)
async log_spans(*, project_identifier, spans, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Logs spans to a project.

If any spans are invalid or duplicates, no spans will be logged and a SpanCreationError will be raised with details about the failed spans.

Parameters:
  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • spans (Sequence[v1.Span]) – A sequence of Span objects to log.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A CreateSpansResponseBody with statistics about

the operation. When successful, total_queued will equal total_received.

Return type:

v1.CreateSpansResponseBody

Raises:
  • SpanCreationError – If any spans failed validation (invalid or duplicates).

  • httpx.HTTPStatusError – If the API returns an unexpected error response.

  • httpx.TimeoutException – If the request times out.

async log_spans_dataframe(*, project_identifier, spans_dataframe, timeout=DEFAULT_TIMEOUT_IN_SECONDS)#

Logs spans to a project from a pandas DataFrame.

If any spans are invalid or duplicates, no spans will be logged and a SpanCreationError will be raised with details about the failed spans.

Parameters:
  • project_identifier (str) – The project identifier (name or ID) used in the API path.

  • spans_dataframe (pd.DataFrame) – A pandas DataFrame with a context.span_id or span_id column.

  • timeout (Optional[int]) – Optional request timeout in seconds.

Returns:

A CreateSpansResponseBody with statistics about

the operation. When successful, total_queued will equal total_received.

Return type:

v1.CreateSpansResponseBody

Raises:
  • SpanCreationError – If any spans failed validation (invalid or duplicates).

  • httpx.HTTPStatusError – If the API returns an unexpected error response.

  • httpx.TimeoutException – If the request times out.