Deployments API
Stand a trained model up as an always-on inference endpoint, billed by uptime, and call it directly.
A deployment turns a trained model into a live /predict endpoint you call over
HTTP. Each deployment is its own isolated app with one authenticated URL. Billing is by
uptime, so you pause a deployment to stop charges and resume it to bring the endpoint
back.
Two credentials, deliberately separate. Managing deployments uses your normal
X-API-Key. Calling a deployment’s /predict endpoint uses a per-deployment bearer
token (prefix pk_deploy_) returned once at create time. See
Calling your deployment.
Deployments require a paid tier (the model_deployment feature). For batch inference
over a dataset or video, use the Workflows API
instead: a workflow loads the model per run and needs no standing deployment.
Deployments are addressed by name on all three surfaces; a UUID works anywhere a name does.
Compute types
A deployment runs on one compute tier. min_containers=0 is scale-to-zero: you are
billed only while the endpoint is serving, with a short cold start on the first request
after idle. min_containers>=1 keeps that many containers warm, for lowest latency and
continuous billing.
compute_type |
gpu_type |
Notes |
|---|---|---|
gpu (default) |
t4 (default), l4, a10g, a100 |
Pick the smallest tier your model fits |
cpu |
not applicable | CPU-only inference for light models |
compute_options
The selectable tiers with their per-minute rate in micro-USD. Use it to populate a picker before quoting or creating.
Source: Deployments.compute_options
for option in client.deployments.compute_options():
print(option.label, option.compute_type, option.gpu_type, option.rate_per_min_micro_usd)
pictograph deployments compute-options
curl -s "https://api.pictograph.io/api/v1/developer/deployments/compute-options" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Sequence[DeploymentComputeOption]
DeploymentComputeOption · 7 fields
class DeploymentComputeOption(BaseModel):
"""A selectable deployment compute tier + its per-minute rate (one warm container)."""
key: str
label: str
compute_type: Literal['cpu', 'gpu']
gpu_type: Optional[Literal['t4', 'l4', 'a10g', 'a100']] = None
is_gpu: bool
description: str = ''
rate_per_min_micro_usd: int
quote
Cost quote for a tier before you create. The args mirror create, so you can
quote and then create with the same values.
Source: Deployments.quote
| 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 |
quote = client.deployments.quote(
compute_type="gpu",
gpu_type="t4",
min_containers=0,
)
print(quote.rate_per_min_micro_usd, quote.cost_per_hour_micro_usd, quote.billing_note)
pictograph deployments quote --compute-type gpu --gpu t4 --min-containers 0
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
DeploymentQuote · 5 fields
class DeploymentQuote(BaseModel):
"""Cost quote for a deployment, before creating it. All amounts are already-marked-up micro-USD (1 USD = 1_000_000 µUSD)."""
rate_per_min_micro_usd: int
cost_per_hour_micro_usd: int
cost_per_day_micro_usd: int
scale_to_zero: bool
billing_note: str
create
Deploy a trained model to a live endpoint. The response carries 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.
Source: Deployments.create
| Arg | Type | Default | Notes |
|---|---|---|---|
model |
str |
required | Trained model by name; status must be ready |
name |
str | None |
None |
Unique within your organization; 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 5) |
max_containers |
int |
1 |
Autoscale ceiling (max 10) |
scaledown_window |
int |
60 |
Seconds a container stays warm after the last request |
inference_config |
dict | None |
None |
Per-model defaults, e.g. a confidence threshold |
created = client.deployments.create(
model="Swift Falcon",
name="prod-detector",
compute_type="gpu",
gpu_type="t4",
min_containers=0,
max_containers=2,
scaledown_window=60,
)
print(created.deployment.id, created.deployment.status)
print(created.auth_token) # pk_deploy_... shown ONCE, store it now
pictograph deployments create "Swift Falcon" \
--name prod-detector --gpu t4 --min 0 --max 2 --scaledown 60
# REST takes the model's UUID; the SDK and CLI resolve the name for you.
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
CreatedDeployment · 2 fields
class CreatedDeployment(BaseModel):
"""Create response - carries the one-time plaintext bearer token."""
deployment: Deployment
auth_token: str
get
Fetch one deployment. Poll this after create until status is active.
Source: Deployments.get
| Arg | Type | Default | Notes |
|---|---|---|---|
deployment |
str |
required | Deployment name or id. |
deployment = client.deployments.get(
deployment="prod-detector",
)
print(deployment.status, deployment.endpoint_url)
pictograph deployments get prod-detector
curl -s "https://api.pictograph.io/api/v1/developer/deployments/prod-detector" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Deployment
Deployment · 19 fields
class Deployment(BaseModel):
"""A live (or provisioning) model inference deployment."""
id: str
organization_id: str
model_id: str
name: str
status: Literal['provisioning', 'active', 'paused', 'failed', 'terminated']
compute_type: Literal['cpu', 'gpu']
gpu_type: Optional[Literal['t4', 'l4', 'a10g', 'a100']] = None
min_containers: int
max_containers: int
scaledown_window: int
endpoint_url: str | None = None
auth_token_prefix: str | None = None
inference_config: dict[str, Any] = {}
cost_rate_per_min: int = 0
cost_per_hour: int | None = None
accrued_cost_credits: int = 0
uptime_seconds: int = 0
created_at: datetime | None = None
started_at: datetime | None = None
A cross-organization or missing name is a 404.
list
One page of deployments, optionally filtered by model or status.
Source: Deployments.list
| Arg | Type | Default | Notes |
|---|---|---|---|
model |
str | None |
None |
Restrict to deployments of one model, by name |
status |
DeploymentStatus | None |
None |
provisioning / active / paused / failed / terminated |
limit |
int |
50 |
Page size |
offset |
int |
0 |
Page offset |
for deployment in client.deployments.list(status="active", limit=50):
print(deployment.name, deployment.status, deployment.endpoint_url)
pictograph deployments list --status active --limit 50
curl -s "https://api.pictograph.io/api/v1/developer/deployments/?status=active&limit=50" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns list[Deployment]
Deployment · 19 fields
class Deployment(BaseModel):
"""A live (or provisioning) model inference deployment."""
id: str
organization_id: str
model_id: str
name: str
status: Literal['provisioning', 'active', 'paused', 'failed', 'terminated']
compute_type: Literal['cpu', 'gpu']
gpu_type: Optional[Literal['t4', 'l4', 'a10g', 'a100']] = None
min_containers: int
max_containers: int
scaledown_window: int
endpoint_url: str | None = None
auth_token_prefix: str | None = None
inference_config: dict[str, Any] = {}
cost_rate_per_min: int = 0
cost_per_hour: int | None = None
accrued_cost_credits: int = 0
uptime_seconds: int = 0
created_at: datetime | None = None
started_at: datetime | None = None
iter
Auto-paging iterator over every deployment. The CLI and REST surfaces page manually.
Source: Deployments.iter
| Arg | Type | Default | Notes |
|---|---|---|---|
model |
str | None |
None |
Model name or id. |
name |
str | None |
None |
Only deployments whose name matches. |
status |
DeploymentStatus | None |
None |
Only deployments in this state: provisioning, active, paused, failed or terminated. |
page_size |
int |
50 |
Rows fetched per underlying request. Tuning only - the iterator yields every item either way. |
max_total |
int | None |
None |
Stop after this many items. None walks everything. |
for deployment in client.deployments.iter(page_size=50):
print(deployment.name, deployment.status)
curl -s "https://api.pictograph.io/api/v1/developer/deployments/?limit=50&offset=0" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
# The CLI does not auto-page; this is a single page.
pictograph deployments list
Returns OffsetPager[Deployment]
Deployment · 19 fields
class Deployment(BaseModel):
"""A live (or provisioning) model inference deployment."""
id: str
organization_id: str
model_id: str
name: str
status: Literal['provisioning', 'active', 'paused', 'failed', 'terminated']
compute_type: Literal['cpu', 'gpu']
gpu_type: Optional[Literal['t4', 'l4', 'a10g', 'a100']] = None
min_containers: int
max_containers: int
scaledown_window: int
endpoint_url: str | None = None
auth_token_prefix: str | None = None
inference_config: dict[str, Any] = {}
cost_rate_per_min: int = 0
cost_per_hour: int | None = None
accrued_cost_credits: int = 0
uptime_seconds: int = 0
created_at: datetime | None = None
started_at: datetime | None = None
Calling your deployment
Once a deployment is active, call its endpoint_url (it already ends in /predict)
with the pk_deploy_ token from create. This request uses the per-deployment bearer
token, not your X-API-Key.
Source: DeploymentClient
infer arg |
Type | Default | Notes |
|---|---|---|---|
image |
str | bytes | Path |
required | Local path, an http(s):// URL, or raw bytes |
confidence |
float | None |
None |
Override the deployment’s default threshold |
class_filter |
list[str] | None |
None |
Restrict the returned classes |
top_k |
int | None |
None |
Classifiers: how many predictions to return |
from pictograph import Client, DetectionResult
client = Client()
deployment = client.deployments.get(
deployment="prod-detector",
)
# `task=` narrows the result type, exactly as it does on get_model.
infer = client.deployments.connect(
deployment=deployment,
api_key="pk_deploy_...",
task="object_detection",
)
result: DetectionResult = infer.infer("photo.jpg", confidence=0.4)
for prediction in result.predictions:
print(prediction.name, round(prediction.confidence, 2), prediction.bounding_box)
# infer_raw(...) returns the endpoint's JSON untouched.
# --endpoint makes the pk_deploy_ token the only credential needed.
pictograph deployments predict prod-detector photo.jpg \
--token "pk_deploy_..." --confidence 0.4
# 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. The response is the model’s JSON,
shaped {"predictions": [...], ...}.
With the URL in hand the token is the only credential you need, no account API key:
DeploymentClient(endpoint_url, "pk_deploy_...", task="object_detection") in Python, or
--endpoint <url> --token <token> on the CLI.
bulk_delete
Terminate many deployments and tear down their endpoints. One request the backend resolves per item, rather than N separate calls.
Idempotent: duplicates collapse, and any name that does not resolve is reported
in not_found rather than raising.
Source: Deployments.bulk_delete
| Arg | Type | Default | Notes |
|---|---|---|---|
deployments |
Sequence[str] |
required | Deployment names or ids to act on. Names that do not resolve are reported, not raised. |
result = client.deployments.bulk_delete(
deployments=["prod-detector", "staging-detector"],
)
print(result.count, "changed;", result.not_found, "not found")
# No bulk command - run the per-deployment one over your list.
pictograph deployments delete prod-detector
pictograph deployments delete staging-detector
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/bulk-delete" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"deployment_ids": ["$PROD_ID", "$STAGING_ID"]}'
Returns BulkDeleteResult
BulkDeleteResult · 3 fields
class BulkDeleteResult(BaseModel):
"""Result of a server-side bulk delete (one chunked, org-scoped call)."""
succeeded: list[str] = []
not_found: list[str] = []
count: int = 0
bulk_pause
Pause many deployments, stopping uptime billing on each. One request the backend resolves per item, rather than N separate calls.
Idempotent: duplicates collapse, and any name that does not resolve is reported
in not_found rather than raising.
Source: Deployments.bulk_pause
| Arg | Type | Default | Notes |
|---|---|---|---|
deployments |
Sequence[str] |
required | Deployment names or ids to act on. Names that do not resolve are reported, not raised. |
result = client.deployments.bulk_pause(
deployments=["prod-detector", "staging-detector"],
)
print(result.count, "changed;", result.not_found, "not found")
# No bulk command - run the per-deployment one over your list.
pictograph deployments pause prod-detector
pictograph deployments pause staging-detector
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": ["$PROD_ID", "$STAGING_ID"]}'
Returns BulkActionResult
BulkActionResult · 3 fields
class BulkActionResult(BaseModel):
"""Result of a server-side bulk state-change (e.g. pause/resume)."""
succeeded: list[str] = []
not_found: list[str] = []
count: int = 0
bulk_resume
Resume many paused deployments, re-provisioning each. One request the backend resolves per item, rather than N separate calls.
Idempotent: duplicates collapse, and any name that does not resolve is reported
in not_found rather than raising.
Source: Deployments.bulk_resume
| Arg | Type | Default | Notes |
|---|---|---|---|
deployments |
Sequence[str] |
required | Deployment names or ids to act on. Names that do not resolve are reported, not raised. |
result = client.deployments.bulk_resume(
deployments=["prod-detector", "staging-detector"],
)
print(result.count, "changed;", result.not_found, "not found")
# No bulk command - run the per-deployment one over your list.
pictograph deployments resume prod-detector
pictograph deployments resume staging-detector
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/bulk-resume" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"deployment_ids": ["$PROD_ID", "$STAGING_ID"]}'
Returns BulkActionResult
BulkActionResult · 3 fields
class BulkActionResult(BaseModel):
"""Result of a server-side bulk state-change (e.g. pause/resume)."""
succeeded: list[str] = []
not_found: list[str] = []
count: int = 0
connect
Build a DeploymentClient bound to one active deployment. It calls the
deployment’s own /predict URL with the pk_deploy_ bearer token, so it needs
no account API key - see Calling your deployment.
Source: Deployments.connect
| Arg | Type | Default | Notes |
|---|---|---|---|
deployment |
Deployment |
required | An active deployment, as returned by get or create. |
api_key |
str |
required | The deployment’s own pk_deploy_ token, shown once at create time. |
task |
TaskName | None |
None |
Narrows the returned result type. None reads the task from the deployment. |
timeout |
float |
60.0 |
Seconds to wait for a single prediction. |
deployment = client.deployments.get(
deployment="prod-detector",
)
endpoint = client.deployments.connect(
deployment=deployment,
api_key="pk_deploy_...",
task="object_detection",
)
result = endpoint.infer("photo.jpg", confidence=0.4)
# No `connect` command - it returns an SDK object, not a request. The CLI
# reaches the same endpoint directly:
pictograph deployments predict prod-detector ./photo.jpg --token pk_deploy_...
# `connect` builds a client locally and issues no request of its own. What it
# then calls is the deployment's endpoint:
curl -s -X POST "$ENDPOINT_URL" \
-H "Authorization: Bearer pk_deploy_..." \
-F "file=@photo.jpg"
Returns DeploymentClient[Any]
A typed client. infer(...) returns the parsed result for the task; infer_raw(...)
returns the endpoint’s JSON untouched.
delete
Terminate a deployment and tear down its serving endpoint. Uptime billing stops
and the pk_deploy_ token is invalidated. This is not reversible - create a new
deployment to serve the model again.
Source: Deployments.delete
| Arg | Type | Default | Notes |
|---|---|---|---|
deployment |
str |
required | Deployment name or id. |
client.deployments.delete(
deployment="prod-detector",
)
pictograph deployments delete prod-detector --yes
# REST takes the deployment's id; the SDK and CLI resolve the name for you.
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/deployments/$DEPLOYMENT_ID" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns None
pause
Pause a deployment by NAME (stops compute + billing). An id works too.
Source: Deployments.pause
| Arg | Type | Default | Notes |
|---|---|---|---|
deployment |
str |
required | Deployment name or id. |
deployment = client.deployments.pause(
deployment="prod-detector",
)
print(deployment.status) # paused - uptime billing stops here
pictograph deployments pause signs-detector
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/my-deployment/pause" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Deployment
Deployment · 19 fields
class Deployment(BaseModel):
"""A live (or provisioning) model inference deployment."""
id: str
organization_id: str
model_id: str
name: str
status: Literal['provisioning', 'active', 'paused', 'failed', 'terminated']
compute_type: Literal['cpu', 'gpu']
gpu_type: Optional[Literal['t4', 'l4', 'a10g', 'a100']] = None
min_containers: int
max_containers: int
scaledown_window: int
endpoint_url: str | None = None
auth_token_prefix: str | None = None
inference_config: dict[str, Any] = {}
cost_rate_per_min: int = 0
cost_per_hour: int | None = None
accrued_cost_credits: int = 0
uptime_seconds: int = 0
created_at: datetime | None = None
started_at: datetime | None = None
resume
Resume a paused deployment by NAME (re-provisions the endpoint).
Source: Deployments.resume
| Arg | Type | Default | Notes |
|---|---|---|---|
deployment |
str |
required | Deployment name or id. |
deployment = client.deployments.resume(
deployment="prod-detector",
)
print(deployment.status, deployment.endpoint_url)
pictograph deployments resume signs-detector
curl -s -X POST "https://api.pictograph.io/api/v1/developer/deployments/my-deployment/resume" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Deployment
Deployment · 19 fields
class Deployment(BaseModel):
"""A live (or provisioning) model inference deployment."""
id: str
organization_id: str
model_id: str
name: str
status: Literal['provisioning', 'active', 'paused', 'failed', 'terminated']
compute_type: Literal['cpu', 'gpu']
gpu_type: Optional[Literal['t4', 'l4', 'a10g', 'a100']] = None
min_containers: int
max_containers: int
scaledown_window: int
endpoint_url: str | None = None
auth_token_prefix: str | None = None
inference_config: dict[str, Any] = {}
cost_rate_per_min: int = 0
cost_per_hour: int | None = None
accrued_cost_credits: int = 0
uptime_seconds: int = 0
created_at: datetime | None = None
started_at: datetime | None = None
Common errors
| Status | Exception | Cause |
|---|---|---|
| 402 | PaymentRequiredError |
Insufficient credits to provision or resume |
| 403 | ForbiddenError |
Role too low (delete needs admin+), or model_deployment is not on your tier |
| 404 | NotFoundError |
Deployment or model does not exist in your organization |
| 409 | ConflictError |
Duplicate name, or an action invalid for the current status |
| 422 | ValidationError |
Unknown gpu_type, or gpu_type missing for compute_type="gpu" |