Hy Image 3.5 Preview returns base64 PNG; Sume returns a URL
Moving image code from a base64 PNG response (Hy Image 3.5 Preview on OpenRouter) to Sume means downloading data[].url. A 12-line Python version.

Tencent's Hy Image 3.5 Preview, as listed on OpenRouter, returns the image as base64 PNG. Sume's POST /v1/images works differently: the response holds data[].url, a Sume-hosted signed URL, and no inline base64. If your code decodes base64 today, the change is to download the URL instead. Sume does not list Hy Image 3.5 Preview, so the example below calls a model that Sume does list.
What each side returns
The OpenRouter listing for Hy Image 3.5 Preview (read 2026-10-08) says the output is an image in PNG format via base64, and that the response carries usage.cost, the USD charge for the call. Sume's Image API docs say the result payload is data[].url, not inline base64, because Sume already mirrors generated media and URLs keep responses small. In Sume's usage object, cost is the billed USD amount and token counts are 0 in v1.
| Item | Hy Image 3.5 Preview (OpenRouter listing) | Sume /v1/images |
|---|---|---|
| Image bytes | Base64 PNG in the response | Signed Sume-hosted URL in data[].url |
| Cost field | usage.cost, USD charge for the call | usage.cost, billed USD amount |
| Token counts | Not stated on the page | 0 in v1 |
| Reference images | Up to 20 | Per model, from the input_references descriptor |
| Slow calls | Not stated on the page | 200 with the image, or 202 with a job envelope |
Download the URL
The code posts a request, treats 202 as a job, and saves the file. The signed URL is the only place the bytes live, so fetch it soon after the job completes. The Content-Type header tells you the extension to use.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
body = {
"model": "bytedance-seed/seedream-5-lite",
"prompt": "A paper boat on a calm lake, morning light",
"aspect_ratio": "1:1",
}
r = requests.post("https://api.sume.com/v1/images", headers=H, json=body, timeout=60)
r.raise_for_status()
if r.status_code == 202:
raise SystemExit("job envelope: poll the status_url from the body")
url = r.json()["data"][0]["url"]
img = requests.get(url, timeout=60)
ext = img.headers.get("content-type", "image/png").split("/")[-1]
open(f"out.{ext}", "wb").write(img.content)
print("saved", len(img.content), "bytes as", ext)Handle the 202 case
Examine the status code, not the body shape. Sume's docs say 200 is the image response and 202 is the job envelope, and that slow configurations (4K, high quality, large n) are the most likely to degrade to 202. A 202 body carries status_url and result_url. Poll the status URL until the job is terminal, then fetch the result, which holds the artifacts with content_type and url.
If your pipeline expects bytes in the first response, wrap the call in a function that returns bytes after either path. The submit-then-poll pattern also works with a webhook for the terminal event, which means no polling loop is needed.
What not to carry over
Do not port a request field just because it exists upstream. Sume rejects a parameter that the selected model does not list with 400 unsupported_parameter, rather than ignoring it. Read GET /v1/images/models for the row you chose, and send only the fields in its supported_parameters.
Sources
Related posts
More in Developers
- One idempotency key per prompt row: re-run a 1,000-clip library safely
Derive the Idempotency-Key from every field you send, so a crashed batch can restart without paying twice and a changed row never collides. Python, 17 lines.
- Image 1.0 defaults to low quality; GPT Image 2.5 defaults to high
Same prompt, two Sume routes, two defaults: Image 1.0 omits quality as low, POST /v1/images with GPT Image 2.5 omits it as high. Set quality in both.
- Image 1.0 to POST /v1/images: image_urls becomes input_references
Move a Sume Image 1.0 request to POST /v1/images: image_urls to input_references, mask_image_url to mask_url, num_images to n, plus new defaults.
- Image API returned 202 after 30 seconds: a Python poll that finishes
POST /v1/images waits 30 seconds, then returns 202 with a job envelope for slow high-quality runs. A 25-line Python script that handles both 200 and 202.
Written by Sume