> For the complete documentation index, see [llms.txt](https://docs.unitlab.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.unitlab.ai/api-sdk-cli/sdks/cloud-storage.md).

# Cloud storage

List safe cloud-storage metadata, browse paginated entries, and import approved paths into a project.

Import images from an existing cloud connection, wait for processing, and verify the imported files before adding labels. This example imports two images from S3 without uploading another original copy into Unitlab's bucket.

## Before you start

Use Unitlab SDK 3.1.3, a configured Unitlab API key, an existing project, and a cloud connection created in Workspace Settings. Follow [authentication](https://docs.unitlab.ai/api-sdk-cli/get-started/authentication-and-configuration) if `UnitlabClient()` is not configured.

### Configure the provider before automation

![Add Cloud Storage configuration dialog](https://292810646-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGjVLUz4wthGkGlRKM6rM%2Fuploads%2FEceeKlJexxGEUrAINYfa%2Fcloud-storage-add.png?alt=media\&token=7f2c7aff-bb2a-476f-87cb-a8bd3a70540f)

*Workspace Settings › Cloud storage creates the approved provider connection used by SDK and CLI cloud-storage operations. Credentials are never returned by the SDK.*

The Python SDK can list existing connections and browse their objects. It does not have a connection-create or credential-update method. Configure and rotate provider credentials in Workspace Settings with **Manage cloud storage** permission. Your Unitlab workspace API key authenticates the SDK; it is separate from the credentials Unitlab uses to access S3.

The Amazon S3 form accepts an access key ID and secret access key, plus bucket, region, and optional prefix. Role ARN/AssumeRole and session-token configuration are not exposed by this connection flow. See [Cloud providers](https://docs.unitlab.ai/documentation/data/cloud-providers) for credentials, CORS, and scoped read/write requirements.

## Example storage layout

Assume your S3 connection has prefix `datasets/` and the bucket contains:

```
example-bucket/
└── datasets/
    ├── incoming/
    │   ├── road.jpg
    │   ├── crossing.jpg
    │   └── later/
    │       └── tunnel.jpg
    └── labels/
        └── predictions.json
```

Use `incoming/` in SDK calls because paths are relative to the saved connection prefix. Selecting it imports the two immediate images. It does not include `incoming/later/tunnel.jpg`; select that child directory separately when needed. `labels/predictions.json` is optional and used only in the annotation example below.

## Browse, import, and verify

{% stepper %}
{% step %}

### Select the project and storage connection

List connections to find the intended ID:

```python
from unitlab import UnitlabClient

client = UnitlabClient()
for connection in client.cloud_storages.list():
    print(connection.id, connection.name, connection.resource)
```

Replace both placeholders with existing IDs, then continue with the same client:

```python
project = client.projects.get("PROJECT_ID")
storage = client.cloud_storages.get("CLOUD_STORAGE_ID")
```

{% endstep %}

{% step %}

### Browse the source directory

```python
for entry in storage.browse(prefix="incoming/", project=project, page_size=500):
    print(entry.type, entry.name, entry.size, entry.is_extension_allowed)
```

Expect two file entries and the `incoming/later` folder. `browse()` reads every result page automatically. Names returned for folders can omit the trailing slash; add `/` when selecting a folder for import. Use these relative names, not signed URLs.
{% endstep %}

{% step %}

### Start the import and wait

```python
batch = project.import_cloud(storage, ["incoming/"])
print("Batch Queue:", batch.batch_queue_id)

status = batch.wait(timeout=1800)
print(status.total, status.completed, status.processing, status.failed)
if status.failed:
    raise RuntimeError("Inspect failed items in the Batch Queue.")
```

For two valid images, expect the final counts `2 2 0 0`. Keep the queue ID if you need to resume monitoring later. Add `fps=` when importing video.
{% endstep %}

{% step %}

### Check the imported items

```python
queue = project.batch_queue(batch.batch_queue_id)
for item in queue.data():
    print(item["id"], item["file_name"], item["status"])
```

Verify that the two selected images appear and processing succeeded. Save the item IDs for explicit label mappings. `tunnel.jpg` should not be in this queue because it is in a child directory.
{% endstep %}
{% endstepper %}

## Source storage and generated data

Importing cloud images registers their existing object references rather than uploading another original copy into Unitlab's bucket. Unitlab still reads the bytes for processing and browser viewing. Ordinary project-image thumbnails and annotation previews are written into the connected bucket under `<connection-prefix>/unitlab-<workspace-id-prefix>/`, using categories such as `thumbnails` and `thumbnail_annotations`.

The standard image workflow therefore needs source-read access and generated-preview write access. A successful connection check reads bucket availability and AWS CORS settings; it does not test writes. Optional features can store other derived data, including Find Similar reference crops in Unitlab's platform storage. For permission scope and deployment-specific ACL compatibility, use the [S3 access guidance](https://docs.unitlab.ai/documentation/data/cloud-providers#s3-access-and-connection-checks).

## Import labels onto cloud-referenced images

On a deployment with annotation-import support, wait for the cloud media to finish processing, then import local label JSON or JSON stored in a connected bucket. The media references must match the project's existing items. You do not need to upload the original images again.

Save supported annotation JSON as `predictions.json` in your local working directory. This file must reference the images just imported. Reuse `project` from the steps above:

```python
labels = project.annotation_imports.create(
    "./predictions.json",
    source_name="road-detector",
)
print(labels.preview["media"])
print(labels.preview["classes"])
print(labels.preview["issues"])
```

Inspect the preview and resolve any media or class mappings before calling `labels.commit()`. Alternatively, read the JSON from the `labels/` directory in the example bucket:

```python
labels = project.annotation_imports.create(
    cloud_storage=storage.id,
    annotation_key="labels/predictions.json",
    source_name="road-detector",
)
```

Choose one label source. The annotation key is relative to the connection prefix, so this reads `datasets/labels/predictions.json`. Inspect the preview before committing this import too.

For explicit mappings, accepted JSON formats, repeated imports, and review behavior, follow [Annotation imports](https://docs.unitlab.ai/api-sdk-cli/sdks/annotation-imports). Imported labels can be edited and reviewed in Unitlab. The client methods require Unitlab SDK 3.1.3 and a backend deployment exposing the annotation-import endpoints; installing the SDK alone does not enable the server feature.

## Resume or recover an import

Replace `BATCH_QUEUE_ID` with the ID you saved:

```python
queue = project.batch_queue("BATCH_QUEUE_ID")
print(queue.status())
for item in queue.data():
    print(item["file_name"], item["status"])
```

If processing times out, continue checking this queue rather than starting the import again. If only some files fail, inspect and correct those files or their storage permissions before retrying that subset. A successful connection check alone does not verify preview-write access.

***

> **Related Unitlab capability guides:** [Unitlab’s data curation platform](https://unitlab.ai/en/data-curation)

## Model predictions to a reviewed delivery

Follow [Pre-labels, review, and export](https://docs.unitlab.ai/api-sdk-cli/sdks/prelabels-review-and-export) for the complete workflow and the limits of approved-only filtering.
