# Credits

> Read your Nodaro credit balance and history from the API, price models and runs before you start them, and see how reservations, refunds and pay-as-you-go work.

Source: https://nodaro.ai/docs/developers/api/credits

**Credits** are the unit you pay with on Nodaro Cloud, and the API always reports them in credits, never in money. The credit endpoints return your balance and your credit history, price models and runs before you start them, and tell a client whether the deployment it talks to meters usage at all.

The credit endpoints exist only on Nodaro Cloud. The Community and Business editions have no credit system, so these routes answer `404` there. `GET /v1/billing/surface` is the exception: it answers on every edition.

## Endpoints

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/v1/credits/balance` | Your balance: total, subscription and top-up credits, and your tier. |
| `GET` | `/v1/user/credits` | A fuller balance record, with daily spending. |
| `GET` | `/v1/credits/transactions` | Your credit history, in pages. |
| `POST` | `/v1/credits/model-costs` | The credit price of up to 50 model ids. |
| `POST` | `/v1/credits/video-pro-estimate` | The price of a Generate Video Pro run. See [Jobs](https://nodaro.ai/docs/developers/api/jobs#estimate-a-run). |
| `GET` | `/v1/billing/surface` | Public. How this deployment meters usage. |
| `GET` | `/v1/billing/account` | Your account summary from the deployment's billing. |
| `POST` | `/v1/jobs/cost-summary` | The credits of a batch of jobs. |

These routes take the same tokens as every other route: a personal API token, an OAuth token or a session JWT.

## Read your balance

**curl**

```bash
curl -s https://app.nodaro.ai/v1/credits/balance \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{ "total": 1250, "subscription": 1000, "topup": 250, "tier": "pro", "effectiveTier": "pro" }
```

**TypeScript SDK**

```ts
const balance = await client.credits.balance()
console.log(`${balance.total} credits (${balance.effectiveTier})`)
```

`total` is `subscription` plus `topup`. `tier` is the stored subscription tier, such as `free` or `pro`. `effectiveTier` is the tier Nodaro actually enforces: `payg` means no subscription but purchased credits, with every model unlocked, no watermark and no daily cap.

`GET /v1/user/credits`, which the SDK's `client.credits.balance()` reads, returns more:

| Field | Meaning |
| --- | --- |
| `total`, `subscription`, `topup` | Your credits, as above. |
| `dailySpent` | Credits spent today. |
| `dailyLimit` | Your daily spending cap, or `null` for no cap. |
| `monthlyAllocation` | Credits your plan grants each billing cycle. |
| `tier`, `effectiveTier` | Your stored tier and the tier enforced. |
| `features` | The features of your tier. |
| `periodEnd` | When the billing period ends. |
| `appCreditsAllowance` | Credits earned from app usage, on the free tier only. |

Spending uses subscription credits first. Subscription credits reset every billing cycle, and top-up credits stay valid for 12 months from purchase.

## How credits are charged

- **Reserved when a job starts.** A generation reserves its price before it runs. When your balance cannot cover it, the call answers `402 insufficient_credits` with `required` and, on most accounts, `balance`. A workflow run needs enough credits for its worst-case cost.
- **Charged when it delivers.** The reservation becomes a charge.
- **Refunded when it does not.** A generation blocked by a model's safety filter or by a deployment's policy is always refunded, and so are cancelled jobs.
- **Priced on what actually runs.** When the server corrects a setting for the chosen model, the reservation follows the corrected value. See [Parameter corrections](https://nodaro.ai/docs/developers/api/workflows#parameter-corrections).

A job's `credit_status` and a transaction's `status` follow the same steps: `reserved`, then `committed` or `refunded`. So you can tell from the job itself whether its credits came back. See [Credits of a job](https://nodaro.ai/docs/developers/api/jobs#credits-of-a-job).

## Transaction history

`GET /v1/credits/transactions` returns your credit history, newest first:

| Query | Meaning |
| --- | --- |
| `limit` | From 1 to 50. The default is 20. |
| `cursor` | The `nextCursor` of the previous page. It is the `created_at` time of the last row. |

```bash
curl -s "https://app.nodaro.ai/v1/credits/transactions?limit=2" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{
"data": [
{
"id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"created_at": "2026-09-26T14:03:11.284Z",
"credits_used": 45,
"action": "generate-image",
"provider": "nano-banana-pro",
"status": "committed",
"metadata": { "model": "nano-banana-pro", "from_sub": 45, "from_topup": 0 },
"payer": "user",
"workspaceId": null
}
],
"nextCursor": "2026-09-26T14:03:11.284Z"
}
```

| Field | Meaning |
| --- | --- |
| `credits_used` | Credits of this entry. |
| `action`, `provider` | What ran, and on which model. |
| `status` | `reserved`, `committed` or `refunded`. |
| `payer` | `user` for your own balance, `workspace` for a class or team budget. |
| `workspaceId` | The paying workspace, or `null`. |
| `metadata` | How the run was billed. Always an object, `{}` when nothing applies. |

`metadata` holds only these keys, when they apply: `model`, `from_sub` and `from_topup` (which of your balances paid), `is_app_run`, `allowance_delta`, `web_free_mode`, `status`, `loop_trim_refunded` and `surround_refine_refunded`.

`nextCursor` is `null` when there are no more rows.

## Price a run before you start it

| Tool | What it prices |
| --- | --- |
| `POST /v1/credits/model-costs` | Up to 50 model ids at once. |
| `GET /v1/models` | Every model, with a `pricing` entry per variant. See [Discover models](https://nodaro.ai/docs/developers/api/nodes#discover-models). |
| `GET /v1/nodes/:type` | A node's `creditCost`, a single price or a range. |
| `GET /v1/api/schema` | A whole workflow, as `estimatedCredits`. See [Workflows](https://nodaro.ai/docs/developers/api/workflows#run-a-workflow-with-new-input-values). |
| `POST /v1/credits/video-pro-estimate` | A Generate Video Pro run, without reserving anything. See [Jobs](https://nodaro.ai/docs/developers/api/jobs#estimate-a-run). |
| `POST /v1/recast/estimate` | A Recast run. See [Recast](https://nodaro.ai/docs/developers/api/recast). |
| `POST /v1/pro-3d-render/quote` | A 3D Render Pro run. See [3D scenes](https://nodaro.ai/docs/developers/api/3d-scenes). |

A model's price can depend on its settings, so the ids of the variants carry them, such as `nano-banana-pro:4K` or `gpt-image-2:2K`:

**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/credits/model-costs \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"models": ["nano-banana-pro", "nano-banana-pro:4K", "gpt-image-2:2K"]}'
```

