Docs

Get started

Credits

One balance, shared by the desktop and the API. What spends it, what never does, and what comes back.

Credits are the only unit of spend. One balance per account, and both the desktop and the API draw on it.

Nothing here is charged per user, per project or per seat.

#What a credit does

You spend credits on generations. What one costs depends on what you asked for — a longer video costs more than a shorter one, a denser mesh more than a rough one — and the cost is known before the job runs, not after (or, for a model charged what the job cost, the most it can be — below): the submit's answer carries credits_charged and credits_remaining before a single frame has rendered.

The price rule for an address is published with its contract. GET /v1/models/<address>/openapi.json carries it as x-auray-price, and the ## Price section of /v1/models/<address>/llms.txt says the same thing in a sentence. Neither needs a key.

#A quote that is a hold

Most catalogue models — every one priced by a band or by the token — are charged what the job cost. Their x-auray-price says metered: true, and the quote is a hold: the submit takes the most the request can cost (credits_quoted), the job settles to what it actually cost — never more than the quote — and the rest comes back on its own. A settled read of the job (GET /v1/jobs/{id} with settled: true) says what it finally cost in credits_charged. In the app, Generate says up to for these models, and the job's receipt shows what it holds until it settles, then what it cost. Every other address, ours included, says metered: false: its quote is the charge.

#What never spends

  • Reading anything. Listing models, reading a job, polling a status, listing webhook endpoints.
  • A refused request. Anything caught before inference — wrong scope, malformed body, no credits, ceiling reached — costs nothing.
  • A failed job. If it starts and then fails, the charge is reversed automatically.
  • A cancelled job. What hadn't been made yet comes back — per product, in the settlement table. A catalogue model's job is the exception: it cannot be stopped once it starts. In the desktop its button says Stop watching, which is what it does — the job finishes and is charged for what it delivers.
  • Downloading what you made. Asking for a job's signed asset URLs costs no credits; it counts the files' size, in whole megabytes, against the egress rate-limit bucket — a daily allowance that depends on your plan, reported in the answer as megabytes_charged — which is why the download route is separate from the status poll. The bucket counts the signing, once per call, not the fetch: a signed link can be fetched again until it expires, and fresh links can be asked for at any time in the job's 7 days, each asking counting the size again.

#Monthly grants

PlanPer monthCredits
Starter$1100
Coding$201,000
Creator$201,000
Pro$502,500

Credits arrive every month. On a monthly plan they come with each payment; on a plan paid by the year you pay once, and they arrive every month of that year, from the day you pay. The numbers above are read from the same table the wallet itself reads; they cannot drift from what you are charged.

#What a month's credits make

An estimate, counted from the same price rules the submit charges by and from what each plan can run — images in the largest batch one job makes, a clip at the cheapest price, a song of a minute. Every plan that runs Photos, Video, Music or 3D runs the whole of it. Only those four are counted; every other model's address publishes its own price rule, as above. The same lines sit under every paid plan on Pricing.

PlanExampleCredits eachEstimate a month
StarterBatches of 4 images at 1K23About 4
60-second songs92About 1
CodingBatches of 4 images at 1K23About 43
5-second clips421About 2
60-second songs92About 10
Textured 3D models131About 7
CreatorBatches of 4 images at 1K23About 43
5-second clips421About 2
60-second songs92About 10
Textured 3D models131About 7
ProBatches of 4 images at 1K23About 108
5-second clips421About 5
60-second songs92About 27
Textured 3D models131About 19

A plan whose limits.api.keys_max in GET /v1/plans (or Account → Plan & Billing → Compare plans, for a plan the pricing page does not list) is 0 holds no key at all, so its credits are spent in the app; a key comes with a plan that holds them, and Pricing says which do.

#Passes

A pass is unlimited use of one model for a day or a week from payment, one job at a time, without spending credits. Where one is on sale, it is bought once in the app and never renews; buying it again adds its time after the one you hold. Each pass works where it says — the app, the API, MCP — and covers the jobs it lists; a job it does not cover is treated as if you held no pass. A pass for a catalogue model runs that model on any plan, Free included.

GET /v1/me lists them in passes: which model, until when, where each works (doors), what it covers (covers, and covers_lines in words), whether a job of it is running, and whether your plan runs its jobs (runs_on_plan). A job a pass paid for costs nothing, and two answers say so — the submit's answer on queue.auray.run (credits_charged: 0) and GET /v1/jobs/{id}:

"coverage": { "by": "pass", "pass": "seedance-2-5" }

The submit's answer on queue.auray.run also says when a pass of yours for the model did not pay for a job it charged — {"by": null, "miss": {"pass": "video", "why": "door"}}, why being door (the pass does not work the way the job was sent) or shape (it does not cover a job like this one). A run on auray.run, and MCP's run_model, answers the job's output, which says neither: read GET /v1/jobs/{id} with its request_id to see what paid.

A pass takes a job at a time. Through the API and MCP, a second job the pass covers, sent before the first finishes, is refused 429 pass_busy with a Retry-After, and nothing is charged; in the app it waits its turn. A job the pass does not cover is not held back by it. A pass also covers a bounded amount each day. Once the jobs it covered that day reach it, the next job it would cover is refused 429 pass_busy with "why": "spent" until 00:00 UTC — in the app as well, where it does not wait — and nothing is charged. Passes and offers can be paused for a moment. While they are, a job one of them would pay for is refused 503 cover_paused and nothing is charged — it is never charged to your credits instead — and a pass gets the paused time back. GET /v1/me says so in covers_paused; a job nothing covers is charged as always. Send X-Auray-Covered-Only: 1 with a submit to run it only if a pass or a promotion pays for it: a job neither pays for is refused 409 not_covered, uncharged — or, where your plan runs the model only through a pass, refused as the plan refuses it (model_not_in_plan, pass_does_not_cover).

A paid plan may also offer bundles: chosen in the app, a bundle gives you a pass of each model it lists, for its days and never past the end of the paid month you chose it in. GET /v1/me lists what your plan offers in bundles — empty until it offers any — and each pass a bundle gave is on passes with source bundle and the bundle it came from; a job one of those paid for says coverage.bundle beside coverage.pass.

#Ceilings

A key can carry its own monthly cap, separate from the balance. Once that key has spent it in a calendar month (UTC), it stops generating until the next month begins, and every other key keeps working.

Worth setting on anything you do not fully control.

#Watching the balance

curl https://api.auray.ai/v1/me \
  -H "Authorization: Bearer $AURAY_API_KEY"
200 OK
{
  "object": "account",
  "id": "86e97633-…",
  "plan": "ultimate",
  "credits": { "balance": 38807, "monthly_allowance": 5000, "period": "2026-09" },
  "key": { "id": "55oy2jFo5cmq", "scopes": ["jobs:read", "assets:read", "photo:write"], "credit_ceiling": 500, "credits_spent_period": 332 },
  "request_id": "961dd3b6-59b4-4e26-bb8b-e4c28f1cbf96"
}
Spend over the period, by product, by origin and by key
curl https://api.auray.ai/v1/usage \
  -H "Authorization: Bearer $AURAY_API_KEY"
200 OK, abridged
{
  "object": "usage",
  "period": "2026-09",
  "credits": { "balance": 38807, "monthly_allowance": 5000, "spent_this_period": 572, "refunded_this_period": 24, "net_this_period": 548 },
  "by_product": [
    { "product": "photo", "origin": "api", "spent": 364, "refunded": 0, "jobs": 91, "net": 364 },
    { "product": "photo", "origin": "app", "spent": 22, "refunded": 0, "jobs": 4, "net": 22 },
    { "product": "chat", "origin": "api", "spent": 30, "refunded": 24, "jobs": 3, "net": 6 }
  ],
  "keys": [
    { "object": "api_key", "id": "…", "key_id": "55oy2jFo5cmq", "name": "…", "credit_ceiling": 500, "credits_spent_period": 332, "last_used_at": "…", "revoked_at": null }
  ],
  "keys_truncated": false,
  "request_id": "…"
}

origin tells the desktop's spend (app) apart from your code's (api); both are the same balance. report.doors splits the same spend by where it came in — desktop, api and mcp, and unknown for a key's jobs charged before the API and MCP were recorded apart. keys lists your secret keys, most recently used first, up to a hundred of them; keys_truncated is true when you hold more.

When the balance cannot cover a request, it is refused with insufficient_credits before anything runs, and the body says how many were needed.

#Rate limits are a separate thing

Running out of credits and going too fast are different refusals, and they look different on purpose.

Five buckets — read, write, generate, interactive (chat and language-model calls) and egress — refill continuously, and every response reports where you stand in both RateLimit-* and X-RateLimit-* headers. Hitting one answers 429 with a Retry-After; it is about pace, not about money, and waiting fixes it.

See Errors for both families in full.

Start typing to search every page of the documentation.

Up arrow Down arrow to moveEnter to openEscape to close