Image reference URL rejected on Sume: localhost, http, private hosts
Sume's Image API rejects localhost, private-network and non-HTTPS reference URLs before submission. A pre-flight check in Python and what to host instead.

If a reference image or mask_url is rejected by Sume's Image API, the cause is usually the URL: reference URLs must be public HTTPS. Localhost, private-network and non-HTTPS URLs are rejected before the generation is submitted, so no image is made and none is billed. Host the file at a public HTTPS address and send that.
The rule is stated in Sume's Image API page ("Reference URLs must be public HTTPS") and repeated for other launch workflows on the Media inputs page, which also lists signed or private URLs and mismatched content types as rejected there.
Which URLs fail?
The Image API page names three failing kinds. The Media inputs page adds two more for the workflows it covers (Avatar, face swap and captions). For images, treat a short-lived signed link as a risk and test it before building on it.
| URL | Result | Why |
|---|---|---|
| https://cdn.example.com/a.jpg | Accepted if fetchable | Public HTTPS |
| http://cdn.example.com/a.jpg | Rejected | Not HTTPS |
| https://localhost:3000/a.jpg | Rejected | Localhost |
| https://192.168.1.20/a.jpg | Rejected | Private network |
| A presigned bucket link | Test first | Media inputs lists signed/private URLs as rejected for its workflows |
How do I catch it before the request?
This function checks the scheme and host of every reference and mask URL, so a bad address fails in your code with a clear message. It does not fetch the file, so it cannot tell whether the host is reachable from the internet; it only catches the documented classes.
import ipaddress
from urllib.parse import urlparse
def check_public_https(url: str) -> None:
u = urlparse(url)
if u.scheme != "https":
raise ValueError(f"not https: {url}")
host = (u.hostname or "").lower()
if not host or host == "localhost" or host.endswith(".local"):
raise ValueError(f"local host: {url}")
try:
ip = ipaddress.ip_address(host)
except ValueError:
return # a DNS name; reachability is not checked here
if ip.is_private or ip.is_loopback or ip.is_link_local:
raise ValueError(f"private address: {url}")
for url in ("https://cdn.example.com/a.jpg", "http://x.test/a.jpg",
"https://localhost:3000/a.jpg", "https://192.168.1.20/a.jpg"):
try:
check_public_https(url)
print("ok ", url)
except ValueError as e:
print("rejected", e)Where should the file live instead?
Any public HTTPS host you control works: an object-storage bucket with a public object, a CDN path, or your website's media folder. The Sume CLI can also register an asset from a public source URL with sume assets create --source-url, but the CLI docs say to prefer public HTTPS URLs in generation payloads unless you need the asset lifecycle.
Does the same rule apply to the mask?
mask_url is documented as an optional public HTTPS mask URL for GPT Image 2.5 edits, so the same check applies. Run it on every URL in input_references and on mask_url. For how the mask behaves, see mask_url inpainting.
When a request still fails with a valid URL, read the error body first. A failed generation is not billed.
How should I handle many references in one request?
GPT Image 2.5 accepts up to 16, so run the check over the whole list and report every bad URL at once instead of stopping at the first. A single failing URL fails the request, and fixing them one at a time costs you round trips, not money, since nothing is billed for a request rejected before submission.
Also check that the content type matches an image. The Media inputs page lists mismatched content types as rejected for its workflows. A URL that serves an HTML page instead of an image file will not pass that check.
Sources
Related posts
More in Developers
- Sume job response: status_url, result_url, events_url, cancel_url
A Sume submit returns four URLs: status_url to poll, result_url once result_ready is true, events_url for the timeline, cancel_url while cancelable is true.
- Sume job status: queue.state, a null position, worker_heartbeat
Why queue.position is null on a Sume job status, what queue.state and worker_heartbeat report, and what a poller should do when a job sits in the queue.
- Sume job usage_summary: reserved, captured, refunded, final
Read usage_summary on a Sume job: status reserved, captured or refunded, amounts in micros, the final flag, and why dollars are micros divided by 1,000,000.
- Sume job webhook_delivery: attempts, exhausted, redeliveries
Read the webhook_delivery object on a Sume job: status, attempts of 10, last_status_code, manual_redeliveries, and what to do when delivery is exhausted.
Written by Sume