```json
{
"data": { "nano-banana-pro": 45, "nano-banana-pro:4K": 60, "gpt-image-2:2K": 30 },
"missing": [],
"errors": []
}
```

**TypeScript SDK**

```ts
const { data, missing } = await client.credits.modelCosts(['nano-banana-pro', 'nano-banana-pro:4K'])
console.log(data['nano-banana-pro:4K'])
if (missing.length) console.warn('No price for:', missing)
```

An id without a price lands in `missing`, and an id whose lookup failed lands in `errors`, so one bad id never fails the whole request. Show a dash for those, not zero. The prices above are an example: read the live answer, or see each model's page in [Models](https://nodaro.ai/docs/models).

The CLI has no credit commands, but `nodaro models list` shows each model's credit tiers.

## Pay-as-you-go

You do not need a subscription to use the API.

- **Any purchase activates it.** Buying a credit pack, or loading any whole-dollar amount from $5 to $1,000 on the Billing page, switches the account to pay-as-you-go. Larger loads get a better rate per credit.
- **Everything unlocked.** `effectiveTier` becomes `payg`: every model is available, results have no watermark, and there is no daily spending cap.
- **Credits last 12 months** from purchase.
- **For the developer surfaces.** Pay-as-you-go credits work through the API, the SDK, the CLI and MCP. The web editor needs a subscription, and a pay-as-you-go account that spends from it gets `403 subscription_required`. Token calls never get that error.
- **Subscriptions cost less per credit** at a steady volume.

Two things to know:

- **Results are public by default.** Private results are a subscription feature, from the Standard plan up, so pay-as-you-go results appear in the public gallery. Jobs created through MCP are always private.
- **Media is kept while the account is active.** After about 3 months with no purchase and no credit spending, files older than 60 days may be removed. Spending credits again stops the clean-up.

