Sume GET /v1/balance expiration fields: warn before credits lapse
GET /v1/balance reports when your next credit lot expires and how much expires soon. Read the fields, convert micros to dollars, and alert from a cron job.

GET https://api.sume.com/v1/balance returns an expiration object next to your spendable amount: next_expires_at, the amount in that lot, and an expiring_soon amount with the number of days it covers. Poll it once a day and alert when expiring_soon_amount_usd_micros is above zero, so a lot does not lapse unnoticed.
The fields below are from the live OpenAPI document (read 2026-10-10) and the API source in the repository, not from a docs prose page. The Billing and credits page only says that balance and usage are read-only endpoints.
What does the balance object contain?
The response is wrapped in data.balance. Money comes in three shapes that all describe the same amount: available_amount_usd_micros (the exact one), available_amount_usd_cents (rounded down), and available_credits (a legacy rounded cent amount kept for compatibility). Use micros for any arithmetic. One US dollar is 1,000,000 micros.
| Field | Type | What it tells you |
|---|---|---|
| state | funded or empty | empty means no spendable USD balance, or no balance row yet. |
| available_amount_usd_micros | integer | Spendable now, excluding expired, reserved, captured and depleted credit. |
| expiration.next_expires_at | timestamp or null | Earliest expiry among spendable lots. Null when nothing is spendable. |
| expiration.next_expiring_amount_usd_micros | integer | Amount in that earliest lot. |
| expiration.expiring_soon_days | integer | The window for the next field. The API source I read sets it to 30. |
| expiration.expiring_soon_amount_usd_micros | integer | Spendable amount that expires inside that window. |
A daily check in Python
The script uses only the standard library. It prints the state, the next lot, and a warning line you can pipe into whatever alerting you already run. Cents fields are rounded down, so do not sum them across lots; sum micros.
import json, os, urllib.request
req = urllib.request.Request(
"https://api.sume.com/v1/balance",
headers={"x-api-key": os.environ["SUME_API_KEY"],
"User-Agent": "sume-example/1.0"})
with urllib.request.urlopen(req, timeout=30) as r:
bal = json.load(r)["data"]["balance"]
exp = bal["expiration"]
usd = lambda micros: micros / 1_000_000
print("state:", bal["state"], "available $%.2f" % usd(bal["available_amount_usd_micros"]))
if exp["next_expires_at"]:
print("next lot: $%.2f expires %s" % (
usd(exp["next_expiring_amount_usd_micros"]), exp["next_expires_at"]))
if exp["expiring_soon_amount_usd_micros"] > 0:
print("WARNING: $%.2f expires within %d days" % (
usd(exp["expiring_soon_amount_usd_micros"]), exp["expiring_soon_days"]))How is an empty balance different from a 402?
A missing balance row is reported as an explicit zero with state: "empty", not as an internal error. That is a read. The 402 insufficient_credits error is a different event: it happens on a generation submit when Sume cannot reserve the estimated cost from the workspace balance, and it is raised before any provider work starts.
So the balance read is a cheap preflight, but it is not a guarantee. Another job can reserve funds between your read and your submit, and the estimate for a given request is the thing that must fit. Keep handling 402 on submit. Per the errors page, the fix is to add funds or lower the request cost, not to retry the same call.
What the API cannot do for you
Top-ups are a dashboard operation. The credits page states that the public Developer API gives balance and usage reads and has no endpoint to create a top-up. So your alert should notify a human, or open a ticket, and not try to buy credit on its own.
Also note that the expiry numbers describe credit lots, not your plan's concurrency. Concurrency is plan-only and prepaid top-ups do not raise it, as the generation admission page explains.
- Alert on
expiring_soon_amount_usd_micros > 0, not onstatealone: a funded balance can still be about to shrink. - Log
next_expires_atas a timestamp so the alert is deduplicated per lot. - Treat any non-2xx on this read as a monitoring failure, not as an empty wallet.
Sources
Related posts
More in Developers
- sume models list is a deprecated alias: use sume catalog list
The Sume CLI keeps sume models list as a deprecated alias of sume catalog list. What the catalog command shows, what it cannot do, and how to migrate scripts.
- Sume public_reason: generation_rejected vs temporary error
How Sume picks generation_rejected, temporary_generation_error or generation_failed on a failed job from the provider HTTP status and the retryable flag.
- Sume "Generation could not start": rejected request or outage
Generation could not start is the fallback when a provider rejects a submit. A 4xx gives generation_rejected_request; anything else gives submit_failed.
- Sume image 400 'does not accept aspect_ratio': read supported (Python)
When a Sume image row rejects aspect_ratio with 400 invalid_request, the error carries a supported array. This Python snippet picks the nearest listed ratio.
Written by Sume