> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encord.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Label Spaces: Code Examples

Label Spaces are the objects returned by [`LabelRowV2.get_spaces()`](/sdk-documentation/sdk-references/objects.ontology_labels_impl) and `LabelRowV2.get_space()`. Each Space manages the object and classification instances for one data unit within a Label Row, and every modality has its own Space class in the [`objects.spaces`](/sdk-documentation/sdk-references/objects.spaces.base_space) module.

The examples below are grouped by the **four labelling patterns** the Space classes fall into. Pick the one that matches your data:

| Pattern                | Space classes                                                                   | How you position a label            |
| ---------------------- | ------------------------------------------------------------------------------- | ----------------------------------- |
| Geometric single-frame | `ImageSpace`                                                                    | `coordinates` only (no frames)      |
| Multi-frame            | `VideoSpace` (video & image sequence), `MedicalSpace` (DICOM/NIfTI), `PdfSpace` | `frames=` **and** `coordinates=`    |
| Range                  | `AudioSpace`, `TextSpace`, `HTMLSpace`, `TimeSeriesSpace`                       | `ranges=`                           |
| Point cloud            | `PointCloudFileSpace`                                                           | `ranges=` (or an Encord RLE string) |

<Info>
  For the full end-to-end Data Group workflow (root spaces, multi-layer groups, `layout_key` targeting), see [Data Groups & Label Spaces](/sdk-documentation/sdk-labels/sdk-data-group-labels).
</Info>

## Get a Space

Every Space is obtained from an initialised Label Row. You can list all Spaces, or fetch a specific one by storage-item ID or Data Group `layout_key`.

<Tip>
  `get_space()` requires the `type_` argument so the SDK returns the correctly typed Space. Identify the Space using **either**:

  * A storage item's unique ID: `lr.get_space(id="7E3KERd9arYTiPicaijP6c1LfI73", type_="image")`
  * A [Data Group](/sdk-documentation/index-sdk/sdk-data-groups) layout key: `lr.get_space(layout_key="left-camera", type_="video")`

  Using `layout_key`s lets you reuse the same code across multiple Label Rows without looking up storage-item IDs.
</Tip>

```python List all Spaces in a Label Row theme={"dark"}
from encord import EncordUserClient, Project

SSH_PATH = "<private_key_path>"   # Path to your SSH private key
PROJECT_ID = "<project_id>"       # Unique Project ID
DATA_TITLE = "<data_unit_title>"  # Title of the data unit

user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path=SSH_PATH,
    # For US platform users use "https://api.us.encord.com"
    domain="https://api.encord.com",
)

project: Project = user_client.get_project(PROJECT_ID)
lr = project.list_label_rows_v2(data_title_eq=DATA_TITLE)[0]
lr.initialise_labels()  # Spaces are only populated after initialising labels

for space in lr.get_spaces():
    print(
        f"space_id: {space.space_id}, "
        f"file_name: {space.metadata.file_name}, "
        f"layout_key: {space.metadata.layout_key}"
    )
```

## Shared `Space` methods

Every Space subclass inherits these read and remove methods from the base [`Space`](/sdk-documentation/sdk-references/objects.spaces.base_space) class, regardless of modality.

<CodeGroup>
  ```python Read instances and annotations theme={"dark"}
  # `space` is any Space obtained from lr.get_space(...) / lr.get_spaces()

  # All object / classification instances on the space
  objects = space.get_object_instances()
  classifications = space.get_classification_instances()

  # Lazily-created annotations (returned as an iterator)
  for annotation in space.get_annotations(type_="object"):
      print(annotation.object_hash, annotation.confidence, annotation.last_edited_by)

  # Filter to specific instance hashes
  for annotation in space.get_annotations(
      type_="object",
      filter_instance_hashes=["<object_hash_1>", "<object_hash_2>"],
  ):
      print(annotation.object_hash)
  ```

  ```python Remove instances theme={"dark"}
  # Remove by hash. Returns the removed instance, or None if it wasn't present.
  space.remove_object_instance(object_hash="<object_hash>")
  space.remove_classification_instance(classification_hash="<classification_hash>")

  # Persist the changes
  lr.save()
  ```
</CodeGroup>

## Image space (geometric, single-frame)

`ImageSpace` handles a single image. Objects are positioned with `coordinates` **only** — there is no `frames` argument. Classifications apply to the whole image.

```python ImageSpace theme={"dark"}
from encord import EncordUserClient, Project
from encord.objects import Object, Classification, ObjectInstance, ClassificationInstance
from encord.objects.coordinates import BoundingBoxCoordinates
from encord.objects.options import Option

SSH_PATH = "<private_key_path>"
PROJECT_ID = "<project_id>"
DATA_TITLE = "<data_unit_title>"
SPACE_ID = "<storage_item_id>"

user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path=SSH_PATH,
    # For US platform users use "https://api.us.encord.com"
    domain="https://api.encord.com",
)
project: Project = user_client.get_project(PROJECT_ID)
lr = project.list_label_rows_v2(data_title_eq=DATA_TITLE)[0]
lr.initialise_labels()

image_space = lr.get_space(id=SPACE_ID, type_="image")

# Build instances from the Ontology
ontology = project.ontology_structure
box_object: Object = ontology.get_child_by_title(title="Vehicle", type_=Object)
day_night: Classification = ontology.get_child_by_title(title="Day or Night", type_=Classification)

bb_inst: ObjectInstance = box_object.create_instance()
cls_inst: ClassificationInstance = day_night.create_instance()
cls_inst.set_answer(answer=day_night.get_child_by_title(title="Day", type_=Option))

# Add an object — coordinates only, no frames for images
image_space.put_object_instance(
    bb_inst,
    BoundingBoxCoordinates(top_left_x=0.6, top_left_y=0.4, width=0.3, height=0.2),
    on_overlap="replace",   # "error" (default) | "replace"
    confidence=0.95,
    manual_annotation=True,
)

# Add a classification to the image
image_space.put_classification_instance(cls_inst, on_overlap="replace")

lr.save()
```

<Tip>
  For bitmask objects, pass an Encord-compatible RLE **string** as the coordinates instead of a `BoundingBoxCoordinates`/polygon object. Convert COCO/pycocotools RLE first with `encord.common.bitmask_operations.coco_rle_to_encord_rle`.
</Tip>

## Video space (multi-frame)

`VideoSpace` (also used for image sequences) and the other multi-frame spaces (`MedicalSpace` for DICOM/NIfTI, `PdfSpace`) require **both** `frames=` and `coordinates=`. Iterating annotations exposes the `frame` each annotation sits on.

```python VideoSpace theme={"dark"}
from encord import EncordUserClient, Project
from encord.objects import Object, ObjectInstance
from encord.objects.coordinates import BoundingBoxCoordinates

SSH_PATH = "<private_key_path>"
PROJECT_ID = "<project_id>"
DATA_TITLE = "<data_unit_title>"
SPACE_ID = "<storage_item_id>"

user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path=SSH_PATH,
    # For US platform users use "https://api.us.encord.com"
    domain="https://api.encord.com",
)
project: Project = user_client.get_project(PROJECT_ID)
lr = project.list_label_rows_v2(data_title_eq=DATA_TITLE)[0]
lr.initialise_labels()

video_space = lr.get_space(id=SPACE_ID, type_="video")

ontology = project.ontology_structure
person: Object = ontology.get_child_by_title(title="Person", type_=Object)
person_inst: ObjectInstance = person.create_instance()

# Place the object across a set of frames
video_space.put_object_instance(
    person_inst,
    frames=[0, 1, 2],
    coordinates=BoundingBoxCoordinates(top_left_x=0.6, top_left_y=0.4, width=0.3, height=0.2),
    on_overlap="replace",   # "error" (default) | "replace"
)

# Read back per-frame annotations
for annotation in video_space.get_annotations(type_="object"):
    print(f"frame {annotation.frame}: {annotation.coordinates}")

lr.save()
```

<Note>
  For DICOM/NIfTI use `type_="medical"`, and for PDFs use `type_="pdf"` (where `frames` refers to the page number). The `frames=`/`coordinates=` call is identical.
