Platform

Billing & quotas

How calls are metered in credit units, the free daily quota, and token-weighted AI pricing.

The two-band model

Every billable call is metered in credit units. Metering runs in two bands, evaluated per app on each request:

  1. Free daily quota. Each app gets a free allowance of credit units per UTC calendar day. Calls draw down this allowance first and cost you nothing.
  2. Prepaid wallet. Once an app has consumed its free allowance for the day, further billable calls debit the owner’s prepaid credit balance (ai_users.tokens_balance) — but only while platform billing is enabled. If billing is disabled, over-quota calls remain free; they are still metered so your usage counters stay accurate.

The wallet debit is the single hard gate. Everything else about metering — advancing the daily counter, recording telemetry — is best-effort and never fails your request. If the wallet cannot cover a billable call, the request is rejected with 402 insufficient_credits and no work is performed.

Credit units are the platform’s internal accounting unit, shared with the CLI and chat billing. They are not dollars — how many units a credit dollar buys, and how the wallet is topped up, is a platform (staff) decision. This page documents how calls consume units.

The free daily quota

The free allowance is a number of credit units per app per UTC day. It defaults to 10,000 units/day (DEVELOPER_API_FREE_QUOTA_UNITS) and can be overridden per app on developer_apps.free_quota_override. A value of 0 means unlimited — the app is never billed and never leaves the free band.

Consumption is tracked in a durable daily counter keyed by app id and UTC date. Because a new date produces a new counter row, the quota resets at UTC midnight by construction — there is no scheduled job to wait on. At 00:00 UTC your used_units_today returns to zero.

An app is considered over quota once its consumed units for the day reach the allowance. Admission is read before the counter is incremented for the current call, so a concurrent burst can overshoot the allowance by the number of in-flight requests. This is an accepted characteristic of v1, not a bug: the overshoot is bounded by your concurrency and is never larger than one call’s worth of units per in-flight request.

Token-metered AI calls

Chat completions are priced by tokens, not per call, because a coding turn can range from a few hundred to tens of thousands of tokens. The credit cost is computed from the prompt and completion token counts using a weighted formula.

The weighted-unit formula

Weighted credit units for one AI call are:

units = max(min_units, ceil((prompt_tokens + completion_tokens * output_weight) / divisor))

The inputs are platform-configured with these defaults:

TermConfig keyDefaultMeaning
output_weightAI_CHAT_TOKEN_BILLING_OUTPUT_WEIGHT1.5Multiplier applied to completion tokens (output is weighted heavier than input). Must be finite and ≥ 0, else falls back to 1.5.
divisorAI_CHAT_TOKEN_BILLING_DIVISOR100Tokens per credit unit; clamped to a minimum of 1.
min_unitsAI_CHAT_TOKEN_BILLING_MIN_UNITS1Floor per call so a trivial turn still costs at least one unit.

With the defaults, a call using 1,200 prompt tokens and 800 completion tokens costs ceil((1200 + 800 × 1.5) / 100) = ceil(24.0) = 24 units.

Reserve, then settle

Because token counts are not known until inference finishes, an AI call is billed in two steps:

If inference itself fails (the provider is unavailable, a stream breaks), the full reserve is refunded and no units are charged — the endpoint returns 502 upstream_error.

Estimated tokens when usage is omitted

Settlement uses the provider’s reported usage when present. When a provider (or a streaming turn) omits token counts, Krallo estimates them from the request and response text at roughly 4 characters per token, so the meter still tracks real token volume instead of collapsing every call to the 1-unit floor. Estimation only ever runs as a fallback; a provider-reported count always wins.

The usage object returned in a chat completion reflects the provider’s reported prompt/completion tokens (0 if the provider omitted them). The credit units actually metered are derived from the effective counts — provider-reported when available, otherwise estimated.

Flat-priced calls

Non-AI endpoints — listing personas and memories, reading guilds, and browsing webhook deliveries — are not token-shaped, so they are priced at a flat units per call. This defaults to 1 unit (DEVELOPER_API_UNITS_PER_CALL). Setting it to 0 makes these endpoints free: they never consume quota and never bill (the request counter still advances for analytics).

Like AI calls, a flat call draws from the free quota first and only debits the wallet once the app is over quota and billing is enabled.

Image generation

Image generation (POST /api/v1/ai/imagine) is flat-priced per image because the cost is known up front, at AI_IMAGE_TOKEN_COST credit units (default 10 units, minimum 1). The charge is applied when the job is accepted and refunded in full if the image is not delivered — a failed job, or one that does not finish within the request’s wait_timeout_ms, is cancelled and the units are returned (the endpoint then returns 504 timeout or 502 upstream_error). You are never charged for an image you did not receive.

Refunds on failure

A billable call that is charged and then fails is reversed. The refund backs out every meter the call added: the wallet debit, the daily credit-unit advance, and the request-count increment. The reversal is pinned to the exact UTC day the charge landed on, so it reverses the correct counter row even across a midnight tick. Refunds are idempotent and best-effort — a call is never charged twice, and a refund failure is logged without affecting the response you already received.

