---
sidebar_position: 9
title: Billing
description: Pods are billed per second at the listed hourly price, with no minimum. How the charge is computed, when it hits your balance, and what happens when the balance runs out.
---

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

# Billing

Every price on Lium is an hourly USD rate — per GPU on the marketplace, per pod on the deploy summary — but you are **billed per second**: a pod is charged `hourly price × seconds / 3600` for the exact seconds it existed, with no minimum and no rounding up to a minute or an hour. A pod at $2.40/hour that you delete after 20 seconds costs $0.0133.

## What is charged

| Item | Rate | Granularity |
|------|------|-------------|
| Pod | Pod price per hour = provider's per-GPU price × GPUs (set at deploy; a later price change by the provider reaches a running pod only as described under [When the provider changes the price](#when-the-provider-changes-the-price)) | Per second, by pod status: see [When a pod is charged](#when-a-pod-is-charged) |
| Cluster | Sum of every node's pod price | Per second, each node billed as its own pod |
| Volume | `$/GB/hour` on the data actually stored | Daily, see [Volumes](./volumes) |
| Backups | `$/GB/hour` on the backup data stored in S3 | Daily, see [Backups](./backups) |

## When a pod is charged

Every 5 minutes the platform charges each pod whose status is **RUNNING**, **REBOOT_PENDING** or **REBOOT_FAILED**, for the seconds since that pod's previous charge. A pod in any other status is not charged at that tick. This one table is the rule; the other pages link here.

