Rust reqwest: POST /v1/images on Sume and branch on 200 or 202
Call Sume's image API from Rust with reqwest::blocking and serde_json; one 40-second client timeout and a match on 200, 202 and errors.

The blocking client is the shortest path for a CLI tool; use the async client inside a larger service.
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 |
Rust code
Add reqwest = { version = "0.12", features = ["blocking", "json"] } and serde_json = "1" to Cargo.toml, then SUME_API_KEY=... cargo run.
use serde_json::{json, Value};
use std::{env, time::Duration};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = env::var("SUME_API_KEY")?;
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_secs(40))
.build()?;
let res = client
.post("https://api.sume.com/v1/images")
.bearer_auth(key)
.json(&json!({
"model": "bytedance-seed/seedream-5-lite",
"prompt": "matte ceramic mug on a white sweep, soft shadow",
"aspect_ratio": "16:9"
}))
.send()?;
let status = res.status().as_u16();
let text = res.text()?;
match status {
200 => {
let v: Value = serde_json::from_str(&text)?;
println!("done: {}", v["data"][0]["url"]);
}
202 => println!("queued, follow status_url: {text}"),
_ => println!("error {status}: {text}"),
}
Ok(())
}Notes
The response is read as text first, so an error body that is not an image result never fails JSON parsing.
env::var("SUME_API_KEY")? fails fast when the key is missing instead of sending an unauthenticated request.
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
- Retry a timed-out video submit without paying twice: Idempotency-Key
Your client timed out after submitting a 30-second video job? Retry with the same Idempotency-Key and Sume returns the original job, not a second bill.
- Save a finished Seedance clip to your own storage, not the result URL
When a Seedance 2.5 or Wan 3.0 job completes, read /v1/jobs/{id}/result, take the artifact url and content_type, and copy the file into your own storage.
- Save a sidecar JSON with every Sume mp4: job id, model, cost, prompt
Export finished video jobs with a Python script that downloads each mp4 and writes a sidecar JSON with job id, model, cost and the prompt you saved.
- Scan your repo for retiring Google image and TTS ids (Python)
A 30-line Python scanner that finds Imagen 4, Nano Banana and Gemini TTS preview ids in your files and prints the replacement Google names for each one.
Written by Sume