Sign in Get started

Deployments API

Stand a trained model up as an always-on inference endpoint, billed by uptime, and call it directly.

View as Markdown

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"
Copied to clipboard