Batch
Bulk move, copy, delete, and update across many images in one round-trip.
Bulk image operations on one dataset. Every call takes a dataset_name plus a
list of image IDs (1 to 10,000) and returns a BatchResult: partial success does
not raise. Reorganizing 10,000 images is one round-trip instead of 10,000
single-image calls, and the work runs server-side as single statements rather
than a loop.
There is no pictograph CLI group for batch - use the SDK or REST below.
SDK source:
resources/batch.py.
All four operations require member+ role; delete(permanent=True) requires
admin+.
move
Move images to a different virtual directory. Directory structure is metadata:
the stored image bytes never move. A filename that already exists in the target
is auto-suffixed (photo.png → photo-1.png) and counted in renamed rather
than failing the batch.
Source: Batch.move
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image_ids |
Sequence[str] |
required | Image UUIDs (1 to 10,000) |
target_directory_path |
str |
"/" |
Destination virtual directory |
result = client.batch.move(
dataset_name="my-dataset",
image_ids=["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"],
target_directory_path="/sorted/cars",
)
print(result.processed, result.renamed, result.failed)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/batch/images/move" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset_name": "my-dataset", "image_ids": ["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"], "target_directory_path": "/sorted/cars"}'
Returns BatchResult
BatchResult · 6 fields
class BatchResult(BaseModel):
"""Outcome of a batch operation."""
success: bool
processed: int
failed: list[BatchFailure] = []
affected_directories: list[str] = []
renamed: int = 0
operation: str | None = None
copy
Copy images to a different directory. The underlying bytes are copied server-side - instant, no data transfer - and new image rows point at the copies.
Source: Batch.copy
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image_ids |
Sequence[str] |
required | Image ids to act on. |
target_directory_path |
str |
"/" |
Destination virtual directory |
duplicate_handling |
"rename" | "skip" | "overwrite" |
"rename" |
skip records the source as failed; overwrite deletes the existing image first |
copy_annotations |
bool |
False |
When True, copy annotations and status too |
result = client.batch.copy(
dataset_name="my-dataset",
image_ids=["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"],
target_directory_path="/cars-copy",
duplicate_handling="rename",
copy_annotations=True,
)
print(result.processed, result.failed)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/batch/images/copy" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset_name": "my-dataset", "image_ids": ["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"], "target_directory_path": "/cars-copy", "duplicate_handling": "rename", "copy_annotations": true}'
Returns BatchResult
BatchResult · 6 fields
class BatchResult(BaseModel):
"""Outcome of a batch operation."""
success: bool
processed: int
failed: list[BatchFailure] = []
affected_directories: list[str] = []
renamed: int = 0
operation: str | None = None
delete
Soft-archive by default: images move to the Archive tab and stay in storage,
restorable with update(is_archived=False). permanent=True purges the stored
bytes plus cached thumbnails, is irreversible, and requires admin+ role. Check
result.operation ("archived" or "deleted") to confirm which one happened.
Source: Batch.delete
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image_ids |
Sequence[str] |
required | Image UUIDs to delete. |
permanent |
bool |
False |
When True, hard delete. Requires admin/owner role. Defaults to False (soft archive). |
result = client.batch.delete(
dataset_name="my-dataset",
image_ids=["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"],
permanent=False,
)
print(result.operation, result.processed, result.failed)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/batch/images/delete" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset_name": "my-dataset", "image_ids": ["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"], "permanent": false}'
Returns BatchResult
BatchResult · 6 fields
class BatchResult(BaseModel):
"""Outcome of a batch operation."""
success: bool
processed: int
failed: list[BatchFailure] = []
affected_directories: list[str] = []
renamed: int = 0
operation: str | None = None
update
Update metadata on a batch of images. Pass exactly the fields you want to change;
None is omitted from the request, and the SDK raises ValueError if every
field is None. Note this is a PATCH, and the fields are nested under
updates on the wire.
Source: Batch.update
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image_ids |
Sequence[str] |
required | Image ids to act on. |
status |
str | None |
None |
"new", "annotate", "review", "complete" |
display_name |
str | None |
None |
Display override; the raw filename is unchanged |
is_archived |
bool | None |
None |
True archives, False restores |
result = client.batch.update(
dataset_name="my-dataset",
image_ids=["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"],
status="complete",
is_archived=False,
)
print(result.processed, result.failed)
curl -s -X PATCH "https://api.pictograph.io/api/v1/developer/batch/images/update" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset_name": "my-dataset", "image_ids": ["a1b2c3d4-5e6f-4708-9a1b-2c3d4e5f6a7b"], "updates": {"status": "complete", "is_archived": false}}'
Returns BatchResult
BatchResult · 6 fields
class BatchResult(BaseModel):
"""Outcome of a batch operation."""
success: bool
processed: int
failed: list[BatchFailure] = []
affected_directories: list[str] = []
renamed: int = 0
operation: str | None = None
BatchResult
| Attribute | Type | Notes |
|---|---|---|
success |
bool |
Whether the call completed |
processed |
int |
Count of images the operation landed for |
failed |
list[BatchFailure] |
{id, reason} per failed image - retry this subset |
affected_directories |
list[str] |
Virtual directories touched |
renamed |
int |
Filenames auto-suffixed on move; 0 elsewhere |
operation |
str | None |
On delete: "archived" or "deleted" |
Common errors
| Status | Exception | Cause |
|---|---|---|
| 403 | ForbiddenError |
permanent=True needs admin+; all writes need member+ |
| 404 | NotFoundError |
Dataset missing, or no matching image_id |
| 400 / 422 | ValidationError |
Invalid field value or empty update |