Credits and usage
What a submission costs you, how you find out you are out, and what /v1/usage is for.
Running out is a 402, not a 400
402 INSUFFICIENT_CREDITS on a submission. Nothing was queued, nothing was spent,
and retrying the identical call will get the identical answer until the balance
changes — so this is not a retryable failure, it is an operational one. Route it to
whoever can top up the account, not to your backoff loop.
A submission that is accepted (202) has committed its cost. A submission that is
refused has not. The failure modes in between are worth being precise about:
The last row is why an Idempotency-Key on every submission is not optional advice.
Without one, a timeout followed by a resubmission is two charges for one image.
When a submission is refused for credits
Credits are tracked per account. Whether a submission without enough units is refused depends on your account:
- Enforced. A submission that needs more units than the account can spend is
refused
402 INSUFFICIENT_CREDITSbefore anything is queued. - Not enforced. Submissions are accepted whatever the balance, and no units are
held or charged for them. You will not receive a
402; what you are invoiced is governed by your commercial agreement.
Read enforced from the balance below rather than assuming either. A pk_test_ key
never spends credits, in either case — see Test mode.
Reading the balance
GET /v1/credits/balance returns the balance of the account that owns the key, in
abstract units, never money. It is enabled per account; where it is not, it answers
404 as an unknown path does.
Polling the balance is a good way to warn before you run out. It does not replace
handling 402: another request can spend the last unit between your read and your
submission. A balance that cannot be read answers 503, never a zero balance.
What GET /v1/usage gives you
Job counts for your own account, per UTC day and per operation, over a half-open
start_date / end_date window: start_date is included and end_date is not. With
neither, the window is the last thirty full days plus today. A window longer than 92
days, or one that ends before it starts, is 400 INVALID_USAGE_RANGE.
A row exists only for a day and operation with activity, so iterate days; do not
index it by day offset. A window with no activity answers zeros in totals and empty
products and days, not a 404.
These are operational aggregates, not billing authority. They are the right input for a dashboard, a capacity conversation, or noticing that yesterday’s job count tripled. They are the wrong input for reconciling an invoice — that comes from your commercial agreement, and the two can legitimately differ.
Field meanings:
jobs_accepted— submissions accepted, successful or not.jobs_succeeded,jobs_failed,jobs_cancelled,jobs_pending— where those jobs stand as ofas_of. A pending job moves into one of the other three later, so a count can change. A cancelled job was cancelled by you before processing started and is never billed.billable_jobs— jobs whose terminal outcome the credit policy captures.final—falsefor a day within the last 48 hours, whose counts may still move. Treat only afinalday as settled.
The endpoint is authenticated and reports the environment of the key you call it
with: a pk_test_ key reads test usage, a pk_live_ key live usage. The report names
no account, and there is no way to read another account’s usage.
Pricing
Not documented here, and deliberately. Rates, included volumes and overage terms are in your commercial agreement, and a number on a docs page would eventually contradict a contract. Ask your account contact.