R httr2: POST /v1/images on Sume without throwing on a 502
Call Sume's image API from R with httr2: bearer key from the environment, a 40-second timeout, and req_error so a 502 body is readable.

R users generating plot backgrounds or report art can call the API with httr2 in a few lines.
The request is the same in every language: POST https://api.sume.com/v1/images with a bearer key from SUME_API_KEY, a JSON body with model, prompt and aspect_ratio, and a client timeout above the route's 30-second wait. The route defaults to mode: "sync", so the status code decides what you do next (docs read 2026-10-07):
Status codes to branch on
| Status | Meaning | What the code below does |
|---|---|---|
| 200 | Image finished inside the wait; data[].url holds the file | Prints the result |
| 202 | Wait expired (or mode is async or webhook); body is a job envelope with status_url and result_url | Prints the envelope; poll status_url and read result_url |
| 502 | The job failed inside the wait; error has code, retryable, next_action | Prints the error |
| 400, 404 | unsupported_parameter, or model_not_found | Prints the error |
R code
Save as gen.R and run SUME_API_KEY=... Rscript gen.R, with the httr2 package installed (the native pipe needs R 4.1 or newer).
library(httr2)
key <- Sys.getenv("SUME_API_KEY")
if (!nzchar(key)) stop("SUME_API_KEY missing")
resp <- request("https://api.sume.com/v1/images") |>
req_auth_bearer_token(key) |>
req_body_json(list(
model = "bytedance-seed/seedream-5-lite",
prompt = "matte ceramic mug on a white sweep, soft shadow",
aspect_ratio = "16:9"
)) |>
req_timeout(40) |>
req_error(is_error = function(resp) FALSE) |>
req_perform()
status <- resp_status(resp)
if (status == 200) {
cat("done:", resp_body_json(resp)$data[[1]]$url, "\n")
} else if (status == 202) {
cat("queued, follow status_url:", resp_body_string(resp), "\n")
} else {
cat("error", status, resp_body_string(resp), "\n")
}Notes
By default httr2 raises an error on 4xx and 5xx. req_error(is_error = function(resp) FALSE) turns that off so you can read the error body, which includes code, retryable and next_action.
A 202 is not an error. It is a job envelope; read the file from its result_url after the job completes.
One bytedance-seed/seedream-5-lite image is $0.04375 billed (list $0.035 x 1.25). Prices here are Sume's list-times-1.25 figures. The catalog states the billable formula as "list × 1.25 → ceil usd cents", so treat the dollar amounts as the pre-rounding value and read the exact charge from billable_amount_usd_micros in the submit envelope. Failed or cancelled generations are not billed.
Sources: Sume Image API docs and Jobs and results (read 2026-10-07).
Sources
Related posts
More in Developers
- Read the motion clip length with video inspect before Kling duration
Kling motion control on Sume reserves money from the duration_seconds you declare. Probe the reference clip with video inspect first, so the number is measured.
- Reconcile Sume jobs after a deploy or outage: poll what is open
After downtime, read status for every job your own table still shows as open, honor terminal and result_ready, and never resubmit. Python with sqlite.
- Redact faces and license plates: Pillow first, AI edit only to replace
For redaction use Pillow boxes you control; use an AI mask edit on openai/gpt-image-2.5 only to replace a plate or face, from $0.0094 per image on Sume.
- Redeliver a missed video webhook after a bad deploy: one Sume call
Receiver down when the video finished? POST /v1/jobs/{job_id}/webhook/redeliver re-sends job.completed with a fresh signature. Scope, statuses, pitfalls.
Written by Sume