Tutorial
How to Use gpt-image-2.5-sunburst/image-to-image via the HiAPI API: curl and Python
A working request, reference-image upload flow, and error triage for developers
On this page
What image-to-image does on this endpoint
The gpt-image-2.5-sunburst/image-to-image endpoint follows a simple contract: you send a reference image plus a natural-language edit instruction, and the model returns a new image that applies that instruction while staying anchored to the reference. Unlike text-to-image, where the model starts from nothing and relies entirely on your prompt, image-to-image uses the reference as visual context — composition, subject, style, and color cues are carried forward unless your instruction explicitly changes them.
This makes it the right choice when you already have an asset and want a controlled transformation: restyling a product photo, changing lighting or background, adjusting a subject's appearance, or generating variants of an existing concept. Choose text-to-image when there is no source asset and you want maximum creative range; choose image-to-image when fidelity to an existing image matters more than novelty.
The key mental model for developers: the reference image is an input constraint, and the edit instruction is the delta. A good instruction describes what should change, not everything that should exist. "Replace the background with a clean studio gradient, keep the subject and its shadows" works better than a full scene description, because the reference already supplies the scene. Keep this in mind as you read the request anatomy below — the payload is small, but the instruction quality drives the output quality.
Prerequisites and request anatomy
Before your first call, make sure you have three things ready:
- An API key issued from your HiAPI account. Keep it in an environment variable rather than in source code.
- The base URL for the HiAPI API, as shown in your dashboard or the API reference.
- The endpoint path:
gpt-image-2.5-sunburst/image-to-image.
Every request is a POST with a JSON body and an Authorization header carrying your key. Conceptually the body has three logical parts:
- Model selection — the model identifier for this endpoint, so the gateway routes the call correctly.
- Inputs — the reference image (as a hosted URL or a base64 data string) and the edit instruction text.
- Output controls — any optional knobs the endpoint documents, such as size or count. Only use parameters that appear in the official reference; do not guess names.
A note on discipline: because this is a hosted endpoint, the exact accepted fields are defined by the current API reference, not by assumptions from other image APIs. When in doubt, send the minimal valid body first (model, image, instruction), confirm a 200 response, then add optional fields one at a time. This keeps failures attributable to a single change instead of a payload you assembled all at once.
Sending a reference image and edit instructions
There are two supported ways to supply the reference image, and the trade-off is mostly about where the bytes live.
Hosted URL
If the image is already publicly reachable, pass its URL. This keeps the request body tiny and fast to serialize, and it is the easiest option during development. The URL must be directly fetchable by the service — not behind auth, not a redirect chain, and not an HTML page that happens to contain an image.
Base64 data
If the image is local or private, encode it and send it inline. Base64 inflates the payload by roughly a third, so large source images can push the request toward size limits. Downscale before encoding if you are near the edge.
Format and size pitfalls
- Stick to mainstream formats (PNG, JPEG, WebP) and make sure the file extension matches the actual bytes.
- Very large images are the most common cause of rejected requests. Prefer a reasonably sized source; you rarely need a full-resolution asset to drive an edit.
- If you build the base64 string yourself, include the data URI prefix only if the API reference shows it in the examples — otherwise strip it. Mixing the two conventions is a frequent source of "invalid image" errors.
Writing the edit instruction
The instruction is where output quality is won or lost. Be specific about the change and explicit about what to preserve. State the target ("the jacket"), the transformation ("recolor to deep navy"), and the constraint ("keep the model's pose and the existing shadows"). Avoid stacking many unrelated edits in one instruction — split them into separate calls if you need independent changes, which also makes each result easier to evaluate.
A working curl request
Below is a complete request you can run from a terminal. Replace the key and the image placeholder with your own values.
curl -X POST "$HIAPI_BASE_URL/gpt-image-2.5-sunburst/image-to-image" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2.5-sunburst/image-to-image",
"image": "https://your-cdn.example.com/reference.png",
"prompt": "Replace the background with a soft neutral studio gradient; keep the subject, its pose, and the existing shadows unchanged."
}'
Flag by flag:
-X POST— the operation creates a new image, so it is a POST.- The URL combines your base URL with the endpoint path; keeping the base URL in an environment variable makes it easy to switch environments.
Authorization: Bearer ...— your API key. Export it asHIAPI_API_KEYso it never appears in shell history or logs.Content-Type: application/json— the body is JSON; omitting this header is a common cause of confusing 4xx responses.model— the endpoint identifier. Keep it exactly as documented.image— the reference, here as a hosted URL. To use base64 instead, swap in the data string or your local encoding variable.prompt— the edit instruction. Keep it to the change plus what to preserve.
If you want to test with a local file without hand-encoding, most shells let you build the JSON with a small script or jq; the important part is that the final body is valid JSON with those fields.
The same request in Python
The Python version keeps the payload identical, so you can compare responses side by side. Using requests:
import os
import requests
base_url = os.environ["HIAPI_BASE_URL"]
api_key = os.environ["HIAPI_API_KEY"]
payload = {
"model": "gpt-image-2.5-sunburst/image-to-image",
"image": "https://your-cdn.example.com/reference.png",
"prompt": (
"Replace the background with a soft neutral studio gradient; "
"keep the subject, its pose, and the existing shadows unchanged."
),
}
response = requests.post(
f"{base_url}/gpt-image-2.5-sunburst/image-to-image",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json=payload,
timeout=120,
)
response.raise_for_status()
print(response.json())
Two things to notice. First, json=payload sets the content type and serializes the body for you, which removes a whole class of hand-built JSON bugs. Second, timeout=120 is deliberate: image generation is not instantaneous, and a client that waits forever is worse than one that fails cleanly. Tune the timeout to your workload, but always set one.
To switch to a local file, read the bytes, base64-encode them, and assign the result to image. For hosted URLs, keep the string as-is. Once both the curl and Python calls return the same shape, you have confirmed parity and can build on either.
Troubleshooting common request failures
Most failures fall into four classes. Work through them in this order, because each one can masquerade as the next.
Authentication errors
A 401 or 403 almost always means a missing, malformed, or expired key. Check that the header is exactly Authorization: Bearer <key>, that there are no stray quotes or newlines from copy-paste, and that the environment variable is actually set in the shell or process running the request. A key that works in curl but not in Python usually points at an unset variable, not a bad key.
Malformed payload
A 400 with a validation message usually means the JSON is invalid or required fields are missing. Pretty-print the body before sending, confirm model matches the endpoint identifier, and confirm the edit instruction field is named as documented. Trailing commas and single quotes are the classic culprits when you hand-write JSON in curl; Python's json= avoids them.
Invalid or oversized image input
If the service rejects the image, verify three things: the URL is publicly fetchable and returns an image (not a redirect or an error page), the format is supported, and the payload is not larger than the documented limit. For base64 inputs, confirm the encoding is clean and that the data URI prefix convention matches the reference examples. When in doubt, downscale and retry — a smaller source image resolves a surprising share of these errors.
Timeouts and transient failures
Long-running generations can exceed client timeouts, and 429 or 5xx responses indicate rate limiting or transient server issues. Distinguish these from client errors: do not retry a 400 unchanged, but a 429 or 503 is a candidate for a backoff retry. Log the status code and response body for every failure — a clear error log turns triage from guesswork into a checklist.
From script to reusable client
A one-off script proves the endpoint works; a small wrapper makes it survive in a pipeline. Wrap the call in a function that owns three things: the request, a timeout, and a bounded retry policy.
import os
import time
import requests
class ImageEditClient:
def __init__(self, base_url: str, api_key: str, timeout: int = 120):
self.base_url = base_url.rstrip("/")
self.api_key = api_key
self.timeout = timeout
def edit(self, image: str, prompt: str, retries: int = 3):
url = f"{self.base_url}/gpt-image-2.5-sunburst/image-to-image"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
payload = {
"model": "gpt-image-2.5-sunburst/image-to-image",
"image": image,
"prompt": prompt,
}
last_error = None
for attempt in range(retries):
try:
resp = requests.post(
url, headers=headers, json=payload, timeout=self.timeout
)
except requests.Timeout as err:
last_error = err
time.sleep(2 ** attempt)
continue
if resp.status_code in (429, 500, 502, 503, 504):
last_error = RuntimeError(f"transient {resp.status_code}: {resp.text}")
time.sleep(2 ** attempt)
continue
if resp.status_code >= 400:
raise RuntimeError(f"request failed {resp.status_code}: {resp.text}")
return resp.json()
raise RuntimeError(f"exhausted retries: {last_error}")
The design choices matter more than the code. Retries apply only to timeouts and transient status codes, never to 4xx client errors, because retrying a malformed payload just burns quota. Exponential backoff keeps a rate-limited pipeline from hammering the endpoint. Client errors raise immediately with the response body attached, so the caller sees exactly what the service objected to. And keeping the model identifier in one place means a future switch to another image endpoint is a single-line change rather than a hunt through the codebase.
From here, add structured logging around each attempt and a size/shape check on the response before downstream consumers touch it. When you are ready to explore other image models on the same gateway, the HiAPI model catalog is the place to compare endpoints without rewriting your client.
Related articles
View all articles →-
Tutorial
How to Call kimi-k3.1-pro via the HiAPI API: curl, Python, and a Working Request
A hands-on integration guide covering endpoints, request parameters, streaming, tool calls, and error triage
18 min read -
Tutorial
ElevenLabs Music API on HiAPI: Generate Music with curl, Python, and Prompt Recipes
A hands-on tutorial for calling elevenlabs/music-v2.5 through HiAPI, plus copy-paste prompt recipes for style, mood, and duration
12 min read -
Tutorial
How to Use gemini-omni-1.2-flash via the HiAPI API: curl and Python
13 min read