| Status or event | Charged? | Detail |
|---|---|---|
| **PENDING** (provisioning: node setup, image pull, start) | Not while it is PENDING; yes once it turns RUNNING | A pod's first charge counts from the moment the pod was created (the deploy request). So a pod that reaches **RUNNING** pays for its provisioning window too, at its first 5-minute charge. |
| Start fails (**CREATION_FAILED**), or you delete a pod that is still **PENDING** | No | The pod never reached a charged status, so it is never charged, not even at removal. |
| Start moved to another node by the platform | From the move | When a pod fails to start because of its node, Lium starts it on another node of the same GPU at the same or a lower price. The charge counts from that move; the time on the first node is not charged (see [Broken pods](./broken-pods#when-a-pod-fails-to-start)). |
| **RUNNING** | Yes | Per second at the pod's price. |
| Reboot, edit or template switch (**REBOOT_PENDING**) | Yes | Same pod, same price. |
| **REBOOT_FAILED** | Yes | The node stays reserved for your pod, whatever the error says: the image, environment, start and health-check commands are yours, so the failure can come from them. Delete the pod to stop paying (see [Pod disappeared or failed](./pod-disappeared)). |
| Node offline: the validator stops reporting the node | Yes until the node is marked inactive, then no | A node is marked inactive once it has gone an hour without a validator report (checked every 15 minutes). From then on each 5-minute charge on its pods is $0 until the node is active again. Provider [maintenance](./provider-maintenance) follows the same rule; a window is at most 60 minutes. |
| **BROKEN** (the provider force-closed the pod) | No | See [Broken pods](./broken-pods). |
| Balance at zero, 2-minute grace | Yes | The pods run, and are charged, until they are removed. See [When the balance runs out](#when-the-balance-runs-out). |
| Removal (by you, a [scheduled termination](./scheduled-termination), a provider removal notice, an unavailable node, an API-key budget or an empty balance) | Up to the removal request | A pod in a charged status is charged the seconds since its last 5-minute charge at the removal request. Nothing after: **DELETING** is not charged. |

## When the provider changes the price

A provider can change a node's price while your pod runs on it. New rentals pay the new price at once. What matters for your pod is the listed price against the price your pod pays now, not whether the provider moved the number up or down:

- **A listed price above what your pod pays** reaches your pod **7 days** after you are told. You get an e-mail with both prices and the exact UTC time, and the pod page shows the same line until then. Until that time the pod bills at the price you rented it at. To avoid the new price, delete the pod before that time. If the provider changes the price again inside the 7 days to anything above what you pay, you are told again and the 7 days start over from that mail. A stopped or failed pod gets the same notice: it keeps its price for its next start or reboot, and a reboot keeps the pending change. This also covers a provider who lowers a listing that is still above your price: for your pod that is a raise, with the same 7 days.
- **A listed price at or below what your pod pays** is for new rentals only. Your pod keeps the price you rented it at, and a pending change is canceled; the line on the pod page goes away. So a provider can price one node's free GPUs lower without changing what the GPUs already rented on it pay.

`GET /pods` shows a pending change as `pending_price` (the hourly price your pod moves to) and `price_change_effective_at` (when); both are null when nothing is pending.

## When it hits your balance

- **Every 5 minutes** the platform charges each running pod for the seconds since its previous charge and subtracts the total from your balance. The first charge covers the seconds since deploy, provisioning included (see [When a pod is charged](#when-a-pod-is-charged)).
- **When a pod is removed** — by you, by a [scheduled termination](./scheduled-termination), by the provider reclaiming the node, or because your balance ran out — the seconds since the last 5-minute charge are settled immediately. Nothing is charged after the removal request (see [When a pod is charged](#when-a-pod-is-charged)).

The **Billing** page shows the balance to the cent and daily totals; the ledger underneath keeps the full precision, so small charges are not lost to rounding.

## When the balance runs out

- Deploying requires a balance covering at least **15 minutes** of the pod's hourly price; the error tells you the exact amount.
- If a 5-minute charge takes your balance to zero or below, your pods keep running for **2 more minutes**, counted from that charge. Auto Top Up, when it is on, charges your card its set amount once in those 2 minutes, at that charge; the amount counts once the card payment is confirmed. Your pods stay when the balance is above zero at the end of the 2 minutes, from Auto Top Up or a top-up of your own. A set amount smaller than the balance owed leaves it at zero or below, and the pods are removed at the end of the 2 minutes. The balance can go slightly negative, by about the previous 5 minutes of running pods plus those 2 minutes, because the pods ran before the check noticed. Volumes and backups keep their daily charges until you delete them, so the balance keeps falling while they exist. Top up to clear it.
- **You are warned first.** Runway is `balance ÷ hourly cost of everything running` (pods plus volumes). You get one email when runway drops below your threshold (default 24 hours), then one at about 1 hour and one at 15 minutes. **Notification Settings** on your profile set the threshold and turn these three off. Each of the last two goes out once per dip. After a top-up they can come again on the next dip, but not within 6 hours of the last warning, unless your pods were removed in between. Neither is sent while a working **Auto Top Up** is about to refill the balance. Under 1 hour of runway the web app also shows a banner on every page. For scripts, `GET /users/me` carries the same warning as `balance_warning` (`kind`, `runway_minutes`, `burn_per_hour`, `detail`). When the pods are removed, you get an email naming them, the charge that emptied the balance and a top-up link. A red banner about the removed pods then replaces the runway banner until you top up, for at most 24 hours; if you still have volumes and the balance is at or below zero, the runway banner comes back after that.
- **Auto Top Up** on the **Billing** page refills the balance from a card on file when the balance falls below a threshold. If the card is declined, the charge is retried after 1 hour and 6 hours and paused after three declines. You get one email per decline streak. The banner shows the bank's reason until an automatic top-up goes through, or until you save the Auto Top Up settings again or turn it off. One charge at a time: after a top-up is charged, the next one waits until that payment is credited to your balance, normally within seconds; if the credit takes more than 15 minutes, the payment provider is asked about that payment, and no new charge is made until it is credited or has failed. If the payment provider answers with an error of its own, or does not answer, the next 5-minute checks repeat that same request rather than start a new charge; after 15 minutes the provider is asked whether that request charged the card: if it did, that payment is credited, and if not, a new charge is made.
- **Minimums.** Card and stablecoin top-ups start at $10 and automatic top-ups at $20; the Top Up form shows the same $10 minimum for TAO and alpha. TAO and alpha payments, like coins sent straight from a wallet (`btcli`, `lium fund`), are credited at the amount received. The Top Up form pre-fills the $10 and names it under a smaller amount; the API refuses a smaller card or stablecoin amount with the same sentence (`GET /users/me` carries both numbers as `top_up_minimums`). The $20 is the **Top-up with** amount an Auto Top Up is saved with. An Auto Top Up you saved below $20 before the minimum applied keeps charging its saved amount. You can still switch it off (set **Balance falls below** to 0) or save it unchanged to resume it after a declined card, at its saved amount; any other change takes an amount of $20 or more.

## When a crypto top-up does not land

A crypto (TMC Pay) invoice gives you 30 minutes to send the exact amount. *Not available yet; this section describes how it will work:*

- The **Billing** page shows a **Pending crypto deposits** card with one row per invoice of the last 7 days that still needs a look: waiting for payment (with the minutes left), payment seen on the network (N of M confirmations, and until when TMC Pay holds the invoice open), partly paid (what confirmed and the remainder to send to the same address before the window closes), payment window closed with nothing seen, expired or canceled, or paid short. Each row has **Open invoice** while the invoice can still be paid and **Create a new invoice**, which opens the top-up form in crypto mode with the amount filled in. A fully paid invoice leaves the card; it is in the billing history.
- You get one email when an invoice expires unpaid, naming any crypto that arrived after the window closed (it is not credited yet; do not send more to that address). A canceled invoice sends no mail. You get one email when an invoice is paid short, with the exact shortfall; its link opens the top-up form with that shortfall filled in, or the minimum top-up when the shortfall is smaller (the same on the card's **Create a new invoice**).
- Both emails name the invoice id and reply to support. If you already sent the crypto, reply to the email with that invoice id so support can look into it.
- `GET /tmc-pay/invoices` returns the same rows for scripts (`status`, `provider_status`, `fiat_amount`, `credited_amount_in_usd`, `hosted_invoice_url`, `expires_at`, `confirmations`, `required_confirmations`, `payment_hold_until`). Through an API key it needs the `billing` scope, like the payment ledger (`GET /transactions`).

Already today, a transfer that reaches an invoice after its window closed raises an alert for the Lium team, so support hears about it without a ticket.

<details>
<summary>For agents and automation: API + CLI</summary>

`GET /users/me` returns `"billing_granularity": "second"` next to `balance`, so a client prints the unit it reads from the response. `lium balance` shows the balance; `lium ps` shows each pod's `$/h` and spend so far (uptime × price, displayed to the cent).

`GET /transactions` lists every payment, credit and refund row of your account (`platform`, `status`, `amount`, `amount_in_usd`, `transaction_paid_at`, ...). Each row also carries `is_deposit` (true when you paid this money in by card, crypto, TAO or alpha and the payment is `COMPLETED`; false for pending or failed rows, bonus rows, disputes and referral or admin credits) and `is_first_deposit` (true on the earliest deposit you ever completed, on no other row). The web app reads the two flags to report a completed top-up once.

```bash
curl -s https://lium.io/api/users/me -H "X-API-Key: $LIUM_API_KEY" | jq '{balance, billing_granularity}'
```

Each pod's hourly price is `price` on `GET /pods`; the marketplace per-GPU price is `price_per_gpu` on `GET /executors`.
</details>

## Top up by API

*Not in production yet — behind a switch the team turns on.* The **Billing** page's card top-up opens a Stripe Checkout page, which an agent has no browser for. Once the switch is on, `POST /payments/topup` charges a card you already saved, with no page to open:

```http
POST /payments/topup
X-API-Key: $LIUM_API_KEY
Content-Type: application/json

{"amount_usd": 50, "idempotency_key": "run-42"}
```

- **Who can call it.** A signed-in session, or an API key that holds the `billing` scope. Today's keys hold `read`, `rent` and `manage` and get a 403 naming the missing scope; `billing` is granted at key creation once the API-keys page offers it (not released), so by default a key cannot move money. A key made by a workspace member cannot charge the billing owner's card. A key with a budget (not released) is bound by it here too: at or past its `daily` or `max` budget the top-up is a 402 `API_KEY_BUDGET_EXCEEDED` naming the window, and nothing is charged; a signed-in session has no budget.
- **Which card.** `payment_method_id` from `GET /stripe/payments/payment-methods`; omitted, the default card, or the only saved card. Several saved cards and no default is a 402 `NO_DEFAULT_CARD`; no card at all is a 402 `NO_SAVED_CARD`.
- **How much.** At least **$10**, the same minimum as the card form, at most $999,999.99, charged in whole cents (a third decimal rounds half-up). A repeat of the same `idempotency_key` with the same amount does not charge again: you get the first call's transaction back (`200 succeeded` once the webhook has credited it, `202 processing` until then) without a Stripe call — its `card` fields are empty, since the row does not record the card the first call charged — whatever else the repeat says; only while the first answer is still lost does the repeat go to Stripe, which replays the charge within 24 hours — and refuses it with a 409 `IDEMPOTENCY_KEY_REUSED` if the repeat names another card. The same `idempotency_key` with a different amount is a new charge: the server folds the amount into the Stripe key (`api-topup-{user}-{amount in cents}-{your key}`), so another amount is another request and a second charge. Same key and same amount stay idempotent (safe to run twice) and do not charge twice. `idempotency_key` is required (a call without one is a 422): send a key you can repeat (the CLI always does).
- **What comes back.** Normally `202 {"status": "processing", "payment_intent_id": "pi_…", "transaction_id": "…", "idempotency_key": "run-42", "amount_usd": 50.0, "card": {"brand": "visa", "last4": "4242"}}`: Stripe has the charge, and the balance is credited by the same Stripe webhook that credits a Checkout top-up, usually within seconds. Until then the row (`transaction_id`) shows in `GET /transactions` as *Stripe payment (API top-up)*, pending; a `payment_intent_id` is not yet a credit. `200` with `"status": "succeeded"` comes only once the webhook has marked that row COMPLETED: a repeat with the same key after the credit, or a first call the webhook beat.
- **If the answer is lost.** Stripe charges the card before it answers, so a Stripe timeout on the charge call means the outcome is not known: you get a `202 {"status": "processing", "payment_intent_id": null, "transaction_id": "…", "idempotency_key": "…"}` (unlike the normal 202, `payment_intent_id` is null) and the same webhook settles the row within a minute — the charge may have gone through. Do not call again without that `idempotency_key`; with it, the repeat returns the same transaction instead of making a second charge — and if Stripe never received the first call, the repeat is the charge. A charge Stripe fails later turns the same row *failed*. A row that stays pending (the request never reached Stripe and you never repeated it) is not swept up yet; repeat the call with its key, or ignore it — nothing was charged.
- **When the bank wants a confirmation.** Some banks require a one-time 3-D Secure step that this path cannot show. You get a 402 with `error.code` = `CARD_AUTHENTICATION_REQUIRED` and a `dashboard_url`: top up once through `POST /stripe/create-checkout-session` with `"save_card": true` and finish the Checkout page it returns — that confirms the card and saves it for later API top-ups, but not as the default card — then retry with that card's `payment_method_id` (from `GET /stripe/payments/payment-methods`) and a **new** `idempotency_key`. A top-up on the **Billing** page is a one-time payment and saves no card. A declined card is a 402 `CARD_DECLINED` with the bank's `decline_code`; fix or replace the card, then retry with a new key. Stripe replays the same 402 for 24 hours if you reuse the key, and that replay cannot charge twice. Neither changes the balance; both show as a failed row in `GET /transactions` — as does a call Stripe refused before charging (a reused key with other parameters, a rejected request), with the reason in `failure_reason`.

The CLI wraps it as `lium topup card --amount 50 [--card pm_…]` (lium#272, not released), and the Python SDK as `Lium().topup_card(50)`.
