# Credits and billing (https://hypit.ai/api-reference/billing/)

> How a call is priced, what a job reserves, and when money comes back.

An account holds exactly one balance and it is denominated in **credits**. There is no separate
dollar wallet, no per-vendor sub-balance and no prepaid pool per modality. Every image, second of
video, thousand tokens and second of audio comes out of the same number.

Current prices live on the [pricing page](/commercial/pricing) and, per model, in
`pricing.credits` on the [model card](/api-reference/models). This page describes the mechanism,
which does not change.

## How a call is priced [#how-a-call-is-priced]

1. The model's rate card gives a **base rate** for whatever it charges by — per image, per request,
   per second of video, per thousand tokens, per thousand characters, per second of audio.
2. Ratio tables multiply that: `size_ratio`, `quality_ratio`, `resolution_ratio` and `flag_ratio`
   are pure multipliers on the headline rate, so a 1080p second and a 480p second of the same model
   are the same rate scaled differently. Long-context models add a `token_tiers` ladder keyed on
   `min_prompt_tokens`.
3. The result becomes the model's **offering price**, and that price is projected onto the credit
   scale by a fixed platform constant, bent by a per-model multiplier we use for promotions and
   premium tiers.
4. Credits are quantised to four decimal places — the same grid the ledger settles on.

Step 3 is why the API publishes `pricing.credits` alongside the USD figures, and why a client must
read it rather than compute it: the per-model multiplier is deliberately not exposed, so any
arithmetic done on the dollar figures will be wrong exactly on the models where being wrong costs
money.

Routing never changes your price. We may serve the same model from any of several upstreams
depending on capacity and health, and those upstreams charge us differently — but the customer price
is fixed by the model, not by whoever happened to answer.

## Synchronous calls [#synchronous-calls]

`/v1/chat/completions`, `/v1/audio/speech` and `/v1/audio/transcriptions` are priced after the
upstream answers, from the usage it reported, and debited in a single local transaction along with
the request log. Either the whole thing commits or none of it does.

Before the call goes out, a balance check refuses anything you cannot pay for. That check does not
fail open: if the ledger cannot be read, the request is refused with `503 precheck_failed` rather
than served unmetered.

## Asynchronous jobs [#asynchronous-jobs]

Because a job's real cost is not known until it finishes, submitting one **reserves** credits from
an estimate:

```text
submit    ─►  reserve  est_credits           (balance drops immediately)
terminal  ─►  succeeded  → settle to the real figure
              failed     → refund everything taken
              queue_expired → refund everything taken
```

* **Under the estimate.** The difference is refunded.
* **Over the estimate.** The difference is charged. A final settlement is allowed to push the
  balance slightly negative — an incurred cost is always recorded — and shows up as an `overdraft`
  entry that the next grant absorbs.
* **Failed or queue-expired.** Everything actually taken comes back, in full. The job's cost columns
  go to zero while the estimate stays on the row as the audit trail of what was reversed.

That last rule covers the awkward case too: if an upstream generated something but we could not
deliver a downloadable artifact, the job fails and you are refunded. You are never charged for a
result you cannot fetch.

Refunds are returned to **the same grants the debit drew from**, never as a fresh grant. That is why
a refunded subscription credit keeps its original period expiry, and a refunded pack credit keeps its
lack of one.

Each of the three money movements on a job — the reservation, the adjustment, the refund — is
guarded by an idempotency key, so a retried poll or a duplicated upstream callback cannot charge or
refund twice.

## Where credits come from, and the order they are spent [#where-credits-come-from-and-the-order-they-are-spent]

Two sources:

* a **subscription** grant, refreshed each billing period and expiring with it;
* **credit packs**, bought as a one-off, which do not expire.

Spending draws down the **subscription grant first**, then packs. That ordering is deliberate: it
spends the credits that would otherwise lapse before the ones that keep.

## No overdraft on the way in [#no-overdraft-on-the-way-in]

There is no "spend now, settle later". The moment your balance cannot cover the next call, that call
does not happen:

```json
{
  "error": {
    "message": "this call costs about 41.6 credits and you have 3.2; top up or upgrade your plan to continue",
    "type": "insufficient_quota",
    "code": "insufficient_credits",
    "param": null
  }
}
```

HTTP `402`. On an asynchronous submit the same check runs before the job is created, so a `402`
there means nothing was reserved and nothing was queued.

<Callout type="info" title="Estimates are conservative on purpose">
  A job's reservation is quoted from what the request *could* cost — including axes the request body
  cannot express, like a model that generates an audio track by default and doubles its per-second
  rate. A conservative estimate refused up front is recoverable; an upstream bill for a clip the
  customer could not afford is not.
</Callout>

## Reading your balance [#reading-your-balance]

Balance, ledger and usage are **account** endpoints, not API endpoints. They live under
`/api/hub/billing/*` and require a signed-in browser session — an `sk-hh-` key is deliberately not
enough to read or spend an account's money outside `/v1`.

Open the account dialog on [hypit.ai](https://hypit.ai) for:

* **Credits** — current balance, split into subscription, pack and overdraft, plus the plan and when
  it renews;
* **Billing records** — the ledger, one row per grant, debit, adjustment, refund and expiry;
* **Usage** — credits and request counts rolled up by day, by model and by modality.

Usage windows are computed in UTC+8, which is the billing day this product uses everywhere.

<Callout type="warn" title="There are no cost headers on `/v1` responses">
  If you are used to `x-ratelimit-remaining-*` or a per-response cost header, there is none here. A
  call's consumption is visible in the response body's `usage` object where the upstream reports one,
  and authoritatively in the account dialog's usage view. The only headers we add are `X-Request-Id`
  and, on a refusal, `Retry-After`.
</Callout>

## Free models [#free-models]

A model card may carry `"free": true` in its pricing, in which case both the dollar figure and the
credit figure are zero. A free model still requires a non-empty balance to call — the gate asks
"is there anything left at all" — and still counts against your rate and concurrency limits.