Deployments
Stand a trained model up as an always-on, per-organization inference endpoint, billed by uptime, and call it directly.
A deployment turns a trained model into a live /predict endpoint you call
directly over HTTP. Each deployment is its own isolated app with one authenticated
URL. Billing is by uptime (the underlying GPU/CPU cost plus the platform markup), so
you pause a deployment to stop charges and resume it to bring the endpoint back.
There are two authentication contexts, and they are deliberately separate:
- Managing deployments (create, list, pause, resume, delete) goes through the
Pictograph backend with your normal
X-API-Key, shown in every example below. - Calling a deployment’s
/predictendpoint uses a different, per-deployment bearer token (prefixpk_deploy_) that is returned once at create time — not yourX-API-Key. See Calling your deployment.
Deployments require a paid tier (the model_deployment feature). Use the
Workflows API instead if you only need batch
inference over a dataset or video, since a workflow loads the model per run and needs
no standing deployment.
from pictograph import Client
client = Client() # reads PICTOGRAPH_API_KEY
Compute types
A deployment runs on one compute tier. min_containers=0 is scale-to-zero (you are
billed only while the endpoint is actively serving, with a short cold start on the
first request after idle); min_containers>=1 keeps that many containers always warm
(lowest latency, billed continuously).
compute_type | gpu_type | Notes |
|---|---|---|
gpu (default) | t4 (default), l4, a10g, a100 | GPU inference; pick the smallest tier your model fits |
cpu | not applicable | CPU-only inference for light models |
compute_options
List the selectable compute tiers with their per-minute rate (already marked up, micro-USD). Use this to populate a picker before quoting or creating.
for opt in client.deployments.compute_options():
print(opt.label, opt.compute_type, opt.gpu_type, opt.rate_per_min_micro_usd)
curl -s "https://api.pictograph.io/api/v1/developer/deployments/compute-options" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns list[DeploymentComputeOption].
quote
Get a cost quote for a tier before you create a deployment. The args mirror create,
so you can quote and then create with the same values.
| Arg | Type | Default | Notes |
|---|---|---|---|
compute_type | ComputeType | "gpu" | "gpu" or "cpu" |
gpu_type | DeploymentGpuType | None | None | Required when compute_type="gpu" |
min_containers | int | 0 | 0 quotes scale-to-zero; >=1 quotes always-warm |
q = client.deployments.quote(compute_type="gpu", gpu_type="t4", min_containers=0)
print(q.rate_per_min_micro_usd, q.cost_per_hour_micro_usd, q.billing_note)
curl -s "https://api.pictograph.io/api/v1/developer/deployments/quote?compute_type=gpu&gpu_type=t4&min_containers=0" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns DeploymentQuote.
create
Deploy a trained model to a live endpoint. The response includes a one-time plaintext
bearer token (prefix pk_deploy_). Store it now: only its hash is kept, so it can
never be retrieved again. The deployment starts in provisioning; poll get until
status is active and endpoint_url is set, then call it (see
Calling your deployment).
| Arg | Type | Default | Notes |
|---|---|---|---|
model_id | str | required | The trained model to deploy |
name | str | None | None | Unique within your org; auto-generated if omitted |
compute_type | ComputeType | "gpu" | "gpu" or "cpu" |
gpu_type | DeploymentGpuType | None | "t4" | Ignored for compute_type="cpu" |
min_containers | int | 0 | 0 is scale-to-zero; >=1 keeps warm |
max_containers | int | 1 | Upper bound for autoscale |
scaledown_window | int | 60 | Seconds a container stays warm after the last request |
inference_config | dict | None | None | Per-model defaults (e.g. confidence threshold) |
created = client.deployments.create(
"model-uuid",
name="prod-detector",
compute_type="gpu",
gpu_type="t4",
min_containers=0, # scale-to-zero
max_containers=2,
scaledown_window=60,
)
print(created.deployment.id, created.deployment.status)
print(created.auth_token) # pk_deploy_… shown ONCE — store it now
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model_id": "model-uuid", "name": "prod-detector", "compute_type": "gpu", "gpu_type": "t4", "min_containers": 0, "max_containers": 2, "scaledown_window": 60}'
Returns CreatedDeployment (the Deployment plus the one-time auth_token).
get
Fetch a single deployment by UUID. Poll this after create until status is
active.
dep = client.deployments.get("deployment-uuid")
print(dep.status, dep.endpoint_url)
curl -s "https://api.pictograph.io/api/v1/developer/deployments/deployment-uuid" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Deployment. A cross-org or missing id is a 404.
list
Single-page list of deployments in your organization, optionally filtered by model or status.
| Arg | Type | Default | Notes |
|---|---|---|---|
model_id | str | None | None | Restrict to deployments of one model |
status | DeploymentStatus | None | None | provisioning / active / paused / failed / terminated |
limit | int | 50 | Page size |
offset | int | 0 | Page offset |
for dep in client.deployments.list(status="active", limit=50):
print(dep.name, dep.status, dep.endpoint_url)
curl -s "https://api.pictograph.io/api/v1/developer/deployments/?status=active&limit=50" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns list[Deployment].
iter
Auto-paging iterator over every deployment (transparently follows offset).
for dep in client.deployments.iter(page_size=50):
print(dep.name, dep.status)
# Page manually with limit + offset until fewer than `limit` rows return.
curl -s "https://api.pictograph.io/api/v1/developer/deployments/?limit=50&offset=0" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
pause
Pause an active deployment. This scales its containers to zero and stops uptime billing. Idempotent.
dep = client.deployments.pause("deployment-uuid")
print(dep.status) # "paused"
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/deployment-uuid/pause" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Deployment.
resume
Resume a paused deployment. This re-provisions the endpoint (a short credit pre-check
runs first), so the endpoint_url may take a few seconds to come back. Requires the
model_deployment feature (paid tier).
dep = client.deployments.resume("deployment-uuid")
print(dep.status) # "provisioning" then "active"
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/deployment-uuid/resume" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Deployment.
delete
Terminate a deployment and tear down its app. The endpoint and its bearer token stop
working immediately. Requires admin or owner role.
client.deployments.delete("deployment-uuid")
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/deployments/deployment-uuid" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
bulk_pause / bulk_resume / bulk_delete
Apply pause, resume, or delete to many deployments in one org-scoped server-side call,
instead of fanning out N single requests. The backend resolves each id per item;
duplicate ids are collapsed, and any id that does not resolve in your org or is not in
a valid state for the action lands in not_found rather than raising. Each requires
the same role as its single-item counterpart.
res = client.deployments.bulk_pause(["dep-a", "dep-b", "dep-c"])
print(res.succeeded, res.not_found, res.count)
client.deployments.bulk_resume(["dep-a", "dep-b"])
client.deployments.bulk_delete(["dep-c"])
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/bulk-pause" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"deployment_ids": ["dep-a", "dep-b", "dep-c"]}'
# bulk-resume and bulk-delete take the same body shape:
# POST /api/v1/developer/deployments/bulk-resume
# POST /api/v1/developer/deployments/bulk-delete
bulk_pause and bulk_resume return BulkActionResult (succeeded / not_found /
count); bulk_delete returns BulkDeleteResult (deleted / not_found /
count).
Calling your deployment
Once a deployment is active, call its endpoint_url (it already ends in
/predict) with the pk_deploy_ token returned by create. This request uses the
per-deployment bearer token, NOT your X-API-Key. The SDK ships a small
DeploymentClient (its ergonomics mirror Roboflow’s InferenceHTTPClient).
infer arg | Type | Default | Notes |
|---|---|---|---|
image | str | bytes | Path | required | A local path, an http(s):// URL, or raw image bytes |
confidence | float | None | None | Override the deployment’s default confidence threshold |
class_filter | list[str] | None | None | Restrict the returned classes |
top_k | int | None | None | For classifiers, how many predictions to return |
from pictograph import Client, DeploymentClient
client = Client()
dep = client.deployments.get("deployment-uuid")
# Build the direct client from the active deployment + the pk_deploy_ token.
infer = client.deployments.connect(dep, api_key="pk_deploy_…")
# or, equivalently, construct it yourself from the endpoint URL:
# infer = DeploymentClient(dep.endpoint_url, "pk_deploy_…")
result = infer.infer("photo.jpg", confidence=0.4) # path | URL | bytes
print(result["predictions"])
# Direct call to the deployment endpoint — Bearer pk_deploy_ token, NOT X-API-Key.
# A local file as multipart:
curl -s -X POST "$ENDPOINT_URL" \
-H "Authorization: Bearer pk_deploy_…" \
-F "file=@photo.jpg"
# Or a URL / base64 image as JSON, with options inline:
curl -s -X POST "$ENDPOINT_URL" \
-H "Authorization: Bearer pk_deploy_…" \
-H "Content-Type: application/json" \
-d '{"image": {"type": "url", "value": "https://example.com/photo.jpg"}, "confidence": 0.4}'
$ENDPOINT_URL is the deployment’s endpoint_url (it already ends in /predict).
The response is the model’s JSON, shaped {"predictions": [...], ...}.
Common errors
| Status | Exception | Cause |
|---|---|---|
| 402 | PaymentRequiredError | Insufficient credits to provision or resume |
| 403 | ForbiddenError | Role too low (delete needs admin+), or the model_deployment feature is not on your tier |
| 404 | NotFoundError | Deployment or model id does not exist in your org |
| 409 | ConflictError | Duplicate name, or an action invalid for the current status (e.g. resume a non-paused deployment) |
| 422 | ValidationError | Unknown gpu_type, or gpu_type missing for compute_type="gpu" |