---
title: Deployments
description: Stand a trained model up as an always-on, per-organization inference endpoint, billed by uptime, and call it directly.
section: API Reference
order: 19
---
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 `/predict` endpoint uses a different, per-deployment
  bearer token (prefix `pk_deploy_`) that is returned once at create time — not your
  `X-API-Key`. See [Calling your deployment](#calling-your-deployment).

Deployments require a paid tier (the `model_deployment` feature). Use the
[Workflows API](/docs/api-reference/workflows.md) 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.

```python
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.

```python
for opt in client.deployments.compute_options():
    print(opt.label, opt.compute_type, opt.gpu_type, opt.rate_per_min_micro_usd)
```

```bash
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 |

```python
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)
```

```bash
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](#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) |

```python
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
```

```bash
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`.

```python
dep = client.deployments.get("deployment-uuid")
print(dep.status, dep.endpoint_url)
```

```bash
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 |

```python
for dep in client.deployments.list(status="active", limit=50):
    print(dep.name, dep.status, dep.endpoint_url)
```

```bash
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`).

```python
for dep in client.deployments.iter(page_size=50):
    print(dep.name, dep.status)
```

```bash
# 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.

```python
dep = client.deployments.pause("deployment-uuid")
print(dep.status)   # "paused"
```

```bash
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).

```python
dep = client.deployments.resume("deployment-uuid")
print(dep.status)   # "provisioning" then "active"
```

```bash
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.

```python
client.deployments.delete("deployment-uuid")
```

```bash
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.

```python
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"])
```

```bash
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 |

```python
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"])
```

```bash
# 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"` |