GPT Image 2.5 image URL expired? Save the file in Node
Sume returns GPT Image 2.5 results as signed URLs, not base64, and its docs give no lifetime. Download each file when the call returns. Node example.

A GPT Image 2.5 result from Sume is a URL in data[].url, not inline base64, and the docs call that URL signed. They do not state how long it stays valid, so do not store the link as your record of the image: download the file as soon as the call returns and keep your own copy.
This comes from the Image API docs, read 2026-09-29. It applies to POST /v1/images results. Format runs and job artifacts are a different case, covered in Do Sume video URLs expire.
What does the response give me?
A 200 response carries data, an array with one entry per image. Each entry has a url on media.sume.com and a media_type such as image/png. The file extension follows the output_format you asked for: png, jpeg or webp on the GPT Image 2.5 ids.
| Field | Meaning |
|---|---|
data[].url | Sume-hosted, signed URL of one generated image |
data[].media_type | For example image/png or image/webp |
usage.cost | The USD amount billed to your wallet |
Status 202 | No image yet: a job envelope with status_url and result_url |
How do I save the file in Node?
Fetch the image, check the status, then fetch each URL and write the bytes. The script stops on a 202 because that body is a job envelope, not an image; poll the status_url in that case.
import { writeFile } from "node:fs/promises";
const res = await fetch("https://api.sume.com/v1/images", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "openai/gpt-image-2.5",
prompt: "a red panda astronaut floating in space, studio lighting",
output_format: "png",
}),
});
if (res.status === 202) throw new Error("Job started: poll status_url");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data } = await res.json();
for (const [i, image] of data.entries()) {
const file = await fetch(image.url);
if (!file.ok) throw new Error(`Download failed: ${file.status}`);
await writeFile(`image-${i}.png`, Buffer.from(await file.arrayBuffer()));
}What if the call returned 202?
Then the render passed the 30-second wait. Poll GET /v1/jobs/{id}/status, then read GET /v1/jobs/{id}/result for the images. The result uses the standard job result shape, not the image body above, so parse it separately. See GPT Image 2.5 request returned 202.
What if I ask for several images or another format?
Each image in data has its own URL, so the loop above saves them all; billing is per image, and cost_usd × n is what you pay. The example hard-codes .png because it asks for png. If you request webp or jpeg, choose the extension from each entry's media_type instead of assuming one.
The docs do not describe an extra header for downloading a result URL, and they give no way to refresh one, which is one more reason to save the file at once.
Why not just store the URL?
Three reasons, all from how the route is documented.
- The docs describe the URL as signed and give no lifetime, so a saved link may stop working.
- Your own storage gives you a file you can re-serve, resize or hand to another tool.
- Anything you later send back to Sume as an image reference must be a public HTTPS URL, so host your copy where it can be fetched.
Sources
Related posts
More in Developers
- GPT Image 2.5 transparent background: how to request a cutout by API
GPT Image 2.5 on Sume accepts background transparent. OpenAI says transparency needs png or webp output. The request, the catch, and when to use another route.
- GPT Image 2.5 400 unsupported_parameter: fields each model accepts
A 400 unsupported_parameter from Sume's image API means the model does not list that field. Which GPT Image ids accept quality, mask_url and background.
- Grok 4.7 remote MCP tool: connect Sume to the xAI Responses API
xAI's remote MCP tool works with grok-4.7 on the Responses API. Point server_url at Sume's hosted MCP, restrict allowed_tools, and cap spend on the Sume side.
- HeyGen API avatar ID and voice ID: where to find them
In HeyGen's v3 API, avatar_id is a look id from GET /v3/avatars/looks, and voice_id comes from GET /v3/voices or the look's default voice.
Written by Sume