# Scale a Workload to more units
This guide continues from [Deploy a Workload from a public image](/guides/containers-deploy). Read [Fixed and autoscaled policies](/units/scaling-policies) first if "units" is new to you.

It uses the `api` helper from the deploy guide, and these variables: `PROJECT_ID` (from the subscription) and `SERVICE_ID` (the service instance). The routes live under `/projects/{project_id}/service-instances/{id}/units`.

```bash
export UNITS="$ZAROZ_API/projects/$PROJECT_ID/service-instances/$SERVICE_ID/units"
```

:::note{title="API status"}
The units routes are used by the console and are tagged console-only in the API reference. They accept your session token or a personal access token.
:::

## 1. See what you have

```bash
curl -s "$UNITS" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '{scalable, max_units, included_units, multi_unit_in_use, volume_locked}'
```

```ts
const base = `/projects/${projectId}/service-instances/${serviceId}/units`;
const units = await api("GET", base);
console.log(units.scalable, units.max_units, units.included_units, units.volume_locked);
for (const u of units.data) console.log(u.ordinal, u.state, u.billing_class);
```

```python
base = f"/projects/{project_id}/service-instances/{service_id}/units"
units = api("GET", base)
print(units["scalable"], units["max_units"], units["included_units"], units["volume_locked"])
for u in units["data"]:
    print(u["ordinal"], u["state"], u["billing_class"])
```

`scalable` must be true and `volume_locked` false. `billing_class` is `included` for units the plan covers and `extra` for the rest.

## 2. Quote a fixed policy of 3 units

Nothing is saved by a quote.

```bash
curl -s -X POST "$UNITS/policy/quote" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"mode":"fixed","min_units":3}'
```

```ts
const quote = await api("POST", `${base}/policy/quote`, { mode: "fixed", min_units: 3 });
console.log(quote.extra_units, quote.hourly_cents, quote.monthly_cents);
```

```python
quote = api("POST", f"{base}/policy/quote", {"mode": "fixed", "min_units": 3})
print(quote["extra_units"], quote["hourly_cents"], quote["monthly_cents"])
```

Giving only `min_units` sets `max_units` to the same number. `monthly_cents` is the most that policy can cost in a month; the arithmetic is in [Billing](/units/billing).

## 3. Apply it

```bash
curl -s -X PATCH "$UNITS/policy" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"mode":"fixed","min_units":3}'
```

```ts
const policy = await api("PATCH", `${base}/policy`, { mode: "fixed", min_units: 3 });
console.log(policy.mode, policy.min_units, policy.max_units);
```

```python
policy = api("PATCH", f"{base}/policy", {"mode": "fixed", "min_units": 3})
print(policy["mode"], policy["min_units"], policy["max_units"])
```

A fixed policy applies at once: two units start. List the units again and watch their `state` move to running.

## 4. Autoscale between 1 and 4, targeting 60% CPU

```bash
curl -s -X PATCH "$UNITS/policy" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"mode":"autoscaled","min_units":1,"max_units":4,"cpu_target_percent":60,"spend_cap_cents":1500}'
```

```ts
await api("PATCH", `${base}/policy`, {
  mode: "autoscaled", min_units: 1, max_units: 4,
  cpu_target_percent: 60, spend_cap_cents: 1500,
});
```

```python
api("PATCH", f"{base}/policy", {
    "mode": "autoscaled", "min_units": 1, "max_units": 4,
    "cpu_target_percent": 60, "spend_cap_cents": 1500,
})
```

An autoscaled policy needs a CPU target, a memory target or a schedule rule. Omitted fields keep their value; send `null` to clear a nullable one, for example `"spend_cap_cents": null`.

## 5. Watch what it does

Decisions say what the autoscaler did and why; the count series shows how many units ran.

```bash
curl -s "$UNITS/decisions?page_size=5" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '.data[] | {at, action, summary}'
curl -s "$UNITS/count?window=24h" -H "Authorization: Bearer $ZAROZ_TOKEN" | jq '{included, markers}'
```

```ts
const decisions = await api("GET", `${base}/decisions?page_size=5`);
for (const d of decisions.data) console.log(d.at, d.action, d.summary);
const count = await api("GET", `${base}/count?window=24h`);
console.log(count.included, count.markers.length);
```

```python
decisions = api("GET", f"{base}/decisions", page_size=5)
for d in decisions["data"]:
    print(d["at"], d["action"], d["summary"])
count = api("GET", f"{base}/count", window="24h")
print(count["included"], len(count["markers"]))
```

## 6. Go back to one unit

```bash
curl -s -X PATCH "$UNITS/policy" \
  -H "Authorization: Bearer $ZAROZ_TOKEN" -H "Content-Type: application/json" \
  -d '{"mode":"fixed","min_units":1}'
```

```ts
await api("PATCH", `${base}/policy`, { mode: "fixed", min_units: 1 });
```

```python
api("PATCH", f"{base}/policy", {"mode": "fixed", "min_units": 1})
```

Units that are scaled away stop billing at once. If a change is refused, the `details` field starts with a reason code, for example `volume_locks_to_one_unit` or a limit (see [Limits](/units/limits)).
