Sign in Get started

Batch

Bulk move, copy, delete, and update across many images in one round-trip.

View as Markdown

Bulk image operations on a single dataset. Each call accepts a list of image IDs and returns a BatchResult with per-item failure context — partial success does not raise.

from pictograph import Client
client = Client()

move

Move images to a different virtual folder within the same dataset.

result = client.batch.move(
    dataset_name="my-dataset",
    image_ids=["img-1", "img-2", "img-3"],
    target_folder_path="/sorted/cars",
)
print(result.succeeded, result.failed_count, result.failures)

Storage paths are immutable — “move” updates virtual_folder_path; the underlying image bytes don’t move.

copy

Copy images to a different folder. Server-side copy of the underlying bytes (instant, zero data transfer).

result = client.batch.copy(
    dataset_name="my-dataset",
    image_ids=["img-1", "img-2"],
    target_folder_path="/cars-copy",
    duplicate_handling="rename",   # collision policy in the destination
    copy_annotations=False,        # destination images start without annotations
)
ArgTypeDefaultNotes
dataset_namestrrequired
image_idsSequence[str]required
target_folder_pathstr"/"Destination virtual folder
duplicate_handlingLiteral["rename", "skip", "overwrite"]"rename"How to handle filename collisions
copy_annotationsboolFalseWhen True, copy annotations_json and status too

delete

Soft-archive by default; permanent on request.

result = client.batch.delete(
    dataset_name="my-dataset",
    image_ids=["img-1", "img-2", "img-3"],
    permanent=False,                 # archive (recoverable)
)

permanent=True purges the stored bytes — irreversible. Requires admin+ role.

update

Update metadata fields on a batch of images. Pass exactly the fields you want to change — None is omitted from the request.

result = client.batch.update(
    dataset_name="my-dataset",
    image_ids=["img-1", "img-2"],
    status="complete",
    is_archived=False,
)
ArgTypeDefaultNotes
dataset_namestrrequired
image_idsSequence[str]required
statusstr | NoneNone"new", "annotate", "review", "complete"
display_namestr | NoneNoneDisplay override
is_archivedbool | NoneNoneTrue archives; False restores

ValidationError if every field is None (the update would be a no-op).

BatchResult

AttributeTypeNotes
succeededlist[str]IDs the op completed for
failed_countintlen(failures)
failureslist[BatchFailure]{image_id, reason} per failure
successbool (property)failed_count == 0

Errors

StatusExceptionCause
403ForbiddenErrorpermanent=True requires admin+ role
404NotFoundErrorDataset missing, or every image_id invalid
422ValidationErrorInvalid field value or empty update

Why batch over loops

Reorganizing 10K images is one round-trip with batch.move() versus 10K with images.update(). Bulk operations are implemented server-side as single statements, not loops.

Copied to clipboard