Skip to content

Billing records

Your organization's ledger by org API token: every charge down to the node and the price window, daily totals in your calendar, the balance now with the hourly burn rate and the balance over time, the balance card with the card on file and the auto reload setting, spend attributed per workload, and the unit prices in force. Five writes start the console's billing actions for a person: three hand back a hosted page URL, one tightens the spend limit, one configures auto reload.

Contract: GET /api/billing/openapi.json.

Route What it serves
GET /api/billing/transactions the itemized ledger: every charge and deposit, newest first, one entry per resource per price window
GET /api/billing/daily one row per calendar day: charges, deposits, the resources charged and their unit-hours
GET /api/billing/summary balance, lifetime totals, this month's charges, spend limit, hold state, the card on file, the auto reload setting
GET /api/billing/balance the balance now, the hourly burn rate the meter last assessed, and what it is made of by kind, cluster and unit price
GET /api/billing/balance/history the balance as a series over a window: start, step_s, one value per bucket
GET /api/billing/usage/workloads spend attributed per workload over a preset range
GET /api/billing/rates the unit prices in force for your organization
POST /api/billing/checkout {usd}: a hosted payment page URL for a person to complete; the deposit lands when they pay
POST /api/billing/setup a hosted page URL that saves a card and charges nothing
POST /api/billing/portal the payment portal URL: receipts and card changes
PUT /api/billing/limit {usd}: set the monthly spend limit where none exists, or lower it
PUT /api/billing/reload {enabled, threshold_usd, amount_usd}: the auto reload setting, a standing charge on the saved card whenever the balance runs low; a token may only disable it, lower the amount or raise the threshold

Any of your organization's tokens reads these routes, cluster-bound or org-wide. The organization is the token's own and nothing identifies it on the wire. There is no cluster selector: the ledger is organization wide, and each charge entry names its cluster. No call on this wire moves money by itself: the three hosted page routes return a URL that a person opens in a browser, and no card detail ever rides the token. Auto reload is the one standing authorization. Once enabled it charges the saved card whenever the balance falls under the threshold, so a token may only tighten it: enabling it stays a console action.

curl -H "Authorization: Bearer $NC_TOKEN" \
  "https://nationalcompute.com/api/billing/daily?limit=31"

Units

Amounts are integers in microcredits: fields ending in _ucredits, where 1,000,000 microcredits = 1 credit = $1. Stamps are RFC 3339 UTC to the second (2026-09-10T14:00:00Z). Where a route aligns to your calendar it takes tz_offset_min, the JavaScript getTimezoneOffset() value (minutes UTC is ahead of local; 0 is UTC days; a value outside ±900 is refused with 422 bad-request).

The ledger

GET /api/billing/transactions returns one row per ledger transaction, newest first:

