Training
Low-level primitives for spawning, polling, and cancelling training runs.
The training resource manages the lifecycle of a single run against a pre-built export. For the end-to-end “give me an ONNX model from this dataset” call, use train_pipeline instead.
Every example below shows the Python SDK call and the equivalent raw REST
request. The REST examples authenticate with an X-API-Key header; set
PICTOGRAPH_API_KEY in your shell to copy-and-run them.
from pictograph import Client
client = Client() # reads PICTOGRAPH_API_KEY
Pipelines
pipeline_type | Output |
|---|---|
yolox | Object detection (boxes) |
sm_pytorch | Semantic segmentation |
classification | Image classification |
rfdetr_detection | Object detection (RT-DETR) |
rfdetr_segmentation | Instance segmentation (RT-DETR) |
GPU tiers
gpu_type | Approx. cost | Pick for |
|---|---|---|
a10g (default) | ~$0.30/hr | YOLOX, classification, RF-DETR-detection |
a100 | ~$2/hr | Large RF-DETR, big batches |
h100 | ~$4/hr | Last resort — only when A100 OOMs |
create
Spawn a run against an existing (completed) export.
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name | str | required | Source project |
export_name | str | required | Pre-built export |
pipeline_type | PipelineType | required | See table above |
name | str | required | Human-readable label (1-100 chars) |
config | dict | {} | epochs, batch_size, learning_rate, image_size |
gpu_type | GpuType | "a10g" | |
wait | bool | True | When False, returns immediately with status="queued" |
poll_interval | float | 5.0 | Seconds between polls |
timeout | float | 7200.0 | Max poll seconds (2 hours) |
run = client.training.create(
dataset_name="road-signs",
export_name="road-signs-20260512-120000",
pipeline_type="yolox",
name="yolox-run-1",
config={"epochs": 50},
gpu_type="a10g",
wait=True,
poll_interval=5.0,
timeout=7200.0,
)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/training/" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset_name": "road-signs", "export_name": "road-signs-20260512-120000", "pipeline_type": "yolox", "name": "yolox-run-1", "config": {"epochs": 50}, "gpu_type": "a10g"}'
Returns TrainingRun.
list
Single-page list of training runs in your organization.
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name | str | None | None | Restrict to one dataset |
status | TrainingStatus | None | None | e.g. "running" |
limit | int | 50 | Backend cap: 100 |
offset | int | 0 | Page offset |
runs = client.training.list(limit=20, status="running")
for run in runs:
print(run.id, run.status, run.progress)
curl -s "https://api.pictograph.io/api/v1/developer/training/?limit=20&status=running" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
iter
Auto-paging iterator across every training run in your org.
for run in client.training.iter(page_size=50):
print(run.id, run.status, run.progress)
# Page manually with limit + offset until fewer than `limit` rows return.
curl -s "https://api.pictograph.io/api/v1/developer/training/?limit=50&offset=0" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
get
Fetch a single run’s current status, metrics, and progress, addressed by
name (or run_id= UUID). status is one of {"pending", "queued", "running", "completed", "failed", "cancelled"}. Because run names are not
unique, a by-name lookup returns the most recent run of that name.
run = client.training.get("Swift Falcon") # by name (latest of that name)
run = client.training.get(run_id="run-uuid") # by id
print(run.status, run.progress, run.current_epoch, "/", run.total_epochs)
# By name:
curl -s "https://api.pictograph.io/api/v1/developer/training/by-name/Swift%20Falcon" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
# By UUID:
curl -s "https://api.pictograph.io/api/v1/developer/training/run-uuid" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns {"data": TrainingRun}.
cancel
Cancel a running job, addressed by name (latest of that name) or
run_id= UUID. The backend Modal-cancels the GPU job in-flight and marks the
run cancelled. Under charge-on-success a run cancelled before it finishes is
never charged (a legacy pre-charged run is refunded once). Member+ API key.
client.training.cancel("Swift Falcon") # by name
client.training.cancel(run_id="run-uuid") # by id
curl -s -X POST "https://api.pictograph.io/api/v1/developer/training/by-name/Swift%20Falcon/cancel" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
bulk_cancel
Cancel many runs in one org-scoped call. Already-terminal, foreign, or missing
ids land in not_found rather than failing the call. Each run is Modal-cancelled
in-flight; a run cancelled before it finishes is never charged (a legacy
pre-charged run is refunded once).
result = client.training.bulk_cancel(["run-1", "run-2"])
print(result.count, "cancelled;", result.not_found, "skipped")
curl -s -X POST "https://api.pictograph.io/api/v1/developer/training/bulk-cancel" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"run_ids": ["run-1", "run-2"]}'
Returns BulkActionResult with succeeded, not_found, and count.
wait_for_completion
If you created with wait=False, you can block later:
run = client.training.wait_for_completion("run-uuid", timeout=3600.0)
if run.status == "completed":
model = client.models.get(model_id=run.model_id)
# Poll the get endpoint until "status" reaches a terminal value.
curl -s "https://api.pictograph.io/api/v1/developer/training/run-uuid" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Minimum dataset size
Training requires at least 5 images matching the export’s status_filter so the worker can split into train / val / test. Below that, training fails with a validation error.
ds = client.datasets.get("my-dataset")
assert ds.completed_image_count >= 5
Cost estimation
estimate = client.credits.estimate("training_a10g", quantity=30)
if not estimate.sufficient:
raise RuntimeError(f"Need ${estimate.total_usd:.2f}, have ${estimate.remaining_usd:.2f}")
Training is billed per GPU-minute in USD; estimate returns the live
total_micro_usd (integer micro-USD, 1 USD = 1,000,000 µUSD).
Billing is charge-on-success: a run is charged once, after it completes, for the actual GPU minutes used — a failed, OOM, or cancelled run is never charged (there is no up-front deduction to refund).
Errors
| Status | Exception | Cause |
|---|---|---|
| 402 | PaymentRequiredError | Insufficient credits |
| 404 | NotFoundError | Dataset or export missing |
| 422 | ValidationError | Pipeline / GPU invalid, dataset too small |
| 408 | PollTimeoutError | wait=True exceeded timeout (run keeps going) |
See also
train_pipeline— end-to-end workflow (recommended starting point)- Models — download trained ONNX weights
- Credits —
estimate("training_<gpu>")for the live USD price