Sume /v1/balance: next_expires_at and the expiring-soon fields
GET /v1/balance returns USD micros and cents, a funded or empty state, and an expiration block with the next expiry and the amount expiring soon. Field list.

GET /v1/balance on Sume returns the workspace's spendable USD balance and, in an expiration block, when the next credit lot expires and how much expires within an expiring-soon window. Use available_amount_usd_micros for the balance, state to see funded or empty, and expiration.expiring_soon_amount_usd_micros to see what will lapse soon.
The fields come from the live OpenAPI schema. The Usage docs say the balance is USD-denominated, with compatibility fields that expose rounded cents as credits.
Which balance fields should I read?
Read the micros fields when you do arithmetic and the state field when you only need a yes or no. The cents and credits fields are rounded views of the same money, so mixing them with micros in one calculation invites off-by-a-cent errors.
| Field | What the schema says |
|---|---|
available_amount_usd_micros | Spendable USD in micros. |
available_amount_usd_cents | The same amount in cents. |
available_credits | Legacy rounded USD-cent amount retained for compatibility. |
state | funded or empty; empty means no spendable USD or no balance row yet. |
expiration.next_expires_at | Earliest expiry among spendable credit lots, or null when none are spendable. |
expiration.expiring_soon_amount_usd_micros | Spendable USD expiring within the expiring-soon window. |
expiration.expiring_soon_days | The window length used for that amount. |
What counts toward the expiry amounts?
The schema says the amounts exclude expired, reserved, captured and depleted credits. So the figure is credit you could still spend, not the sum of all lots ever bought. A missing balance row is returned as an explicit zero, not an error.
How do I check it before a run?
The response is wrapped as data.balance. This script exits if the wallet is empty and warns when credit is about to lapse.
import os
import requests
r = requests.get(
"https://api.sume.com/v1/balance",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
b = r.json()["data"]["balance"]
if b["state"] == "empty":
raise SystemExit("wallet empty")
exp = b["expiration"]
print(b["available_amount_usd_micros"] / 1e6, "USD")
if exp["expiring_soon_amount_usd_micros"]:
print("expiring soon:", exp["expiring_soon_amount_usd_micros"] / 1e6,
"within", exp["expiring_soon_days"], "days")What should I do with it?
Treat the balance as a gate, not a forecast.
- Gate bulk runs on
stateand the micros balance, as in the balance preflight post. - Do not read
available_creditsas a unit of its own: it is a rounded cent amount kept for older clients. - Top-ups are a dashboard action; the credits docs say the public API exposes balance and usage reads, not top-up creation.
Sources
Related posts
More in Developers
- Sume /v1/usage limit: it caps rows, not the summary total
On GET /v1/usage, limit (1 to 100) only caps the rows listed. With thread_id, run_id or job_id the summary folds every row, up to 5,000.
- Sume webhook signature fails: compare the secret fingerprint
When a sume-v1 signature does not verify, one header tells you if the wrong secret signed it. A 24-line Node check reads the fingerprint and names the cause.
- Sume webhook never arrived: a sweeper that settles pending jobs
Run a small timer job that reads status for jobs still pending after ten minutes and settles them with the same guarded update your webhook route uses.
- Test a Sume webhook receiver with node:test and signed fixtures
Three node:test cases that sign their own Sume webhook bodies: fresh, rotation header, and the three rejections. Runs with node --test.
Written by Sume