Pricing and credits
Prepaid credits, how a generation is priced, and what you're charged.
You pay from a prepaid credit balance in US dollars. Add credits on the dashboard's Billing page with a card. Each top-up gets a paid invoice. Turn on auto-recharge to top up automatically whenever the balance drops below a threshold you choose.
How a generation is charged
- Estimate. When you create a generation, its maximum price is worked out from the model's price
list and your request: for example duration × the per-second price for the chosen resolution, or the
number of images × the per-image price. It's returned as
price_estimate_micros. - Hold. That amount is reserved from your available balance. If your balance can't cover it, the
request fails with
402insufficient_creditsand nothing is queued. - Charge. When the generation succeeds, you're charged for what was actually produced (for
example, seconds of video delivered), never more than the estimate. The rest of the hold is released.
The charge is in
charge_micros.
Failed generations aren't charged, and neither is anything a provider or our checks blocked. Canceling a queued generation is free. Canceling one that's running follows the model's cancel policy, shown on its model page.
Reading prices
Prices are listed per model in pricing from GET /v1/models, as rules like:
{ "unit": "per_second", "when": { "resolution": "1080p" }, "amount_micros": 100000 }unit is what's counted (per_second of output video, per_image, per_megapixel, per_request),
when is the request parameters the rule applies to, and amount_micros is the price per unit. Exactly
one rule applies to any valid request. A model may also have a min_charge_micros.
Amounts
Every amount in the API is an integer in micro-USD: 1,000,000 = $1.00. Sub-cent prices stay exact, and integers don't drift with rounding. Divide by 1,000,000 to show dollars.
Balance and usage
import { RawFrame } from "@rawframe/sdk";
const rf = new RawFrame(); // needs the account:read scope
const usd = (micros: number) => `$${(micros / 1_000_000).toFixed(4)}`;
const balance = await rf.account.balance();
console.log(`Available ${usd(balance.available_micros)}, held by running jobs ${usd(balance.held_micros)}`);
const today = new Date().toISOString().slice(0, 10);
for (const bucket of await rf.account.usage({ start: today, end: today, group_by: "model" })) {
console.log(`${bucket.key}: ${bucket.generations} generations, ${usd(bucket.charge_micros)}`);
}
// Every succeeded generation, newest first. Pages are fetched as you iterate.
for await (const generation of rf.generations.iterate({ status: "succeeded", limit: 100 })) {
console.log(generation.id, generation.model, usd(generation.charge_micros ?? 0));
}GET /v1/account/balance:available_microsyou can spend, andheld_microsreserved by generations in progress.available_microscan go negative if a refunded top-up had already been spent.GET /v1/account/usage: totals per day or per model for a date range of up to 366 days.GET /v1/account/usage/export: one CSV row per generation, with charges and yourmetadata.
These need the account:read scope.