Model APIs

Pricing and credits

On this page
  1. Per-model pricing
  2. What you pay for
    1. Generations
    2. Agent turns and Studio renders
  3. What you are not charged for
  4. Checking prices programmatically
    1. Published model prices
    2. Quote the exact request
  5. Balances and the ledger
    1. Spendable balances
    2. Itemized transactions
    3. Usage totals
  6. Plans, top-ups and codes
  7. Organizations

Price a generation before submitting it, show the customer that price, and follow its hold through to a charge or refund. Published model prices, quotes and the itemized ledger all use credits.

Credits reserved and settled as work completes
Credits reserved and settled as work completes

Per-model pricing #

Each model publishes its billing unit. Read the current catalog rather than keeping a second price list in your application.

Modality Billing unit How the charge is calculated
Image per_image The selected model and quality-tier price for each image.
Video per_clip The published price assumes baseline_seconds, normally 5 seconds. Charge ceil(credits × duration_seconds / baseline_seconds) for the requested duration.
Text to speech per_character The headline credits compares a 1,000-character script. Actual charge is max(minimum_credits, ceil(characters / characters_per_credit)); the minimum bills a short line as 200 characters. Count Unicode characters, not bytes.
3D and other flat-rate generation per_generation One published rate per generation; inspect the selected model's cost.unit.
Image Video Audio 3D
48 74 17 2

See Model APIs for the full per-model tables. A missing cost means the price is not published; display an unknown price, not zero. Nolgia does not expose a GPU-time billing fallback.

What you pay for #

Generations #

Nolgia takes a credit hold when the request is accepted. Delivery consumes that hold; it does not create a second charge. A failed job normally releases the hold, subject to the policy-refusal exception below.

Setting Effect on credits
Image count Each requested image contributes its model price.
Video duration Scales the selected per-clip price by duration_seconds / baseline_seconds, rounded up.
Speech text Uses the Unicode character count and the published minimum.
quality Selects the complete per-tier price from quality.options[].credits; do not add that value to the base price. Video tiers use the same clip baseline.
render_quality GPT Image 2.5's xhigh and max add the published credits per image, independently of the native/2k/4k quality tier. auto, low, medium and high do not add credits.
generate_audio Where audio_surcharge is published, the default price includes it. generate_audio=false subtracts it before duration scaling.

Agent turns and Studio renders #

Work Billing behavior
Agent turn A separate agent_turn ledger row. A turn charges at least the published per-turn rate; a more expensive turn uses max(flat_rate(model), ceil(provider_cost_usd / 0.01)). Its row supplies tokens and provider_cost_usd when those explain a charge above the flat rate.
Generation started by an agent Its own generation hold and ledger row, separate from the turn that requested it.
Studio render The ledger reserves a render kind, but composition renders currently take no credit hold. Generating the source images, clips or narration is billed separately.
Credits: hold, then settle On submit a hold is placed on the wallet for the quoted credits. While the job runs the hold stays. On success the hold is consumed and credits_refunded is false. If the job failed and the provider did not bill, the hold is released and credits_refunded is true. A provider refusal that was billed is charged. wallet balance submit: hold placed for the quoted credits job runs succeeded: hold consumed credits_refunded: false failed, provider did not bill: hold released credits_refunded: true provider refusal that was billed: charged submit run settle
Credits move from available to held, then charged or refunded

What you are not charged for #

Outcome What happens to credits
Ordinary failed generation The hold is released; check failure.credits_refunded.
409 duplicate submission No new hold and no second charge. Follow the existing job_id.
408 request or wait timeout The timeout response itself adds no charge. A wait timeout does not cancel or refund the accepted job; that job can still finish and settle its existing hold.
Quote with POST /jobs/cost No job, reservation or charge.
Checking a job's status or waiting again No additional generation charge.

Checking prices programmatically #

Published model prices #

Property Value
Endpoint GET /pricing/models
Authentication Public; no token required
Response ModelPricingList, with the same prices as GET /models
Caching Cache-Control: public, max-age=300
shellRead published prices
curl --fail-with-body -sS https://api.nolgia.ai/v1/pricing/models

