Swift URLSession: call Sume's image API with async/await
A Swift script that posts to /v1/images with URLSession, sets a 40-second timeout and prints the result for 200, the job envelope for 202, and errors otherwise.

Swift's URLSession is the natural choice for an iOS or macOS tool that needs one generated picture.
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 |
Swift code
Save as gen.swift and run SUME_API_KEY=... swift gen.swift. It uses top-level await, so use a recent Swift toolchain (5.7 or newer).
import Foundation
let key = ProcessInfo.processInfo.environment["SUME_API_KEY"] ?? ""
guard !key.isEmpty else { fatalError("SUME_API_KEY missing") }
var req = URLRequest(url: URL(string: "https://api.sume.com/v1/images")!)
req.httpMethod = "POST"
req.timeoutInterval = 40
req.setValue("Bearer \(key)", forHTTPHeaderField: "Authorization")
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try JSONSerialization.data(withJSONObject: [
"model": "bytedance-seed/seedream-5-lite",
"prompt": "matte ceramic mug on a white sweep, soft shadow",
"aspect_ratio": "16:9",
])
let (data, resp) = try await URLSession.shared.data(for: req)
let code = (resp as? HTTPURLResponse)?.statusCode ?? 0
let text = String(decoding: data, as: UTF8.self)
switch code {
case 200: print("done:", text)
case 202: print("queued, follow status_url:", text)
default: print("error", code, text)
}Notes
URLRequest.timeoutInterval defaults to 60 seconds; the explicit 40 seconds here is mostly documentation, set it to whatever your app can bear above the 30-second sync wait.
On a phone, do not wait on the main actor. Use mode: "async" and poll status_url, so a backgrounded app does not lose a request mid-flight.
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
- Swift withTaskGroup: four Sume video submits in flight
A sliding window in Swift 6: withTaskGroup keeps four POST /v1/videos requests in flight, re-arming as each finishes. One file, URLSession, no packages.
- 10 hooks by 10 endings: a 100-variant grid in one Sume bulk queue
A 10 by 10 hook and ending grid is exactly 100 items, the bulk queue maximum. How to build the items array, pick concurrency up to 16, and read the result.
- A ten-photo edit regression suite to re-run when a model launches
Ten photos, five edits each, one scoring sheet: a cheap test to re-run whenever a new image edit model launches. 50 edits cost $1.875 at the low tier on Sume.
- How many video scenes fit in one Sume script_run? Ten
script_run allows at most 32 paid calls per run. At three paid calls per scene (voice, image, clip) that is ten scenes, with two calls to spare.
Written by Sume