Bulk Container Creation
The SDK creates containers one at a time with the add_* methods documented in Core Concepts: Containers. There is no batch-create endpoint — creating many containers means looping those single-container calls. This page covers the structure that makes such a loop safe to re-run and recover from: idempotent creation and per-item error handling.
There is no all-or-nothing creation
The SDK cannot create a group of containers as a single unit that either fully succeeds or fully fails. Each add_* call is committed on its own. If your script fails partway through, the containers created before the failure remain in place. Plan for partial success: make the operation idempotent so that re-running it finds the containers already created and only fills in the rest.
Idempotent creation
The single most useful pattern for bulk creation is get-or-create: look for the container first, and only create it if it is missing. This makes the whole script safe to re-run after a failure — already-created containers are found and skipped rather than duplicated.
def get_or_create_subject(project, label):
existing = project.subjects.find_first(f"label={label}")
if existing:
return existing
return project.add_subject({"label": label})
Drive it from your list of labels:
created = []
for label in subject_labels:
subject = get_or_create_subject(project, label)
created.append(subject)
Running this script a second time creates nothing new — each find_first() matches the subject created on the first run. This is the SDK's substitute for the "transaction-like" guarantees the relational world provides: instead of all-or-nothing, you get safe-to-repeat.
Tip
For the underlying find_first / add_* mechanics and a get_or_create_project variant, see Core Concepts: Finders. This page focuses only on applying that pattern at scale.
Collecting errors instead of stopping
A simple loop aborts on the first failure, leaving the rest of the batch untouched and the exception unhandled. Bulk work usually calls for the opposite behavior: attempt every item, record what failed, and report at the end. Catch flywheel.ApiException per iteration and accumulate failures.
created = []
failures = []
for label in subject_labels:
try:
subject = project.add_subject({"label": label})
created.append(subject)
except flywheel.ApiException as exc:
failures.append({"label": label, "status": exc.status, "reason": exc.reason})
print(f"Created {len(created)} subjects, {len(failures)} failed")
for failure in failures:
print(f" {failure['label']!r}: {failure['status']} {failure['reason']}")
flywheel.ApiException exposes .status (the HTTP status code) and .reason, which are enough to triage most bulk failures:
| Status | Typical cause in a creation loop |
|---|---|
400 | Invalid payload — missing or malformed required field (e.g. empty label) |
403 | No write permission on the parent container |
404 | Parent container ID does not exist |
409 | A conflicting container already exists |
For the get-or-create pattern, a 409 conflict usually means the container already exists — which is exactly the case idempotent creation is designed to avoid hitting in the first place.
Related
- Core Concepts: Containers — the
add_*anddelete_*methods this page loops over - Core Concepts: Finders —
find_firstand the get-or-create pattern - Bulk Metadata Updates — populate
info, tags, and classification on the containers you just created - Performance Optimization — speed up a large creation loop with concurrent requests