GPT Image 2.5 curl command: generate and download in a shell

A copy-paste curl call to Sume's POST /v1/images for GPT Image 2.5, with jq to pull the URL, download the file, and a check for the 202 job response.

5 min readSume
All posts

The shortest GPT Image 2.5 call from a shell is one curl to https://api.sume.com/v1/images with model set to openai/gpt-image-2.5 and a prompt, and jq to read the image URL from data[0].url. Download that URL with a second curl. If the response has no data array it is probably a 202 job envelope, which needs a status poll instead.

The script below does the whole round trip, checks for the 202 case, and writes out.png. It uses only curl and jq. Facts are from the Image API docs and Jobs and results, read 2026-10-03.

What is the one-liner?

Export your key as SUME_API_KEY. Sume accepts either Authorization: Bearer or x-api-key. The call below asks for medium quality at 1024x1024; if you leave quality out, Sume uses high.

set -euo pipefail
resp=$(curl -sS -w '\n%{http_code}' https://api.sume.com/v1/images \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-image-2.5","quality":"medium","image_size":"1024x1024","prompt":"A red kite over a green hill, flat illustration"}')
code=${resp##*$'\n'}
body=${resp%$'\n'*}
if [ "$code" = 200 ]; then
  url=$(printf '%s' "$body" | jq -r '.data[0].url')
  curl -sS -o out.png "$url"
  echo "saved out.png"
elif [ "$code" = 202 ]; then
  echo "still running; poll: $(printf '%s' "$body" | jq -r '.data.status_url')"
else
  echo "HTTP $code: $body" >&2; exit 1
fi

Why does the script check the status code?

Sume's docs say to check the status code, not the body shape: 200 is the image response and 202 is the job envelope. The blocking wait is capped at 30 seconds; slower configurations such as 4K, high quality or a larger n are the likeliest to return 202.

On a 202, the job is already running and billed on completion. Poll status_url with the same header until terminal is true, then read result_url. Do not run the original command again.

Which flags and fields do I change most?

These are the knobs you will edit, all taken from Sume's schema.

Common fields for GPT Image 2.5 on POST /v1/images, from Sume's Image API docs, read 2026-10-03.
FieldValues on GPT Image 2.5Note
qualityauto, low, medium, high, xhigh, maxOmitted means high on Sume
image_sizeWIDTHxHEIGHT, both multiples of 16, max edge 3840Aspect at most 3:1, 655,360 to 8,294,400 pixels
output_formatpng, jpeg, webpCheck the model row; svg is listed in the schema
backgroundauto, transparent, opaqueGPT Image 2.5 only
modesync, async, subscribe, webhookAsync always returns the job envelope

How do I edit a photo with curl?

Add input_references with a public HTTPS URL and set aspect_ratio to auto so the output matches the source. Do not set image_size and aspect_ratio together; image_size wins. A local file path will not work, because references must be public HTTPS URLs.

curl -sS https://api.sume.com/v1/images \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-image-2.5","aspect_ratio":"auto","quality":"medium",
       "prompt":"Keep the subject; change the sky to sunset. Change nothing else.",
       "input_references":[{"type":"image_url","image_url":{"url":"https://example.com/photo.jpg"}}]}'

What goes wrong most often?

Most failed first attempts come from a short list.

  • A 400 unsupported_parameter from a field the model does not list, such as seed or output_compression.
  • A custom size with an edge that is not a multiple of 16, for example 1080.
  • A 402 because the workspace balance is too low; no job starts.
  • Pasting the same command again after a 202 and paying for a second image.

How do I poll a 202 from the shell?

Take status_url and result_url from the 202 body, then loop: read status_url with the same header, stop when terminal is true, and only then request result_url. Honour next_poll_after_seconds if the status response includes it, and fall back to a few seconds between reads. Stop on failed or canceled and read the job's error instead of retrying the paid request.

If you plan to run many of these, move to mode: "async" so every call returns the job envelope and your script has one code path. Add an Idempotency-Key header for any call you might repeat after a network failure, and reuse the key only for the identical payload.

A quick way to see what a model accepts before you script it is GET /v1/images/models, which lists the capability descriptors per model. Anything missing from a model's list is rejected with 400 unsupported_parameter, so a thirty-second look saves a failed run.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume