RawFrame

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

  1. 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.
  2. Hold. That amount is reserved from your available balance. If your balance can't cover it, the request fails with 402 insufficient_credits and nothing is queued.
  3. 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_micros you can spend, and held_micros reserved by generations in progress. available_micros can 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 your metadata.

These need the account:read scope.

On this page