Concretely, you are refunded when:

Running out of credits

When an app is over its free quota, billing is enabled, and the wallet cannot cover the call, the request is rejected with HTTP 402 and the code insufficient_credits. No work is performed and nothing is charged. The error carries the units the call needed and your current balance so you can decide whether to top up or back off.

Native (envelope) endpoints return the standard error shape:

{
  "error": {
    "code": "insufficient_credits",
    "message": "Insufficient credit balance for this request.",
    "required_units": 1,
    "balance": 0
  },
  "meta": { "request_id": "a3d0f9b2-4c6e-4b8a-9d1c-7e5f2a0b8c64" }
}

The OpenAI-compatible chat completions endpoint returns the same information in OpenAI’s error shape (here the pre-authorization reserve could not be covered):

{
  "error": {
    "message": "Insufficient credit balance for this request.",
    "type": "insufficient_credits",
    "required_units": 250,
    "balance": 0
  }
}

See Errors for the full status-code and error-code reference.

Checking your usage

The usage endpoint reports the authenticated app’s quota status for today plus a day-by-day series you can chart. It is deliberately unmetered — quota visibility stays reachable even when you are over quota or out of credits — and requires the usage:read scope.

GET/api/v1/usage

Query parameters

NameTypeRequiredDescription
daysintegerNoLength of the returned daily series, ending today (UTC). Clamped to [1, 90]; defaults to 7. Invalid values fall back to 7.

Response fields

FieldTypeDescription
app_idstringThe authenticated app.
datestringToday’s UTC date (YYYY-MM-DD) — the current quota window.
free_quota_unitsintegerThe app’s free daily allowance. 0 means unlimited free.
used_units_todayintegerCredit units consumed so far today.
remaining_free_unitsinteger | nullFree units left today (free_quota_units − used_units_today, floored at 0). null when the quota is unlimited.
billing_enabledbooleanWhether over-quota calls currently debit the wallet.
units_per_callintegerThe flat price of a non-AI billable call.
window_daysintegerThe resolved days value for the series.
dailyarrayDense series, oldest → today, one entry per UTC day: { "date", "credit_units", "requests" }. Days with no traffic are zero-filled.

Request

curl "https://exe.krallo.com/api/v1/usage?days=7" \
  -H "Authorization: Bearer krallo_live_..."

Response

{
  "data": {
    "app_id": "01J8ZBQ2C4D6E8F0G2H4J6K8M0",
    "date": "2026-07-15",
    "free_quota_units": 10000,
    "used_units_today": 148,
    "remaining_free_units": 9852,
    "billing_enabled": false,
    "units_per_call": 1,
    "window_days": 7,
    "daily": [
      { "date": "2026-07-09", "credit_units": 0,    "requests": 0  },
      { "date": "2026-07-10", "credit_units": 512,  "requests": 34 },
      { "date": "2026-07-11", "credit_units": 96,   "requests": 12 },
      { "date": "2026-07-12", "credit_units": 0,    "requests": 0  },
      { "date": "2026-07-13", "credit_units": 1240, "requests": 71 },
      { "date": "2026-07-14", "credit_units": 380,  "requests": 22 },
      { "date": "2026-07-15", "credit_units": 148,  "requests": 9  }
    ]
  },
  "meta": { "request_id": "b7e1c3a5-9d2f-4e6b-8a0c-4f7d1e9b3a52" }
}

Errors

StatusCodeWhen
401unauthorized · invalid_tokenMissing (unauthorized) or invalid (invalid_token) bearer key. See Authentication.
403insufficient_scopeThe key lacks usage:read. See Scopes.
405method_not_allowedAny method other than GET.
429rate_limitedPer-key rate limit exceeded. See Rate limits.
500internal_errorCounter store outage. The endpoint fails closed rather than reporting a wrong number.

Configuration reference

All pricing is platform-configured; these are the keys and their defaults. Per-app overrides (the free allowance) live on the app record. You do not set these — they are listed so you can reason about what a call will cost.

KeyDefaultControls
DEVELOPER_API_FREE_QUOTA_UNITS10000Free credit units per app per UTC day (0 = unlimited). Overridable per app via free_quota_override.
DEVELOPER_API_BILLING_ENABLEDfalseWhether over-quota calls debit the prepaid wallet. Off = over-quota calls stay free (still metered).
DEVELOPER_API_UNITS_PER_CALL1Flat credit cost of a non-AI billable call (0 = never charge / never consume quota).
AI_IMAGE_TOKEN_COST10Flat credit cost of one image generation (minimum 1).
AI_CHAT_TOKEN_BILLING_OUTPUT_WEIGHT1.5Completion-token multiplier in the weighted-unit formula.
AI_CHAT_TOKEN_BILLING_DIVISOR100Tokens per credit unit (minimum 1).
AI_CHAT_TOKEN_BILLING_MIN_UNITS1Floor units per AI call.
AI_CHAT_TOKEN_BILLING_PREAUTH_UNITS250Units reserved from the wallet before an over-quota AI call runs.

Related