{
  "transactions": [
    {
      "type": "charge",
      "start": "2026-09-10T14:00:00Z",
      "end": "2026-09-10T14:15:00Z",
      "amount_ucredits": …,
      "source": "metered",
      "note": null,
      "balance_after_ucredits": 3998500000,
      "charges": [
        {
          "kind": "burst",
          "cluster": "acme-train",
          "resource_id": "…",
          "resource_type": "GPU_…",
          "units": 8,
          "unit_hourly_price_ucredits": …,
          "start": "2026-09-10T14:00:00Z",
          "end": "2026-09-10T14:15:00Z",
          "amount_ucredits": …
        }
      ]
    }
  ],
  "next": "1757513700000.2"
}
Field Meaning
type charge or deposit
start, end the window the row accounts for — metering windows are about 15 minutes
amount_ucredits always positive; type gives the sign
source metered (the platform's compute meter), inference (Marshall model usage charged to credits), public_research (a meter row made only of Public Research pool lease entries), card (a card payment), airdrop (the credits a public research organization receives at sign-up), manual (platform-authored: a wire deposit, a grant, an adjustment)
note a platform-authored note on a manual row, else null
charges[] the per-resource entries a metered row is made of — empty on deposits and manual rows; they sum to amount_ucredits
balance_after_ucredits the balance after this row in ledger order: the newest row's is the balance now, and each older row's is the next newer one plus a charge or minus a deposit. Rows a filter hides still move it
next the page cursor; pass it back as before for the next page. A full page always carries one (keyset paging cannot know whether more exists); a short or empty page ends the walk, next then null

Charge entries

Each entry is one resource over one price window, so a metered row lists every node you held in that window, once per rate change:

Field Meaning
kind burst: market capacity at the clearing rate. base_load: a base load block at its fixed rate. fixed: an attached node at the price posted for your organization. inference: one member's Marshall model usage over one metering pass. public_research: one whole node held under Public Research, at the flat rate in force when the lease was granted
cluster the cluster the resource served; null on an inference or public_research entry
resource_id the node's identity, reservation:<id> for a base load block, public_research:<request id> for a Public Research lease (the node rides node), or the shared volume's id
resource_type the priced type — a GPU type, a CPU type, or STORAGE_SHARED_GIB. GPU and CPU type names carry the hardware model; read them from the payload
units GPUs for a GPU type, nodes for a CPU type, GiB of used bytes for storage
unit_hourly_price_ucredits the rate per unit-hour assessed for this window
start, end the entry's own window inside the row's
principal, model, prompt_tokens, completion_tokens inference entries only: the member (username), the public model id the calls named, and the token counts in the pass. resource_type and resource_id are null there; units and unit_hourly_price_ucredits are 0
principal_kind inference entries only: member when a person's credential made the calls (principal is then the username). token when a credential other than a person's made them (principal is then that credential's id)
node, site, gpus, rate_ucr_per_gpu_hour, start_ms, end_ms public_research entries only: the node the lease held, the site id it ran on, the node's GPUs (the billed units), the flat rate per GPU-hour fixed when the lease was granted in microcredits (the same figure as unit_hourly_price_ucredits), and the entry's window as epoch milliseconds beside start and end. resource_type is the node's GPU type
amount_ucredits (end − start) / 1 h × units × unit_hourly_price_ucredits, rounded to the microcredit

Query parameters

Parameter Meaning
limit rows per page, 1–100; default 25
before the previous page's next (opaque)
type charge or deposit
source metered, inference, public_research, card, airdrop, or manual
since, until a half-open window on the row's end: RFC 3339 (2026-09-01T00:00:00Z; a bare date is midnight UTC) or epoch milliseconds

A bad value is 422 bad-request; detail names the parameter.

What the ledger read hides

  • Metered rows totalling $0 never appear — a metering window that cleared at $0 leaves an auditable zero row in the books, but the read returns neither it nor a day made only of such rows. Manual $0 rows stay (an adjustment can carry a note worth reading).
  • Who acted never leaves: a manual row names no platform operator. source is the class, nothing finer.

Daily totals

GET /api/billing/daily folds the ledger into one row per calendar day in your zone, newest first:

{
  "days": [
    {
      "date": "2026-09-10",
      "start": "2026-09-10T00:00:00Z",
      "end": "2026-09-11T00:00:00Z",
      "today": true,
      "charges_ucredits": …,
      "deposits_ucredits": 0,
      "transactions": 96,
      "resources": [
        {"kind": "burst", "resource_type": "GPU_…",
         "unit_hourly_price_ucredits": …,
         "units": 16, "unit_hours": 192.0}
      ],
      "clusters": ["acme-train"],
      "balance_end_ucredits": 3904000000
    }
  ],
  "next": "20706"
}
Field Meaning
date YYYY-MM-DD in your zone (tz_offset_min)
start, end the day's bounds as UTC stamps
today the current local day — its totals are still running
charges_ucredits, deposits_ucredits the day's totals
transactions ledger rows folded into the day
resources[] the day's charges per (kind, resource type, unit price), most unit-hours first: kind is burst / base_load / fixed / public_research as on a charge entry (inference charges fold into no resource line), units the distinct units charged that day, unit_hours their metered time
clusters cluster names touched that day
balance_end_ucredits the balance at the day's end; for today, the balance now

limit counts days (1–100, default 31); before takes the previous page's next, with the same full-page rule as the ledger. For the rows behind a day, read the ledger with since and until set to its bounds.

Balance and burn rate

GET /api/billing/balance is the one call a budget script polls: the balance now, the hourly rate the meter last assessed your organization, and what that rate is made of.

{
  "balance_ucredits": 4213500000,
  "burn_ucredits_hr": …,
  "as_of": "2026-09-11T17:05:00Z",
  "billing_hold": null,
  "resources": [
    {"kind": "burst", "cluster": "acme-train", "resource_type": "GPU_…",
     "units": 16, "unit_hourly_price_ucredits": …,
     "rate_ucredits_hr": …},
    {"kind": "fixed", "cluster": "acme-train",
     "resource_type": "STORAGE_SHARED_GIB", "units": 800,
     "unit_hourly_price_ucredits": …, "rate_ucredits_hr": …}
  ],
  "clusters": [{"cluster": "acme-train", "rate_ucredits_hr": …}]
}
Field Meaning
balance_ucredits the balance now; negative when charges outran deposits
burn_ucredits_hr microcredits per hour: the sum of units × unit price over every resource still held when the newest metering window closed. A node released or a rate replaced mid-window is not part of it; the entry that replaced it is
as_of the close of the metering window the rate was read from; null before the first charge
billing_hold the hold state the summary and the capacity reads carry, or null
resources[] the rate by (kind, cluster, resource_type, unit_hourly_price_ucredits), largest first, with the units held at that price; kind is burst / base_load / fixed / public_research (a live Public Research lease, cluster null). Jobs holding nodes at different clearing rates are separate lines, so the fold says what each limit price is costing
clusters[] the rate per cluster, largest first

The rate is read from the meter, not the market, so it reconciles with the ledger to the microcredit and covers every kind the meter bills: burst, base load, attached nodes and storage, on VM and Kubernetes clusters alike. The meter closes one window every 5 minutes, about 5 minutes after the fact, so the figure is 5 to 10 minutes behind real time and changes at most every 5 minutes; polling faster buys nothing. The meter writes no window in which nothing was billed, so a newest window older than 20 minutes reads as a rate of 0, with as_of still naming it. For one Kubernetes cluster's live market rate, ahead of the meter, read GET /api/k8s/market/spend.

Balance over time

GET /api/billing/balance/history returns the balance as a series over the last hours (1 to 8760, default 24; a value out of range is 422 bad-request). The path is the operation; hours rides the query string.

{"start": "2026-09-10T17:10:00Z", "step_s": 300,
 "balance_ucredits": [4300000000, 4300000000, 4289333333]}

step_s is the bucket width, max(300, span / 400) seconds, so a window is at most 400 points; bucket i covers (start + i × step_s, start + (i + 1) × step_s] and the last bucket closes now. balance_ucredits[i] is the balance at the close of bucket i: a balance is a step function, so buckets with no ledger event carry the last value forward, seeded from the last event before the window, and 0 before the first ever event. For the exact stamp of a deposit or charge read the ledger, whose rows carry balance_after_ucredits; for a day's closing balance read the daily totals.

Balance and totals

GET /api/billing/summary:

Field Meaning
balance_ucredits the current balance; negative when charges outran deposits
deposited_ucredits, charged_ucredits lifetime totals
month start, end, and charged_ucredits for the current calendar month in your zone
spend_limit_ucredits the monthly spend limit, or null; set on the console, or tightened by token
spend_limit_used_pct month.charged_ucredits as a percentage of the limit, one decimal; null without a limit. The console's limit bar warns at 75 and turns critical at 99. The value can exceed 100
billing_hold the hold state, or null — the same object the capacity read carries
spend_limit_enforced true when reaching the spend limit holds your organization's capacity. false when the platform records the limit and does not act on it for your organization's clusters; the cap is then the instruction a person gave the agent and nothing else stops it
billing_hold_enforced true when a balance at or below zero holds your organization's capacity. false when the platform records the balance and does not hold on it for your organization's clusters. spend_limit_enforced requires it
base_load true when your organization is on a base load arrangement
purchases true when self serve credit purchases are open to your organization on the console's Billing page
payment_method the card on file as {brand, last4}, or null when none is saved
card_max_usd, card_window_days the card cap: your organization's card payments may total card_max_usd USD inside any rolling card_window_days days (currently $50,000 per 7 days). Every card payment counts, checkout and auto reload alike
card_window_remaining_usd whole dollars a card payment can still carry right now: the cap minus the card payments inside the window, never negative. It rises as payments age out of the window
card_window_blocked true while card_window_remaining_usd is below the smallest purchase: no card payment goes through right now
min_usd, max_usd the smallest and the largest single checkout, whole dollars (platform constants; the card cap still applies)
auto_reload the auto reload setting: enabled, threshold_ucredits, amount_ucredits, last (status, reason, at) for the newest attempt whether or not auto reload is enabled now (null before the first), and cooldown_until: the stamp until which a failed attempt pauses new ones, or null
wire the wire transfer rail, or null while purchases is false
recent_deposits[] the last 20 deposits: at, amount_ucredits, source (card, airdrop or manual)
inference Marshall model usage charged to credits, dollars with cents: billed (true when model usage is charged to the organization's credits), free_tier_usd (the lifetime free allowance per member; 0 when model usage is billed from the first call), free_tier_remaining_usd (what the caller has left of it; null for an org token, which has no person behind it, or while the allowance is 0), charged_month_to_date_usd and charged_lifetime_usd (the organization's charged model usage)
public_research Public Research leases, as {gpu_hours_month_to_date, charged_month_to_date_usd}: GPU-hours this calendar month from the lease records (an open lease counts up to now; a free rate still counts) and the charged amount from the ledger's public_research entries, dollars with cents. Both null while the ledger cannot be read. The MCP billing_read summary view carries the same object

These read what the console's balance card shows. The writes start three of the console's actions for a person (buying credits, saving a card, opening the payment portal), tighten the spend limit and configure auto reload.

Hold state

billing_hold is one object on the summary, the balance read and the capacity reads, or null when nothing holds or counts down.

Field Meaning
state hold: an active hold. grace: a countdown to a hold
reason balance (the balance at or below zero), spend_limit (the month's charges at spend_limit_ucredits), or balance+spend_limit (both at once)
stage hold only. notice: growth is blocked and nothing is reclaimed yet. enforce: capacity is being reclaimed
enforce_after_ms hold only: epoch milliseconds when stage turns enforce
remaining_min grace only: minutes until the hold opens. The grace is 0 by default, so this state is rare
base_load_min present only while your organization holds a live base load block during a balance condition: minutes left before the block is cancelled

A deposit cures a balance condition. Only a raised or cleared limit, or the month rolling over, cures a spend_limit one. billing_hold_enforced and spend_limit_enforced say whether either can open for your organization at all.

Wire transfer

wire carries what the console's Billing page shows under Wire transfer, for amounts the card cap refuses. It is null while purchases is false: an organization on a reserved arrangement pays by invoice.

Field Meaning
bank_name, aba_routing, bank_address the receiving bank; aba_routing serves domestic wires and ACH
aba_routing_alt the routing number to use when the sending bank does not recognize aba_routing
swift_bic, intermediary_swift_bic international wires: the receiving bank's SWIFT code and the intermediary bank the wire must name
beneficiary_name, account_number, account_kind, beneficiary_address the beneficiary
reference your organization's memo for the current UTC day. It rolls at midnight UTC: read it on the day of the wire. It must ride the wire's reference or memo field, or the deposit is not matched to your organization promptly
notify_email the address to email when the wire is sent

The bank and beneficiary values are platform constants and appear on this wire and the console only.

Spend per workload

GET /api/billing/usage/workloads?range=7d re attributes charge entries to the jobs and pods that occupied the charged nodes, from the platform's occupancy samples. The figures are approximate by design: idle capacity and spend that cannot be attributed ride as their own labeled series, never smeared across jobs.

Parameter Meaning
range one of 1d, 7d, 30d, 90d, mtd, ytd, lastmonth (default 7d); anything else is 422 bad-request
tz_offset_min the calendar the buckets use, as on the other reads

The response carries starts[] (one stamp per bucket) and end, labels[] (HH:00 for 1d, MM-DD otherwise), today_index (the bucket holding today, null for lastmonth), and groups with two groupings, workload and user. Each grouping has order[], series (per name: spend[] in microcredits and unit_ms[], milliseconds of unit time, per bucket) and meta (per name: label, who, cluster, unit, handover_ucredits; the user grouping adds workloads, how many workloads the user ran). who names the person who started the workload: the Slurm username, or the creator of the Kubernetes object as the cluster's audit log recorded it (the console's "Started by"). A workload with no recorded creator carries its Helm release or app group instead. A workload with neither carries an empty who, and the user grouping lists it under "No user recorded". Series names are workload ids plus three sentinels: IDLE (held capacity no job used), UNATTRIBUTED (spend no sample explains) and OTHER (the fold of the smaller workloads, the way the console's chart folds them). The payload is served through a cache: cache_age is its age in seconds, and stale: true means a refresh is in flight, so read again in a moment for fresh figures.

A range nobody has read yet is computed on first request. When the computation takes more than a few seconds, the call answers 202 Accepted with {"pending": true, "retry_after_s": 3} and a Retry-After header instead of holding the connection. Poll the same URL after that many seconds. The 200 lands once the figures are ready, and later reads of the same range come from the cache.

Prices in force

GET /api/billing/rates lists every resource type priced specifically for your organization right now:

{"rates": [
  {"resource_type": "GPU_…", "label": "…", "unit": "GPU",
   "unit_hourly_price_ucredits": …, "since": "2026-09-01T00:00:00Z"}
]}

unit is GPU, CPU, or node. Burst capacity does not appear here — it bills at the clearing rate, which the price feed publishes.

A public research organization also sees its Public Research rate as one line per site that offers the program: resource_type the node's GPU type, label Public Research · <GPU model> GPU, unit GPU, unit_hourly_price_ucredits the flat rate a lease granted now is fixed at, and since the stamp the rate was last set. A free rate is listed at 0; a standard organization never sees the line. The MCP billing_read rates view carries the same rows.

Writes

The five writes are the console's billing actions an agent starts for the person it works for. Bodies are JSON; every amount is a whole number of dollars.

curl -X POST -H "Authorization: Bearer $NC_TOKEN" \
  -H "Content-Type: application/json" -d '{"usd": 250}' \
  https://nationalcompute.com/api/billing/checkout
{"url": "https://checkout.stripe.com/c/pay/…"}
Route Body What comes back
POST /api/billing/checkout {"usd": 250} a hosted payment page URL ($1 = 1 credit). Hand it to a person; the page never offers the organization's saved card, the person enters a card, so the URL alone cannot pay. The deposit lands on the ledger when they pay, so read the summary again afterwards. Card payments are capped at card_max_usd total per rolling card_window_days days per organization. card_window_remaining_usd on the summary is what a card can still carry right now. Larger amounts go by wire transfer
POST /api/billing/setup none a hosted page URL that saves a card and charges nothing. The on ramp for auto reload
POST /api/billing/portal none the payment portal URL: receipts and card changes. Before the first purchase there is no customer record to manage
PUT /api/billing/limit {"usd": 500} {"ok": true, "spend_limit_ucredits": 500000000}. Sets the monthly spend limit where none exists, or lowers the one in force
PUT /api/billing/reload {"enabled": true, "threshold_usd": 50, "amount_usd": 150} {"ok": true, "enabled": true, "threshold_ucredits": 50000000, "amount_ucredits": 150000000}: the setting as stored. Tightens the auto reload setting: lowers amount_usd, raises threshold_usd, or disarms it with {"enabled": false} (nothing else needed; the stored amounts stay for the next enable). A write that changes nothing is a no op

The spend limit is the cap a person sets on the month's charges. Read spend_limit_enforced on the summary before relying on it. When it is true, reaching the limit holds capacity until the limit is raised or the month rolls over. When it is false, the platform records the limit and does not act on it for your organization's clusters. The flag differs between organizations: read it, never assume it. A token may only tighten the limit: raising the limit, or clearing it with null, is refused 403 session-required and stays a console action. The hosted page URLs are short lived; request a fresh one if it expired before the person opened it.

Auto reload is a standing spending authorization: every charge it fires runs off session with no further approval and counts against the card cap. A token may only tighten it, the spend limit's posture: disable it, lower amount_usd, or raise threshold_usd. Enabling it, raising the amount, or lowering the threshold is refused 403 session-required and stays a console action for a person. An update sends enabled: true with both amounts (threshold_usd 1 to 1000000, amount_usd within min_usd and max_usd; the bounds apply to every write, a disable included). A failed charge pauses attempts for 24 hours, or until a change is saved on the console, whichever comes first; the summary's auto_reload.cooldown_until names the stamp, and a no op by token never moves it. On the MCP server the same write runs in both directions, because a person approves the preview there: billing_write carries human_confirmation on it.

Errors

Status Code When
401 unauthorized missing, malformed, expired, or revoked token
403 reserved-arrangement credits are managed by the platform team for your organization; purchases are not self serve
403 session-required raising or clearing the spend limit by token, or enabling auto reload, raising its amount or lowering its threshold by token: console actions
409 card-window the card cap for the rolling window is spent (card_window_remaining_usd is 0); wait for payments to age out of the window, or pay by wire transfer
409 no-purchases the payment portal before the first purchase: nothing to manage yet
409 no-card enabling auto reload with no card on file; POST /api/billing/setup hands back the page that saves one
422 bad-request a query value or body field out of range or malformed; detail names it
422 card-cap usd above what a card can still carry this window (card_window_remaining_usd on the summary); lower the amount, or pay larger amounts by wire transfer
429 rate-limited past the token's request budget for the minute (300 by default; the vm contract states the figure); wait retry_after_s
503 purchases-unavailable self serve purchases are off
503 station-unavailable the ledger or the payment provider is unreachable; retry

Every error is the plain {"error": code} body of the API conventions.