> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gpuoutlet.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing and spot

> How holds and accrual work, when a charge becomes final, and the trade-off behind interruptible rentals.

Rentals are prepaid from your wallet balance. There is no invoice at the end of
the month and no card charged per rental — you top the wallet up, and rentals
draw it down.

Every rental carries a `billing` object:

```json theme={null}
{
  "currency": "usd",
  "price_per_hour_cents": 498,
  "accrued_cents": 1743,
  "hold_cents": 498
}
```

<ResponseField name="price_per_hour_cents" type="integer" required>
  For the whole rental at its size — two GPUs at \$2.49 each is 498.
</ResponseField>

<ResponseField name="accrued_cents" type="integer" required>
  Billed so far. Accrues **per second**, reported about once a minute. On a
  `stopped` rental this is the final charge.
</ResponseField>

<ResponseField name="hold_cents" type="integer" required>
  Currently reserved on your balance. `0` once the rental is stopped.
</ResponseField>

## The hold

When a rental starts we reserve roughly an hour of runtime on your balance. It is
**not a charge and not a cap**:

* It does not leave your account — it is unavailable for other rentals until
  released.
* It does not limit how long the rental runs. A machine can run for days against
  a one-hour hold.
* When the rental stops, the hold is released and replaced by the real charge.

The point is to fail early. Without a hold, a rental would start against an empty
wallet and discover the problem an hour later, with a machine already running and
nothing to pay for it. With one, the failure is a clean `402
insufficient_balance` before anything is provisioned.

## Per-second accrual

Billing runs per second from the moment the rental is `running` — not from when
you asked for it. Provisioning is free, and a rental that never came up
(`failed`) is never charged at all.

`accrued_cents` updates about once a minute, so between updates it lags reality
by up to a minute. For a live cost display, extrapolate from
`price_per_hour_cents` and `started_at` rather than showing a number that visibly
jumps.

<Warning>
  `accrued_cents` is final **only** when the status is `stopped`. On a `stopping`
  rental the machine may still be running and the last seconds are not yet counted.
</Warning>

## When money moves

```
create   → hold placed on the balance, nothing charged
running  → accrual begins, per second
stopping → teardown requested; final seconds still being counted
stopped  → hold released, accrued_cents charged, hold_cents = 0
failed   → hold released, nothing charged
```

## Running out mid-rental

If the balance runs down while a rental is live, the rental is stopped and ends
with `termination_reason: stopped_by_operator`. The same applies when a key's
[daily spend limit](/api/spend-limits) is crossed by an ongoing rental, which
ends with `termination_reason: spend_limit_reached`.

Neither is a pleasant surprise mid-training. Two defences:

* **Auto top-up** in the dashboard, so the balance refills before it bites.
* **Watch `active=true` rentals** and their burn rate — see
  [Common queries](/api/managing-rentals#common-queries).

## Spot and preemption

A `spot` rental is materially cheaper and **can be reclaimed at any moment**.
When that happens the rental ends with:

```json theme={null}
{ "status": "stopped", "termination_reason": "preempted" }
```

and a `rental.preempted` event in its [history](/api/rental-lifecycle#the-event-history).
You are billed for what ran, nothing more.

There is no warning period, no drain interval, and no way to defer a reclaim.

### When spot is right

<CardGroup cols={2}>
  <Card title="Good fit" icon="check">
    Checkpointed training · batch inference over a queue · hyper-parameter sweeps
    · anything that can lose a worker and carry on
  </Card>

  <Card title="Bad fit" icon="xmark">
    An interactive session someone is typing into · a single long run with no
    checkpoints · anything holding state only in RAM
  </Card>
</CardGroup>

The honest way to decide: **how much work do you lose if it dies right now?** If
the answer is "up to the last checkpoint, so a few minutes", spot is close to
free money. If it is "eleven hours", the discount is not the trade you are
actually making.

### Building for preemption

```python theme={null}
rental = create_rental(offer_id=offer["id"], service_tier="spot")

while True:
    r = get_rental(rental["id"])

    if r["status"] == "running":
        time.sleep(30)
        continue

    if r["termination_reason"] == "preempted":
        # Expected, not exceptional. Resume from the last checkpoint elsewhere.
        resume_from_checkpoint()
        break

    break
```

Treat preemption as an ordinary branch, not an error path. Code that raises on
`preempted` will page someone at 3am for a thing that is working as designed.

Find spot capacity with `GET /offers?service_tier=spot`, and check
`spot_price_per_hour_cents` on the offer to see the discount before you commit.
