Claude batch canceled or expired results: billed? Sume cancel billing
Claude does not bill canceled or expired batch requests. Sume bills generation a run finished before a cancel, so a cancel stops spend but never refunds.

On Claude's Message Batches API, a request whose result is canceled or expired is not billed, because it never reached the model. On Sume, cancelling a Format run stops further work but generation the run already completed is billed, and usage reports it. If you are porting batch cost logic, the two rules point in opposite directions for in-flight work.
Claude's rules are on its batch processing page. Sume's are in Runs and results and Errors and spend.
What result types does a Claude batch return?
Each request in a finished batch has a result that is succeeded, errored, canceled or expired. The page says a request is canceled if you cancelled the batch before it was sent to the model, and expired if the batch reached its 24-hour expiration first. Neither is billed. Results stay available for 29 days after the batch was created, counted from created_at rather than from when processing ended.
| Result | Meaning | Billed |
|---|---|---|
| succeeded | The request ran | Yes |
| errored | The request hit an error; error object included | See the page for error details |
| canceled | Batch canceled before this request was sent to the model | No |
| expired | 24-hour expiry reached before this request was sent | No |
What does cancel do on a Sume run?
A Format run is not one model call; it is an agent turn that submits generation jobs. Sume's cancel is POST /v1/format-runs/{run_id}/cancel, needs formats:write, and is idempotent. The receipt carries cancel_effect: canceled means this call stopped a run in flight, and no_op means it had already finished.
The billing line is explicit in the docs: generation the run completed before the cancel is billed, and a later step failing does not refund it. A 4xx at create, an idempotent 200 replay and a skipped run cost nothing. Check usage.billable_amount_usd_micros on the receipt to see the figure, keeping in mind it excludes the agent's own LLM turn and is a receipt, not an invoice.
What about cancelling inside a bulk run?
Cancelling a child marks that queue item canceled and frees its slot, so the next queued item starts. There is no endpoint to cancel the whole queue, so to stop a queue you cancel its children one by one, and items still queued have no run_id to cancel yet. Plan for that before a large queue: the cheaper control is a per-run generation_spend_cap_usd, which ends a run that would spend past its ceiling.
A canceled run never delivers a webhook, so a webhook-only integration must poll status_url for the final state.
How do I port the cost logic?
Do not copy the Claude rule that cancelled work is free. For Sume, compute spend from the receipt after the run is terminal, whichever way it ended.
import os, requests
def spent_usd(run_id: str) -> float:
r = requests.get(
f"https://api.sume.com/v1/format-runs/{run_id}",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
usage = r.json()["data"].get("usage")
if usage is None: # spend could not be read, which is not zero
raise RuntimeError("usage unavailable, check GET /v1/usage")
return usage["billable_amount_usd_micros"] / 1_000_000
print(spent_usd(os.environ["RUN_ID"]))The docs say usage is null when spend could not be read, which is different from 0, so the snippet raises rather than reporting free. For billing records use `GET /v1/usage` and GET /v1/balance, not the receipt.
Sources
Related posts
More in Comparisons
- Claude Message Batches queue limits by tier vs Sume bulk runs
Claude's batch queue holds 200,000 requests on Start and up to 500,000 on Scale. A Sume bulk run takes 1 to 100 items, 16 at once. Different jobs.
- Cloudflare Stream clip API vs Sume video trim: start and end
Cloudflare Stream clips by posting a source UID with startTimeSeconds and endTimeSeconds. Sume video-trim takes start plus end or duration, returning a new MP4.
- Cloudinary video 423 error on long videos vs Sume's 1800 s cap
Cloudinary returns 423 while a video over 30 or 60 minutes transforms asynchronously. Sume admits renders up to 1800 seconds and never returns 423.
- Cloudinary video trimming: percent or seconds vs Sume video trim
Cloudinary trims by start, end or duration, in seconds or a percentage of the asset. Sume video-trim takes seconds only: start plus one of end or duration.
Written by Sume