Sign in Get started

Models

List trained CV models in your organization and download their ONNX weights.

View as Markdown

Models are produced by training runs. The SDK doesn’t insert model rows directly — you train, then read.

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

Like datasets, models are unique by (organization, name). Every single-model method takes the name positionally or a model_id= keyword — exactly one — and both forms hit the same serializer, so the returned shape is identical.

Response envelope. A single model is {"data": {…}}; a collection is {"data": [...], "pagination": {"limit", "offset", "total", "has_more"}} where total is the org-wide count. Internal storage paths are never returned — fetch weights via download, which mints a short-lived signed URL.

list

Single-page list of models in your organization. Returns the collection envelope with a server-computed total and has_more.

ArgTypeDefaultNotes
dataset_namestr | NoneNoneRestrict to models trained on this dataset
statusModelStatus | NoneNone"training" / "ready" / "failed" / "archived"
model_typeModelType | NoneNone"object_detection" / "semantic_segmentation" / "instance_segmentation" / "classification"
limitint50Backend cap: 100
offsetint0Page offset
models = client.models.list(limit=20)
for m in models:
    print(m.name, m.architecture, m.status, m.metrics)
curl -s "https://api.pictograph.io/api/v1/developer/models/?limit=20" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

iter

Auto-paging iterator across every model in your organization.

for m in client.models.iter(page_size=50):
    print(m.id, m.model_type)
# Page manually with limit + offset until fewer than `limit` rows return.
curl -s "https://api.pictograph.io/api/v1/developer/models/?limit=50&offset=0" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

get

Fetch a single model by name (or model_id= UUID).

model = client.models.get("Stop Sign Detector")   # by name
model = client.models.get(model_id="model-uuid")   # by id
print(model.architecture, model.metrics["mAP"], model.class_mapping)
# By name (org-unique):
curl -s "https://api.pictograph.io/api/v1/developer/models/by-name/Stop%20Sign%20Detector" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"
# By UUID:
curl -s "https://api.pictograph.io/api/v1/developer/models/model-uuid" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns {"data": Model}. Inspect metrics (mAP, precision, recall) and class_mapping (index → class name) for inference setup.

update

Rename a model, edit its description / readme, set its license, or flip its visibility. Only the fields you pass change. Requires a member+ API key; changing visibility (publishing to Explore) requires admin+. A new_name that collides with another model in your org is rejected (400).

ArgTypeNotes
name / model_idstrAddress the model by name (positional) or UUID (keyword)
new_namestr | NoneRename the model
description / readmestr | NoneMetadata
visibility"private" | "public" | Noneadmin+ only
license_id / license_custom_textstr | NoneLicense catalog id, or "custom" + body
model = client.models.update("Stop Sign Detector", description="road-signs v2")
model = client.models.update(model_id="model-uuid", new_name="Signs v2", visibility="public")
curl -s -X PATCH "https://api.pictograph.io/api/v1/developer/models/by-name/Stop%20Sign%20Detector" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"description": "road-signs v2"}'

download

Stream the weights to a local file. Only status="ready" models are downloadable. format="onnx" (default) serves the exported ONNX graph; format="pytorch" serves the native PyTorch .pth. Models trained before dual-format export have ONNX only — format="pytorch" raises 409 ConflictError for those.

ArgTypeDefaultNotes
name / model_idstrrequiredAddress by name (positional) or UUID (keyword)
output_pathstr | PathrequiredLocal destination
format"onnx" | "pytorch""onnx"Weights format
from pathlib import Path

client.models.download("Stop Sign Detector", output_path=Path("./yolox.onnx"))
client.models.download(model_id="model-uuid", output_path=Path("./yolox.onnx"))
# Returns {"data": {"download_url": …, "expires_in_minutes": 60, …}}; fetch the weights from it.
curl -s "https://api.pictograph.io/api/v1/developer/models/by-name/Stop%20Sign%20Detector/download?format=onnx" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

The SDK download is chunked and lands in a sibling .part file renamed atomically on success. Safe for multi-GB models.

fork

