"Insufficient credits" / "credit reservation failed": what it means
When you create an image, video or audio task, its full price is reserved from your balance before anything is generated. When the task finishes you are charged what it actually cost (for example only the images that were delivered) and the rest is released; if it fails, everything is released. A task can only start when your available balance, total minus what is currently reserved, covers its price.
The error tells you the numbers: "this request needs $0.1200 but only $0.0450 is available ($0.3000 is held by requests still running)". The first figure is this request's price, the second is what you can spend right now, and the part in brackets is credit held by your other requests that have not finished yet.
Chat and other language-model requests are checked just before the call, against an estimate of the input plus max_tokens of output with a 20% margin.
The three ways it shows up
| Where | What you get | Status |
|---|---|---|
| Creating an image / video / audio task | "Insufficient credits: this request needs $X but only $Y is available ($Z is held by requests still running). Please top up and try again; nothing was charged." | HTTP 402; no task is created |
| A task that already has an id | "Your account balance was too low to start this request, so it was not run or charged." (or the message above) | state "failed", failCode "INSUFFICIENT_BALANCE"; this happens when several requests arrive at the same moment |
| Chat and other language-model calls | {"error": {"message": "Insufficient credits for this request", "type": "insufficient_credits"}} | HTTP 402 |
Why it happens when your balance looks big enough
Requests still running hold credit. Every request in progress keeps its full price reserved until it finishes. If you send a batch in parallel, the reservations add up, and once they exceed your balance the later requests are refused even though none of them has been charged yet. The "held by requests still running" figure in the error is exactly that amount, and it comes back as each running request finishes.
A request reserves its largest possible cost. Image requests reserve the number of images times the per-image price plus any reference-image fees; video requests reserve the requested seconds at the per-second price for that resolution, plus reference-video seconds where they are billed; language-model requests reserve for max_tokens of output, or for the model's default limit when you do not set max_tokens. A 4K, four-image or 15-second request can need far more than the one you sent a minute ago.
How to fix it
Top up on the Balance page of the console, by card, Alipay or USDT. If you run jobs unattended, turn on auto top-up there: save a card, pick a threshold and an amount ($10, $50, $100, $500 or $1,000), and the account is topped up whenever the balance falls below the threshold.
Cap concurrency for batches. Keep the number of requests in flight at or below your available balance divided by the price of one request, and check GET /v1/balance (the balance field is what you can spend now) before sending the next wave.
Set max_tokens on language-model requests. Without it the pre-check assumes the model's full output limit, which can be refused on a small balance even when the reply would have been short.
Make the request smaller: fewer images per call, a lower resolution or a shorter duration all lower the amount reserved.
Handling it in your code
Treat HTTP 402, failCode "INSUFFICIENT_BALANCE" and error type "insufficient_credits" as one condition: stop sending, alert whoever tops up the account, and resume after the balance is back. Retrying in a loop will not help, because nothing changes until credit is added or running requests finish.
Início rápido
cURL
# Available balance before sending a batch (free, no credits charged)
curl https://api.apimodels.app/v1/balance \
-H "Authorization: Bearer $APIMODELS_API_KEY"
# → { "code": 200, "msg": "success",
# "data": { "balance": 12.5, "total": 12.8, "frozen": 0.3, "currency": "USD" } }
# balance = total − frozen; frozen includes credit reserved by requests still runningPython
import os, requests
H = {"Authorization": f"Bearer {os.environ['APIMODELS_API_KEY']}"}
price_per_request = 0.05 # what one of your requests costs
available = requests.get("https://api.apimodels.app/v1/balance", headers=H).json()["data"]["balance"]
max_in_flight = int(available // price_per_request)
if max_in_flight == 0:
raise SystemExit("Top up first: one request costs more than the available balance")
print(f"Run at most {max_in_flight} requests at the same time")Perguntas frequentes
Was I charged for the request that failed with insufficient credits?
No. The request was refused before it ran, so nothing was charged and nothing stays reserved for it.
My balance shows enough credit. Why was the request refused?
Part of your balance is reserved by requests that are still running, and a request reserves its largest possible cost. Compare the "needs" and "available" figures in the error; the bracket shows how much your running requests are holding.
What does "frozen" mean in GET /v1/balance?
Credit that is reserved and cannot be spent right now, mostly by requests still running. It is settled when they finish: the actual cost is charged and the rest returns to your available balance.
Can the account top itself up?
Yes. On the Balance page of the console, save a card and turn on auto top-up with a threshold and an amount of $10, $50, $100, $500 or $1,000.
Why do chat requests ask for more credit than the reply costs?
The check runs before the reply exists, so it assumes the longest possible reply (max_tokens, or the model's default limit) plus a 20% margin. You are only charged for the tokens actually used. Setting a sensible max_tokens lowers the amount the check asks for.
