Service-account 402: daily, monthly or per-end-user spend cap hit
Three different 402 codes mean three different caps. Read details.cap_usd_micros and current_usd_micros to see which window blocked the request.

If your Sume key is a service-account key, a paid generation can be refused with one of three 402 codes: service_account_daily_spend_cap_exceeded, service_account_monthly_spend_cap_exceeded or service_account_end_user_spend_cap_exceeded. The code tells you which window ran out, and details gives the cap, what the account has already used and what this request would cost, all in millionths of a USD. The refusal happens before a reserve, so nothing is billed for the refused request.
Which cap fires first
The source checks the caps in a fixed order, and returns the first one that would be crossed. Each check is current usage plus the requested amount against the cap, so a request can be refused while usage is still under the cap:
| Order | Code | Window | Only checked when |
|---|---|---|---|
| 1 | service_account_daily_spend_cap_exceeded | Daily total for the service account | A daily cap is set |
| 2 | service_account_monthly_spend_cap_exceeded | Monthly total for the service account | A monthly cap is set |
| 3 | service_account_end_user_spend_cap_exceeded | Daily total for one end user | A per-end-user cap is set and the x-sume-source-user-id-hash header is present |
What retrying does
In the current source these 402s do not have a dedicated mapping, so the envelope falls to the generic branch: category: validation, retryable: false, next_action: fix_input. That wording is misleading, because the body of the request is fine. What fixes it is time, a smaller request, or a change to the key's policy by whoever manages it. A same-body retry in the same window returns the same code.
The per-end-user cap exists only when you pass the end-user hash header. If your product serves many users through one key, send a stable hash of the user id (never the raw id) so one heavy user hits their own cap and not the shared daily total.
Branch on the code, not on 402
A plain 402 insufficient_credits means add funds. These three do not. Sort by code and show the right message:
import json
BODY = '''{"error": {"code": "service_account_end_user_spend_cap_exceeded",
"details": {"cap_usd_micros": 2000000, "current_usd_micros": 1900000,
"requested_usd_micros": 300000}}}'''
WINDOW = {
"service_account_daily_spend_cap_exceeded": "daily",
"service_account_monthly_spend_cap_exceeded": "monthly",
"service_account_end_user_spend_cap_exceeded": "per-user daily",
}
err = json.loads(BODY)["error"]
d = err["details"]
left = (d["cap_usd_micros"] - d["current_usd_micros"]) / 1e6
print(f"{WINDOW[err['code']]} cap: ${left:.2f} left, need ${d['requested_usd_micros']/1e6:.2f}")A practical sizing checklist
Before you ship a batch job on a capped key, work out the budget from the numbers the error already gives you, and keep the logic in one place so that the three codes are handled the same way.
- Log
code,cap_usd_micros,current_usd_microsandrequested_usd_microswith therequest_idfrom the envelope, so support can find the refusal. - Split a large batch so that one request never costs more than the smallest cap on the key.
- For a daily cap, queue the rest until the next day instead of retrying every few minutes.
- For a per-end-user cap, show that one user a plain message and keep serving everyone else.
- Never retry with a different Idempotency-Key to get around a cap: a new key is a new intent and meets the same cap.
Limits
Service-account keys are a policy layer on a key and are not created by every account. Format runs and Agent Completions, for example, are not available to them (see the related posts). Check what your key is allowed to do before you build on it. The general error envelope is on the Errors and credits page.
Sources
Related posts
More in Developers
- language_code or auto-detect on Sume STT? A two-arm test on your clips
AssemblyAI reports 8.4% mean WER over 18 FLEURS languages. For your audio, run each clip twice on Sume STT, with and without language_code, and compare.
- SHA-256 manifest for a batch of 30-second clips: spot bad downloads
After downloading many 30-second Sume clips, write a manifest of size and SHA-256 per job id so a rerun skips good files and re-fetches bad ones. Python.
- Shorts series episodes number by publish date: a Python order check
YouTube numbers Shorts series episodes by publish date, so upload order is episode order. Check durations and publish times in Python before you schedule.
- Shorts series: one Idempotency-Key per season and episode
Timeline renders require an Idempotency-Key. Name it from season and episode, so a retried upload script for episode 4 does not queue a second render.
Written by Sume