This trimmed flux-pro entry preserves the real cost and quality values from the production model fixture. The public response wraps entries in models; other models and other pricing fields are omitted here.

JSONPublished price excerpt
{
  "models": [
    {
      "id": "flux-pro",
      "modality": "image",
      "min_tier": "starter",
      "cost": { "credits": 4, "unit": "per_image" },
      "quality": {
        "default": "native",
        "options": [
          { "credits": 4, "id": "native", "premium": false },
          { "credits": 17, "id": "2k", "premium": true },
          { "credits": 54, "id": "4k", "premium": true }
        ]
      }
    }
  ]
}
Field Type Required Description
models array of ModelPricing Yes
Field Type Required Description
id string Yes The catalog id a generation request sends as model.
display_name string Yes What to call this model on a customer-facing surface. Always present: no published model is nameless, because a raw id is sometimes a provider route path and must never be rendered.
maker string No The company that made the model (Google, Kuaishou, Black Forest Labs).…
summary string No One sentence about the model. Absent until written; copy arrives incrementally, and an absent summary renders as nothing.
recommended boolean No Mirrors Model.recommended — the pick for its modality.
video VideoCapabilities No
audio AudioCapabilities No
image ImageCapabilities No
references ReferenceCapabilities No
restore boolean No Mirrors Model.restore.
remove_background boolean No Mirrors Model.remove_background.
image_enhance boolean No Mirrors Model.image_enhance.
image_expand boolean No Mirrors Model.image_expand.
modality Modality Yes
min_tier SubscriptionTier Yes Minimum subscription plan required to run this model; starter means every plan can.…
cost ModelCost No
quality QualityCapabilities No
three_d ThreeDCapabilities No
aspect_ratios array of string Yes The output aspect ratios this model renders (video.aspect_ratios for video models, image.aspect_ratios for image models), as plain strings so one field spans both enums.…
render_quality RenderQualityCapabilities No Mirrors image.render_quality from GET /models.…
inpaint_mask boolean No Mirrors image.inpaint_mask from GET /models: this model can edit only PART of an image, guided by a mask.…
capabilities array of VideoCapabilityTag No Mirrors Model.capabilities: the video capability CLASSES this model exists to serve, as opposed to the individual inputs it accepts.…
Field Type Required Description
credits integer Yes Credits charged per unit at the model's DEFAULT audio state.…
unit ModelCostUnit Yes
audio_surcharge integer, nullable No Extra credits (per the model's unit) that a soundtrack adds, when the provider charges more to render audio.…
video_input_credits integer, nullable No credits for a request that carries a reference video, at the model's default tier, when the provider bills a video input at a different rate.…
input_image_credits integer, nullable No Extra credits for each input image past free_input_images, on a model that charges for input images.…
free_input_images integer, nullable No Present with input_image_credits: how many input images a request carries before the surcharge starts.
baseline_seconds integer, nullable No Present only when unit is per_clip. The clip duration in seconds that credits assumes. The actual charge scales with the requested duration as ceil(credits * duration_seconds / baseline_seconds).
characters_per_credit integer, nullable No Present only when unit is per_character.…
minimum_credits integer, nullable No Present only when unit is per_character.…
Field Type Required Description
id string Yes Tier identifier accepted by the quality request parameter.
credits integer Yes Credits charged per the model's cost.unit when this tier is selected (for video, per baseline_seconds clip), at the model's DEFAULT audio state.…
audio_surcharge integer, nullable No Extra credits this tier's soundtrack adds when the provider charges more for audio.…
video_input_credits integer, nullable No Credits per baseline_seconds clip at this tier when the request carries a reference video (video_asset_ids / video_urls), for a model whose provider bills a video input at a different rate than a text or image render.…
premium boolean Yes Marks a premium (highest-quality, higher credit cost) tier so clients can present it distinctly from standard options.
aspect_ratios array of string No When present, the tier renders at these aspect ratios ONLY (a subset of the model's video.aspect_ratios); a request pairing the tier with any other aspect_ratio is refused with a 400 before any credits are held.…

Quote the exact request #

Property Value
Endpoint POST /jobs/cost
Input kind and the matching generation body, such as image
Result The exact credits the same settings will hold, plus a confirmation token you may return on submit
Re-quote when The request changes or expires_at passes

These examples use the generated clients. The CLI currently has no dedicated quote command; its tab uses curl with the same NOLGIA_TOKEN. The response below is a production capture, not a claim that each language example was run live.

shellQuote an image example
curl --fail-with-body -sS https://api.nolgia.ai/v1/jobs/cost \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"image","image":{"model":"flux-pro","prompt":"a paper-cut mountain range at dawn"}}'
shellQuote from the terminal example
# No dedicated nolgia quote command; call the quote endpoint directly.
curl --fail-with-body -sS https://api.nolgia.ai/v1/jobs/cost \
  -H "Authorization: Bearer $NOLGIA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"image","image":{"model":"flux-pro","prompt":"a paper-cut mountain range at dawn"}}'
TypeScriptQuote an image example
import { createNolgiaClient } from "@nolgia/sdk";

const nolgia = createNolgiaClient(process.env.NOLGIA_TOKEN!);
const { data: quote, error } = await nolgia.POST("/jobs/cost", {
  body: {
    kind: "image",
    image: { model: "flux-pro", prompt: "a paper-cut mountain range at dawn", num_images: 1 },
  },
});
if (error) throw new Error(`${error.title}: ${error.detail ?? ""}`);
console.log(quote.credits, quote.expires_at);
PythonQuote an image example
import os
from nolgia import AuthenticatedClient
from nolgia.api.jobs import quote_job_cost
from nolgia.models import GenerateImageRequest, ImageModel, JobCostKind, JobCostQuote, JobCostRequest

client = AuthenticatedClient(base_url="https://api.nolgia.ai/v1", token=os.environ["NOLGIA_TOKEN"])
quote = quote_job_cost.sync(client=client, body=JobCostRequest(
    kind=JobCostKind("image"),
    image=GenerateImageRequest(model=ImageModel("flux-pro"), prompt="a paper-cut mountain range at dawn"),
))
if not isinstance(quote, JobCostQuote):
    raise SystemExit(f"refused: {quote}")
print(quote.credits_, quote.expires_at)
RustQuote an image example
use nolgia_client::{types, ClientBuilder};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = ClientBuilder::new("https://api.nolgia.ai/v1")
        .bearer_token(std::env::var("NOLGIA_TOKEN")?)
        .build()?;
    let prompt: types::GenerateImageRequestPrompt = "a paper-cut mountain range at dawn".parse()?;
    let image: types::GenerateImageRequest = types::GenerateImageRequest::builder()
        .model("flux-pro").prompt(Some(prompt)).num_images(1u64).try_into()?;
    let quote = client.quote_job_cost()
        .body_map(|b| b.kind(types::JobCostKind::Image).image(Some(image)))
        .send().await?.into_inner();
    println!("{} {}", quote.credits, quote.expires_at);
    Ok(())
}

The real cost-image.json capture quotes one native flux-pro image. Its prompt differs from the reusable example above, and its opaque confirmation token is redacted.

JSON200 quote response
{
  "balance_credits": 2573,
  "basis": "one_generation",
  "confirmation_token": "…",
  "credits": 4,
  "expires_at": "2026-09-21T03:43:14.171728407Z",
  "kind": "image",
  "model": "flux-pro",
  "settings": [ { "label": "Model", "value": "flux-pro" } ],
  "sufficient_credits": true
}
Field Type Required Description
kind JobCostKind Yes
image GenerateImageRequest No
video GenerateVideoRequest No
audio GenerateAudioRequest No
three_d Generate3DRequest No
set GenerateSetRequest No
Field Type Required Description
kind JobCostKind Yes
model string Yes The model id the quote is for, after defaulting — not necessarily the one you sent.
credits integer Yes Display credits. This is the number the submit will hold, computed by the same pricer the submit path uses, for exactly these settings. It is what to show the customer.
basis one of one_generation, set_run Yes Whether credits covers one generation or a whole set run.
members integer, nullable No Number of set members credits covers. Present only when basis is set_run.
duration_seconds integer, nullable No The billed duration the price is for, on a video quote.
quality string, nullable No The resolved quality tier the price is for, when the model has one.
settings array of JobCostSetting Yes The settings that made this price, in the order a dialog should show them. Human-readable; do not parse it.
balance_credits integer, nullable No The wallet balance this quote was compared against, when one could be read.
sufficient_credits boolean Yes Whether the balance covers credits right now. Advisory — the submit re-checks.
confirmation_token string Yes Short-lived, single-request proof that this price was quoted. Send it back as confirmation_token on the matching generate request. Opaque: do not parse or construct it.
expires_at string Yes After this the token is refused and a submit carrying it fails with confirmation_rejected. Re-quote.
Quote, confirm, submit Your app asks the Nolgia API for a cost quote, shows the price to the customer, and on confirmation submits the generation with the confirmation token. The same pricer holds the same credits. A declined quote submits nothing, and a stale or mismatched token is rejected with 422. Your app Nolgia API POST /jobs/cost show the price, customer confirms POST /generate/* with confirmation_token quote: credits, settings summary, confirmation_token, expires_at customer declines: confirmation_rejected, nothing submitted same pricer, same credits, credits held stale or mismatched token: 422 confirmation_rejected
Confirmation gate: quote the request, show the price, and submit with its token

Return confirmation_token on the unchanged generation body if you want submission bound to the quoted request and price. The token is optional; when supplied, a stale, malformed, foreign or mismatched token fails with 422 confirmation_rejected. Quote again. sufficient_credits is advisory because submission checks the balance again.

Balances and the ledger #

Spendable balances #

Personal Access Tokens you create spend available_for_api. The app, the Nolgia Agent, organization API keys and connected assistants (ChatGPT, Claude or any MCP client connected through OAuth) spend available_for_app. available_for_credential is the figure for the credential that made the request, and credential_channel names its channel. Monthly app_subscription credits expire at the subscription cycle end; shared_topup credits do not expire and are spendable by either surface.

shellRead your balance
curl --fail-with-body -sS https://api.nolgia.ai/v1/billing/credits \
  -H "Authorization: Bearer $NOLGIA_TOKEN"

This schema-built example shows a personal account with 100 subscription credits and 200 top-up credits.

JSON200 balance response example
{
  "user_id": "00000000-0000-4000-8000-000000000001",
  "scope": "personal",
  "app_subscription": 100,
  "shared_topup": 200,
  "total": 300,
  "available_for_app": 300,
  "available_for_api": 200,
  "buckets": [
    { "wallet_id": "00000000-0000-4000-8000-000000000002", "type": "app_subscription", "balance": 100, "expires_at": "2026-10-01T00:00:00Z" },
    { "wallet_id": "00000000-0000-4000-8000-000000000003", "type": "shared_topup", "balance": 200, "expires_at": null }
  ]
}
Field Type Required Description
user_id string Yes
app_subscription integer Yes Monthly app-only credits that expire at the subscription cycle end.
shared_topup integer Yes Shared app/API prepaid credits that do not expire.
total integer Yes
available_for_app integer Yes App-visible credits; subscription bucket plus shared top-up overflow.
available_for_api integer Yes API prepaid credits; shared top-up only at launch.
credential_channel CreditChannel No
available_for_credential integer No What the credential that made this request can spend: available_for_app on the app channel, available_for_api on the API channel (see credential_channel).…
buckets array of CreditBalanceBucket Yes
scope BillingScope No
organization_id string No Set when scope is organization: the balances above are the ORGANIZATION's shared pool (the caller's personal wallets are not consulted inside an organization) and user_id is the caller.
seats integer No Seats on the organization's subscription; organization scope only.
seat_limit integer, nullable No The organization's seat cap (null = unlimited); organization scope only.
member_budget integer, nullable No The caller's monthly credit budget in the organization (null = unlimited); organization scope only.
member_spent_this_month integer No What the caller has spent from the organization's pool this UTC calendar month (held plus consumed reservations); organization scope only.
Field Type Required Description
wallet_id string Yes
type CreditWalletType Yes
balance integer Yes
expires_at string, nullable No

Itemized transactions #

GET /billing/transactions returns newest-first rows. The debit when a hold is taken is the charge; completion writes no second debit. Releasing it writes a refund for the same amount. balance_after belongs to the particular wallet_id, not to the combined account balance.

shellRead the ledger
curl --fail-with-body -sS 'https://api.nolgia.ai/v1/billing/transactions?limit=20' \
  -H "Authorization: Bearer $NOLGIA_TOKEN"

This schema-built example uses the documented Quick Start amounts: a 12-credit veo-3.1-lite four-second charge and its 12-credit refund. The row ids, timestamps and wallet balances are illustrative; this is not a captured ledger response.

JSON200 ledger response example
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000005",
      "occurred_at": "2026-09-21T03:36:00Z",
      "kind": "refund",
      "wallet": "topup",
      "wallet_id": "00000000-0000-4000-8000-000000000003",
      "credits": 12,
      "balance_after": 200,
      "description": "Refund · Video generation · veo-3.1-lite · 4 s",
      "model": "veo-3.1-lite",
      "job_id": "00000000-0000-4000-8000-000000000006"
    },
    {
      "id": "00000000-0000-4000-8000-000000000004",
      "occurred_at": "2026-09-21T03:35:00Z",
      "kind": "generation",
      "wallet": "topup",
      "wallet_id": "00000000-0000-4000-8000-000000000003",
      "credits": -12,
      "balance_after": 188,
      "description": "Video generation · veo-3.1-lite · 4 s",
      "model": "veo-3.1-lite",
      "job_id": "00000000-0000-4000-8000-000000000006"
    }
  ],
  "next_cursor": null,
  "scope": "personal"
}
Parameter In Required Description
cursor query No Opaque pagination cursor returned by a prior response.
limit query No Rows per page.
from query No Inclusive window start (RFC 3339). Omit for no lower bound.
to query No Exclusive window end (RFC 3339). Omit for no upper bound.
kind query No Only rows of this kind.
wallet query No Only rows on this wallet type (default all).
member_id query No Organization context only: rows caused by this member. Owner, admin and billing roles may name any member; other roles only themselves. 400 in the personal space.
Field Type Required Description
items array of CreditTransaction Yes Rows newest first.
next_cursor string, nullable Yes Pass as cursor for the next page; null on the last page.
scope BillingScope Yes
organization_id string No Set when scope is organization.
Field Type Required Description
id string Yes
occurred_at string Yes
kind CreditTransactionKind Yes
wallet CreditTransactionWallet Yes
wallet_id string Yes
wallet_expires_at string, nullable No When the wallet's credits expire (subscription wallets); null for the top-up wallet.
credits integer Yes Signed movement. Negative for charges and debits, positive for grants, top-ups, redemptions and refunds.
balance_after integer Yes The wallet's balance after this row (running sum of the wallet identified by wallet_id).
description string Yes Plain-language label, for example Video generation · seedance-2.5 · 5 s or Refund · Agent turn. Never contains an em dash.
model string No Catalog model id when the row charges or refunds a generation, and the agent brain (for example claude-opus-5-5) when it charges or refunds an agent turn. Label it client-side.
preset string No Preset slug when the generation was launched from a preset.
job_id string No The generation job behind a generation charge or its refund.
session_id string No The agent chat session behind an agent_turn charge or its refund.
tokens integer No Tokens the agent turn behind this row reported, across every iteration of its reasoning loop.…
provider_cost_usd number No Measured provider cost in US dollars of the agent turn behind this row.…
render_id string No The render behind a render charge or its refund.
member CreditTransactionMember No
meter CreditTransactionMeter No
Kind Meaning
grant Monthly plan credits or credits granted by Nolgia.
topup A credit purchase.
redeem A credit-code redemption.
generation A generation charge.
agent_turn A chat turn charge.
render Reserved for composition-render charges; renders currently take no hold.
refund Credits returned for a prior charge.
adjustment A plan correction or other manual movement.

Usage totals #

GET /billing/transactions/summary rolls up the same rows and scope. It defaults to the current UTC month; credits_used and credits_refunded are positive totals, while by_kind[].credits is signed.

shellRead credit usage
curl --fail-with-body -sS https://api.nolgia.ai/v1/billing/transactions/summary \
  -H "Authorization: Bearer $NOLGIA_TOKEN"

This schema-built response illustrates the charge and refund above, with no other movements in the month.

JSON200 usage summary example
{
  "from": "2026-09-01T00:00:00Z",
  "to": "2026-10-01T00:00:00Z",
  "scope": "personal",
  "credits_used": 12,
  "credits_refunded": 12,
  "credits_added": 0,
  "by_day": [ { "date": "2026-09-21", "credits_used": 12, "credits_refunded": 12 } ],
  "by_kind": [
    { "kind": "generation", "credits": -12, "count": 1 },
    { "kind": "refund", "credits": 12, "count": 1 }
  ]
}
Field Type Required Description
from string Yes
to string Yes
scope BillingScope Yes
organization_id string No Set when scope is organization.
member_id string No Set when the summary is narrowed to one member's rows.
credits_used integer Yes
credits_refunded integer Yes
credits_added integer Yes Grants, top-ups and redemptions in the window.
by_day array of CreditUsageDay Yes
by_kind array of CreditUsageKindTotal Yes

Plans, top-ups and codes #

See nolgia.ai/pricing for current plans and credit purchases. Subscriptions supply monthly app credits; top-ups supply shared prepaid credits. A model's min_tier and its credit price are separate: adding credits does not unlock an out-of-plan model.

Operation Use it for
GET /billing/subscription Read the plan, status and current period end.
POST /billing/portal-link Open the returned expiring Stripe Customer Portal URL to manage billing.
POST /credits/redeem Redeem an issued code into shared top-up credits, once per account.
GET /billing/auto-refresh Read automatic top-up settings.
PUT /billing/auto-refresh Update whether auto-refresh is enabled, its threshold and purchase amount.
Method Path What it does
GET /billing/subscription Get the current user's subscription state.
GET /billing/trial The account's free trial state and whether it can start one.
POST /billing/trial Start the account's seven-day free trial.
POST /billing/trial/confirm Start the free trial from an emailed confirmation link.
POST /billing/portal-link Create a Stripe Customer Portal session and return its URL.
GET /billing/credits Get the current user's credit wallet balances.
GET /billing/transactions List the itemized credit ledger, newest first.
GET /billing/transactions/summary Credits used per day and totals per kind over a window.
GET /billing/auto-refresh Get the current user's auto-refresh settings.
PUT /billing/auto-refresh Update the current user's auto-refresh settings.
POST /credits/redeem Redeem a credit code for shared top-up credits.

A code refusal returns 404 for an unknown code, 410 for a deactivated, expired or exhausted code, 409 if this account already redeemed it, and 429 for rate-limited attempts. A repeated redemption can never grant credits twice.

Organizations #

In an organization, spending uses the organization's wallets, not your personal balance. GET /organizations/{id}/credits reports the shared pool, seats, and monthly member budgets. An unlimited budget is null; the member's month-to-date spend includes both held and consumed reservations.

Role Credit and usage visibility
Owner, admin, billing The organization pool and every member's rows.
Member, viewer The shared pool and their own member row or spend.
Non-member 404; the organization is not exposed.

GET /organizations/{id}/usage groups consumed credits by member, model or UTC day; it defaults to the current UTC calendar month and excludes held and released reservations. Usage and activity explains why these totals differ from gross ledger charges. For organization contracts, see the Enterprise option on Pricing.