Cancel 12 queued Sume jobs: 12 writes, and a 409 that is not an error
Cancel is a write, only works before generation starts, and returns 409 job_generation_already_started after. A Python sweep that treats 409 as fine.

Cancelling a Sume job is one write request each, so 12 queued jobs cost 12 writes. On Free that is 10% of the 120 writes per minute. Cancellation only succeeds before generation work starts; afterward the API returns 409 job_generation_already_started with details.cancelable: false, and the job runs to completion and bills. Treat that 409 as information, not as a failure.
Write cost by plan
The Authentication page counts cancellation as a write. The table shows a full queued backlog at the default queue capacity from Generation admission.
| Plan | Default queue capacity | Writes per minute | Cancel writes as a share of one minute |
|---|---|---|---|
| Free | 5 | 120 | 5 / 120 = 4.2% |
| Pro | 20 | 300 | 20 / 300 = 6.7% |
| Startup | 40 | 600 | 40 / 600 = 6.7% |
| Scale | 100 | 1,200 | 100 / 1,200 = 8.3% |
A sweep that tolerates the 409
The function returns a label for each job. A cancel of a job that is already canceled is idempotent and returns the same canceled job, so a second pass is safe. Any other error is raised. Set QUEUED_JOB_IDS to a comma-separated list to run it.
import json
import os
import urllib.error
import urllib.request
def cancel(job_id: str) -> str:
req = urllib.request.Request(
f"https://api.sume.com/v1/jobs/{job_id}/cancel",
method="POST",
headers={"Authorization": f"Bearer {os.environ.get('SUME_API_KEY', '')}"},
)
try:
urllib.request.urlopen(req, timeout=20).read()
return "canceled"
except urllib.error.HTTPError as err:
code = json.load(err).get("error", {}).get("code")
if err.code == 409 and code == "job_generation_already_started":
return "running: will finish and bill"
raise
if __name__ == "__main__":
ids = os.environ.get("QUEUED_JOB_IDS", "").split(",")
for job_id in filter(None, ids):
print(job_id, cancel(job_id)) # one write eachWhat cancel does to the bill
At submit Sume reserves the estimated amount. A cancel before generation starts releases the reservation where applicable, while a job that already started completes or fails normally and captures usage on success. That is why you cancel queued jobs you no longer need, rather than letting them reach processing.
The same rule holds when you stop waiting. A client timeout or an aborted wait does not cancel the job. Store the job id and cancel explicitly.
Avoiding the 409
Cancel soon after you decide. A queued job that moves to processing can no longer be canceled, so the window is the time spent waiting in the queue, which depends on your concurrency. On a Free workspace with one slot, the queue is longer than on Scale.
If you need many cancellations, pace them: 100 cancels on Scale is 100 of 1,200 writes, which leaves room for the rest of your traffic in the same minute.
Sources
Related posts
More in Developers
- Check a GPT Image 2.5 size before you call it: a Python validator
Sume rejects GPT Image 2.5 sizes that break four rules: multiples of 16, edge 3840, ratio 3:1, 655,360 to 8,294,400 pixels. A Python check, run on 8 sizes.
- Check your image cost table against Sume /v1/images/models cost_usd
Read pricing lines from GET /v1/images/models/{id}/endpoints and compare cost_usd with your own table before a batch. Sume lines already include its margin.
- CI guard: fail the build when a new Sume pricing_skus key appears
A node:test file fetches GET /v1/videos/models and fails when a model uses a pricing_skus key your cost code cannot total. Wan, Omni and H3 rates in the table.
- Run an Agent Completion nightly from CI with curl and a spend cap
A 25-line bash job that starts a Sume Agent Completion with a cap and an idempotency key, polls the receipt, and fails the build unless the run completed.
Written by Sume