Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch
REST API

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.

Available on Nodaro Cloud

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

MethodPathWhat it does
GET/v1/credits/balanceYour balance: total, subscription and top-up credits, and your tier.
GET/v1/user/creditsA fuller balance record, with daily spending.
GET/v1/credits/transactionsYour credit history, in pages.
POST/v1/credits/model-costsThe credit price of up to 50 model ids.
POST/v1/credits/video-pro-estimateThe price of a Generate Video Pro run. See Jobs.
GET/v1/billing/surfacePublic. How this deployment meters usage.
GET/v1/billing/accountYour account summary from the deployment's billing.
POST/v1/jobs/cost-summaryThe 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 -s https://app.nodaro.ai/v1/credits/balance \
  -H "Authorization: Bearer $NODARO_API_KEY"
{ "total": 1250, "subscription": 1000, "topup": 250, "tier": "pro", "effectiveTier": "pro" }
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:

FieldMeaning
total, subscription, topupYour credits, as above.
dailySpentCredits spent today.
dailyLimitYour daily spending cap, or null for no cap.
monthlyAllocationCredits your plan grants each billing cycle.
tier, effectiveTierYour stored tier and the tier enforced.
featuresThe features of your tier.
periodEndWhen the billing period ends.
appCreditsAllowanceCredits 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.

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.

Transaction history

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

QueryMeaning
limitFrom 1 to 50. The default is 20.
cursorThe nextCursor of the previous page. It is the created_at time of the last row.
curl -s "https://app.nodaro.ai/v1/credits/transactions?limit=2" \
  -H "Authorization: Bearer $NODARO_API_KEY"
{
  "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"
}
FieldMeaning
credits_usedCredits of this entry.
action, providerWhat ran, and on which model.
statusreserved, committed or refunded.
payeruser for your own balance, workspace for a class or team budget.
workspaceIdThe paying workspace, or null.
metadataHow 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

ToolWhat it prices
POST /v1/credits/model-costsUp to 50 model ids at once.
GET /v1/modelsEvery model, with a pricing entry per variant. See Discover models.
GET /v1/nodes/:typeA node's creditCost, a single price or a range.
GET /v1/api/schemaA whole workflow, as estimatedCredits. See Workflows.
POST /v1/credits/video-pro-estimateA Generate Video Pro run, without reserving anything. See Jobs.
POST /v1/recast/estimateA Recast run. See Recast.
POST /v1/pro-3d-render/quoteA 3D Render Pro run. See 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 -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"]}'
{
  "data": { "nano-banana-pro": 45, "nano-banana-pro:4K": 60, "gpt-image-2:2K": 30 },
  "missing": [],
  "errors": []
}
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.

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.

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:

{
  "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:

FieldMeaning
planYour plan, as a string. unknown is a real answer.
balanceYour balance, or null.
dailyAllowanceYour daily allowance, or null.
unitThe 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.
  • 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 for deployments that authorize spending through their own wallet.

Frequently asked questions

Last updated on

On this page