Purchases themselves happen in the web app: the `/v1/billing/*` routes for checkout, loads, auto-recharge, purchase history and the payment portal refuse API and OAuth tokens. Manage billing at [app.nodaro.ai/billing](https://app.nodaro.ai/billing).

## Read how a deployment meters usage

Two routes let a client build cost and usage views without assuming how the deployment bills:

**`GET /v1/billing/surface`** needs no token and returns the same answer to everyone:

```json
{
"data": {
"contract": 2,
"providerId": "…",
"displayUnit": "credits",
"canReport": true,
"canQuote": true,
"canAccount": true,
"mountCostTab": true,
"deploymentPayer": false
}
}
```

- `displayUnit` is the unit a cost view should show by default, such as `credits` or `usd`.
- On a Community install without credits, `providerId` is `none` and `mountCostTab` is `false`: show no cost view.
- `deploymentPayer` is `true` when one billing account pays for every user. Which account that is is never shown.

**`GET /v1/billing/account`** returns your account summary as `{ "data": … }`. When the billing service cannot answer, `data` is `null`: show that as unavailable, never as a zero balance. The summary always has:

| Field | Meaning |
| --- | --- |
| `plan` | Your plan, as a string. `unknown` is a real answer. |
| `balance` | Your balance, or `null`. |
| `dailyAllowance` | Your daily allowance, or `null`. |
| `unit` | The unit of these figures. |

A deployment may add optional fields, and a client shows only the ones it receives: `periodStart`, `generations`, `spent`, `payg`, `daily`, `reserveValue` and `byCategory`. Money figures are `{ amount, currency }` objects. In `daily`, a `limit` of `0` means blocked, not unlimited, and `daily` wins over `dailyAllowance` when both are present. Every `null` means unavailable, never zero: show a dash.

**`POST /v1/jobs/cost-summary`** totals the credits of a batch of jobs. `total_credits`, at the top and on each breakdown row, is a number or `null`, and the response names its `unit` and counts the jobs it could not price in `unavailable`. A `null` total means no job in the batch had a known charge. It does not mean zero.

## Workspaces and shared billing

- **Workspace budgets.** Work done inside an organization's workspace is paid by the workspace's budget, not your balance, and its transactions carry `payer: "workspace"`. A run over the budget answers `402 budget_exceeded`, and over your own cap in that workspace `402 member_cap_exceeded`. See [Workspaces and organizations](https://nodaro.ai/docs/developers/api/workspaces#budgets-and-usage).
- **Deployments with one billing account.** On a deployment where one billing account pays for every user, `GET /v1/user/credits` adds `allowance: { granted, remaining, enforced }`, your own allowance in credits. `enforced: false` means it is shown but does not stop runs. `allowance` is `null` for the billing account itself or when it could not be read, so never read `null` as zero. When the deployment enforces allowances, a run over yours answers `402 user_allowance_exceeded`. The billing account manages the pool from its own browser session only: its balance reads with a token answer `403 payer_balance_jwt_only`. See [External wallet](https://nodaro.ai/docs/developers/external-wallet) for deployments that authorize spending through their own wallet.

## Frequently asked questions

### How do I check my credit balance through the API?

Send GET /v1/credits/balance. It returns total, subscription and topup credits, plus your tier and effectiveTier. In the SDK, client.credits.balance() returns a fuller record that also includes your daily spending.

### When are credits charged for an API generation?

Credits are reserved when the job starts and charged when it delivers. When a job is blocked by a safety filter or a policy, or cancelled before it runs, the reservation is refunded. The job's credit_status shows which happened.

### Can I use the Nodaro API without a subscription?

Yes. Buying any credit pack activates pay-as-you-go, with every model unlocked, no watermark and no daily cap. Pay-as-you-go credits work through the API, the SDK, the CLI and MCP, and stay valid for 12 months.

### How do I know what a run will cost before I start it?

POST /v1/credits/model-costs returns the credit price of up to 50 model ids, GET /v1/models lists every model's price tiers, and GET /v1/api/schema estimates a whole workflow. Generate Video Pro has its own estimate route.

### Do the credit endpoints work on a self-hosted install?

No. The Community and Business editions have no credit system, so the credit routes answer 404 there, and node and model descriptors leave out their credit prices. You pay the model providers directly.
