---
title: Images
description: Upload, fetch, download, and delete individual images.
section: API Reference
order: 2
---
For bulk operations across many images, prefer the
[upload workflow](/docs/quick-start.md) and the [`batch`](/docs/api-reference/batch.md)
resource. This page is for single-image ops.

```python
from pictograph import Client
client = Client()
```

## get

Fetch metadata for a single image.

```python
image = client.images.get("img-uuid-1")
print(image.filename, image.status, image.annotation_count)
```

Returns `Image`. Annotations live on the [annotations](/docs/api-reference/annotations.md)
resource — call `client.annotations.get(image.id)` to fetch them.

## upload

Upload a local file to a dataset. Three steps under the hood: get a
signed upload URL → PUT the bytes → register the image.

```python
from pathlib import Path

project = client.projects.get("my-dataset")
image = client.images.upload(
    dataset_id=project.id,
    file_path=Path("./photo.jpg"),
    folder_path="/cars",          # virtual folder on the dataset
)
```

| Arg | Type | Default | Notes |
|---|---|---|---|
| `dataset_id` | `str` | required | UUID of the destination dataset |
| `file_path` | `str \| Path` | required | Local file. Pillow extracts dimensions client-side |
| `folder_path` | `str` | `"/"` | Virtual folder (e.g. `/cars`). Storage paths are immutable |
| `idempotency_key` | `str \| None` | auto | Override the auto-generated dedup key |

Returns `Image`. Raises `ConflictError` if a file with the same name already exists in the same folder.

**Supported extensions**: `.jpg`, `.jpeg`, `.png`, `.webp`, `.bmp`, `.tif`, `.tiff`, `.gif`, `.heic`. HEIC is auto-converted to PNG server-side.

## download

Stream the original image bytes to a local file (chunked, safe for
large images).

```python
client.images.download("img-uuid-1", output_path="./photo.jpg")
```

| Arg | Type | Default |
|---|---|---|
| `image_id` | `str` | required |
| `output_path` | `str \| Path` | required |

The bytes are served via Cloud CDN with 30-day edge caching, so repeat
downloads are fast.

## delete

Soft-delete (archive) by default. Set `permanent=True` to free the
stored bytes — irreversible.

```python
client.images.delete("img-uuid-1")                  # archive (recoverable)
client.images.delete("img-uuid-1", permanent=True)  # permanent
```

Permanent deletes require `member`+ role on the API key.

## Bulk uploads

For directories of images, use the workflow:

```python
from pictograph.workflows import upload_dataset_from_folder

report = upload_dataset_from_folder(
    client,
    "my-dataset",
    folder="./photos",
    organize_by_class=True,   # subdirectory → virtual folder
    parallel=True,
    max_workers=8,
)
print(report.images_uploaded, len(report.failures))
```

See [Quick Start](/docs/quick-start.md) for the full workflow surface.

## Common errors

| Status | Exception | Cause |
|---|---|---|
| 404 | `NotFoundError` | `image_id` doesn't exist, or belongs to another org |
| 409 | `ConflictError` | Filename collision in the same virtual folder |
| 413 | `ApiError` | Uploaded file exceeds 50 MB |
| 415 | `ValidationError` | Unsupported file extension |