Import (fork) a public model into your organization. The model analog of forking a public dataset: the fork references the source model’s weights (no byte copy), so it is downloadable immediately and fast even for large models. The copy’s name is suffixed ("Name (2)") if a model of that name already exists. Requires member, admin, or owner role.

model = client.models.fork("source-public-model-uuid")
print(model.id, model.visibility, model.forked_from_model_id)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/models/source-public-model-uuid/fork" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns the newly created Model in your organization (visibility="private", status="ready").

delete

Delete a model by name (or model_id= UUID). Requires admin or owner role. Removes the database row immediately; GCS weight cleanup runs as a background task, so the file may linger briefly after the call returns.

client.models.delete("Stop Sign Detector")   # by name
client.models.delete(model_id="model-uuid")   # by id
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/models/by-name/Stop%20Sign%20Detector" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

bulk_delete

Delete many models in one atomic, org-scoped, server-side call — never fans out N requests. Idempotent: ids that don’t resolve in your org are returned in not_found rather than raising. Requires admin or owner role.

result = client.models.bulk_delete(["model-uuid-1", "model-uuid-2"])
print(result.succeeded, result.not_found, result.count)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/models/bulk-delete" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"model_ids": ["model-uuid-1", "model-uuid-2"]}'

Returns {"data": {"succeeded": [...], "not_found": [...], "count": N}}.

Status lifecycle

statusMeaning
trainingTraining is in progress; download returns an error until ready.
readyTrained successfully; weights downloadable via download().
failedTraining stopped with an error. Inspect the source TrainingRun.error_message.
archivedRetired via update(status="archived"). Hidden from list() unless the status="archived" filter is passed. (Distinct from delete, which removes the row.)

Inference

The SDK’s models resource ships read access only — there is no client.models.infer(). To run inference on the weights yourself, download the ONNX file and use your own runtime (onnxruntime, tensorrt, etc.):

import onnxruntime as ort

client.models.download(model.id, output_path="./model.onnx")
session = ort.InferenceSession("./model.onnx")
# … standard ORT inference loop

For managed inference, run the model through a workflow (loads the weights per run, no separate deployment required).

Evaluation

client.model_evaluations scores a trained detection or instance-segmentation model against a labeled dataset’s ground truth — per-class and overall precision / recall / F1 plus a confusion matrix — running the inference for you server-side. Your ground-truth annotations are never modified. (For a purely offline scoring pass over predictions you already have, use pictograph.metrics.)

# One call: create the run and block until it completes.
ev = client.model_evaluations.evaluate(
    model_id="model-uuid",
    project_id="labeled-dataset-uuid",
    iou_threshold=0.5,
    confidence_threshold=0.5,
)

print(ev.overall_metrics.precision, ev.overall_metrics.recall, ev.overall_metrics.f1)
for c in ev.per_class_metrics or []:
    print(c.class_name, c.precision, c.recall, c.support)

# The confusion matrix (rows = ground truth, cols = predicted; last row/col is
# `__background__` for false positives / false negatives).
cm = ev.confusion_matrix
print(cm.labels)
print(cm.grid)

Prefer to start it and poll later? Use the lower-level methods:

ev = client.model_evaluations.create("model-uuid", "labeled-dataset-uuid")
ev = client.model_evaluations.wait_for_completion(ev.id)   # or .get(ev.id) to poll manually
runs = client.model_evaluations.list(model_id="model-uuid")
client.model_evaluations.cancel(ev.id)

Evaluation runs on the trained-model batch-inference path and is billed the same way. An AsyncClient mirror is available at client.model_evaluations too. Only detection and instance-segmentation models are supported (other model types return a ValidationError).

Common errors

StatusExceptionCause
404NotFoundErrorName/model_id missing or belongs to another org; or fork source is not a ready public model
409ConflictErrordownload(format="pytorch") on a model with ONNX-only weights
400ValidationErrordownload on a non-ready model; or update(new_name=…) collides with an existing model
403ForbiddenErrorupdate/fork require member+; update(visibility=…), delete, bulk_delete require admin+
Copied to clipboard