</Note>

## Audio space (range)

Range spaces — `AudioSpace`, `TextSpace`, `HTMLSpace`, `TimeSeriesSpace` — position objects over one-dimensional `ranges` instead of frames. `get_object_ranges()` returns the ranges an instance occupies, and range `on_overlap` additionally supports `"merge"`.

```python AudioSpace theme={"dark"}
from encord import EncordUserClient, Project
from encord.objects import Object, ObjectInstance
from encord.objects.frames import Range

SSH_PATH = "<private_key_path>"
PROJECT_ID = "<project_id>"
DATA_TITLE = "<data_unit_title>"
SPACE_ID = "<storage_item_id>"

user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path=SSH_PATH,
    # For US platform users use "https://api.us.encord.com"
    domain="https://api.encord.com",
)
project: Project = user_client.get_project(PROJECT_ID)
lr = project.list_label_rows_v2(data_title_eq=DATA_TITLE)[0]
lr.initialise_labels()

audio_space = lr.get_space(id=SPACE_ID, type_="audio")

ontology = project.ontology_structure
speaker: Object = ontology.get_child_by_title(title="Speaker", type_=Object)
speaker_inst: ObjectInstance = speaker.create_instance()

# Add the object to one or more ranges (a single Range or a list of Ranges)
audio_space.put_object_instance(
    speaker_inst,
    [Range(start=100, end=2000), Range(start=3000, end=4500)],
    on_overlap="merge",   # "error" (default) | "merge" | "replace"
)

# Inspect the ranges an instance occupies
print(audio_space.get_object_ranges(speaker_inst))

# Range annotations expose `.ranges`
for annotation in audio_space.get_annotations(type_="object"):
    print(annotation.object_hash, annotation.ranges)

lr.save()
```

<Note>
  `TextSpace`, `HTMLSpace`, and `TimeSeriesSpace` follow the same `ranges=` pattern. HTML uses `HtmlRange`/`HtmlNode` (`from encord.objects.html_node import HtmlRange, HtmlNode`) instead of `Range`.
</Note>

## Point cloud space

`PointCloudFileSpace` is a range space for 3D scenes. Segmentation objects are positioned either with `Range` objects over point indices or with an Encord-compatible RLE **string**. Its `metadata` is a `SceneMetadata` (exposing `stream_id`, `uri`, and `event_index`).

```python PointCloudFileSpace theme={"dark"}
from encord import EncordUserClient, Project
from encord.objects import Object, ObjectInstance
from encord.objects.frames import Range

SSH_PATH = "<private_key_path>"
PROJECT_ID = "<project_id>"
DATA_TITLE = "<data_unit_title>"
SPACE_ID = "<storage_item_id>"

user_client = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path=SSH_PATH,
    # For US platform users use "https://api.us.encord.com"
    domain="https://api.encord.com",
)
project: Project = user_client.get_project(PROJECT_ID)
lr = project.list_label_rows_v2(data_title_eq=DATA_TITLE)[0]
lr.initialise_labels()

pcd_space = lr.get_space(id=SPACE_ID, type_="point_cloud")
print(pcd_space.metadata.stream_id, pcd_space.metadata.uri)

ontology = project.ontology_structure
# Point cloud objects must be a segmentation shape
ground: Object = ontology.get_child_by_title(title="Ground", type_=Object)
ground_inst: ObjectInstance = ground.create_instance()

# Option A: explicit point ranges
pcd_space.put_object_instance(
    ground_inst,
    [Range(start=0, end=5000)],
    on_overlap="replace",   # "error" (default) | "merge" | "replace"
)

# Option B: an Encord-compatible RLE string (segmentation shapes only)
# pcd_space.put_object_instance(ground_inst, "<encord_rle_string>")

lr.save()
```

<Info>
  `on_overlap` semantics differ by pattern: geometric and multi-frame spaces accept `"error"` (default) and `"replace"`; range and point cloud spaces additionally accept `"merge"` to add new ranges while keeping existing annotations.
</Info>
