generation_spend_cap_usd on a Format run: null is $500, 0 is a 400

On a Format run request, omit generation_spend_cap_usd for the Format cap, send a number up to 500, null for the $500 maximum. 0 or above 500 returns 400.

5 min readSume
All posts

Leave generation_spend_cap_usd out and the run uses the Format's own cap. Send a number from above 0 up to 500 and that number is the cap of this run. Send null and the cap becomes the $500 platform maximum. Send 0, or anything above 500, and the request fails with 400. The docs give the reason for the zero rule: a run that cannot spend cannot deliver.

The five cases

Every Format has a generation spend cap, and a run can never spend more than its own effective cap. The request field only sets this run's ceiling. A Format that never named a cap reports the platform default of $400 in generation_spend_cap_usd_micros.

You sendThe run's cap
NothingThe Format's cap
A number up to 500That number. A value above the Format's own cap is accepted and not clamped
nullThe platform maximum, $500. It lifts the ceiling but does not remove it
0400
Above 500400

Two details follow from the table. First, a request can raise the cap above what the Format owner set, so the cap is a control of the caller. Treat it as a budget you enforce in your own code. Second, null is not the same as omitting the field: omitting keeps the Format's cap, while null raises the run to $500. Serializers that drop undefined and keep null will therefore change spend, so check what your JSON library emits.

Build the field safely

The helper below returns the body fragment for each case and throws for values the API would reject, so the mistake shows up in your tests rather than as a 400 in production. It performs no network calls.

// Per-run ceiling for generation_spend_cap_usd, per the Calling a Format rules.
export function capField(value) {
  if (value === undefined) return {};          // the Format's own cap applies
  if (value === null) return { generation_spend_cap_usd: null }; // platform maximum, $500
  if (!(value > 0 && value <= 500)) {
    throw new RangeError("generation_spend_cap_usd must be above 0 and at most 500");
  }
  return { generation_spend_cap_usd: value };
}

console.log(capField(undefined), capField(null), capField(25));
try { capField(0); } catch (e) { console.log(e.message); }
try { capField(501); } catch (e) { console.log(e.message); }

What happens at the cap

  • The wallet is checked at create. 402 insufficient_credits (next_action: add_funds) or 402 organization_wallet_not_provisioned means nothing ran.
  • The cap is checked during the run. When it is reached, the run ends as failed, and usage shows how near to the cap it got.
  • You still pay for generation that finished before a failure. The docs do not refund it.
  • Watch headroom on the receipt with usage.cap. It gives limit_usd_micros, counted_usd_micros and remaining_usd_micros.
  • Sizing: the docs say production long-form runs usually have caps near $120, and a single-scene retry a few dollars. Use your own receipts to pick your numbers.

Where a cap belongs in a pipeline

Set the cap per job type, not per account. A retry of one scene and a full long-form video should not share a number. When an item fails on the cap, raise the value for the next run instead of retrying the same one, since the same plan will hit the same ceiling. Log the cap you sent next to the run id and the Idempotency-Key, so a later failed receipt can be explained without guessing which ceiling applied. The numbers on this page come from the Calling a Format and Errors and spend pages.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume