Read /v1/balance: state, micros and the expiring-soon amount
What GET /v1/balance returns, why an empty state means a 402 is coming, and a Node check that compares available micros to a job's cost before you submit.

Call GET /v1/balance, read data.balance.state, and compare data.balance.available_amount_usd_micros with the cost of the job you are about to send. If the state is empty, or the micros are lower than the cost, do not submit: the create call would answer 402 insufficient_credits. The balance is a USD figure for the workspace of the API key, and a workspace with no balance row yet reads as an explicit zero, not as an error.
The fields on data.balance
The OpenAPI schema lists seven top-level fields plus an expiration summary. Use the micros field for math, because it is an integer and never rounds.
| Field | Type | Meaning |
|---|---|---|
| currency | string | Always USD |
| available_amount_usd_micros | integer | Spendable balance in millionths of a dollar |
| available_amount_usd_cents | integer | The same balance in cents |
| available_credits | integer | Legacy rounded cent amount kept for compatibility |
| state | funded or empty | empty means no spendable balance, or no balance row yet |
| updated_at | date-time or null | When the balance last changed |
| expiration | object | Summary of spendable credit lots that expire |
The expiration summary
The expiration object covers credits that are spendable right now. Amounts exclude expired, reserved, captured and depleted credits. next_expires_at is the earliest expiry, or null when nothing is spendable. next_expiring_amount_usd_micros says how much expires at that time. expiring_soon_days is the window, and expiring_soon_amount_usd_micros is the amount that falls inside it.
A Node preflight
Save this as preflight.mjs so top-level await works. It sends one credential header, x-api-key, because sending both Authorization and x-api-key returns 401. The cost is passed in micros: an 8-second Gemini Omni Flash 1.1 clip at 720p is 8 x $0.125 = $1.00, which is 1,000,000 micros.
const need = Number(process.argv[2] ?? 1_000_000); // micros
const res = await fetch("https://api.sume.com/v1/balance", {
headers: { "x-api-key": process.env.SUME_API_KEY ?? "" },
});
if (!res.ok) {
console.error("balance read failed", res.status);
process.exit(2);
}
const { balance } = (await res.json()).data;
const usd = (m) => (m / 1_000_000).toFixed(2);
const exp = balance.expiration;
if (balance.state === "empty" || balance.available_amount_usd_micros < need) {
console.error(`short: have $${usd(balance.available_amount_usd_micros)}, need $${usd(need)}`);
process.exit(1);
}
if (exp.expiring_soon_amount_usd_micros > 0) {
console.log(`$${usd(exp.expiring_soon_amount_usd_micros)} expires within ${exp.expiring_soon_days} days`);
}
console.log(`ok: $${usd(balance.available_amount_usd_micros)} available`);What the check does not guarantee
Treat the preflight as a cheap way to stop a batch before the first request, and keep the 402 handler as the real guard.
For a batch, multiply: 20 clips of 8 seconds at 720p need 20 x 1,000,000 = 20,000,000 micros, which is $20.00. Compare that sum once, then submit in waves that respect your plan's queue capacity.
- It is a read, not a hold. Another job can spend the balance between your check and your submit, so still handle 402 on the create call.
- Sume reserves the cost at submit, so a queued job already lowers the balance you read.
- Exit code 1 means short balance and 2 means the read failed, so a CI step can tell them apart.
Sources
Related posts
More in Developers
- Bash script to submit, poll, and download a Sume video
A 22-line bash and jq script for POST /v1/videos: idempotency key, 30-second polls, a 20-minute cap, and exit codes 0 to 3 that a scheduler can read.
- Proxy for a Sume video button: forward the click's Idempotency-Key
The docs proxy sample adds crypto.randomUUID() on every forwarded call, so a browser retry makes a second paid job. Take the key from the client for /v1/videos.
- Budget 250 product shots: read each Sume model's price in code
A short Python script reads the pricing line from Sume's per-model endpoints route and prints the cost of 250 product shots. Reference rules and a worked table.
- Build IMAGE_REF tags in Python for up to 10 Omni references
A Python helper turns a list of up to 10 image URLs into an Omni Flash prompt with matching IMAGE_REF tags, 0-based, in list order. Runs as written.
Written by Sume