Image-to-image and reference-image models on HiAPI read your source image from a publicly reachable HTTP(S) URL — not from a base64 blob, and not from a private address. The trap is that a bad image input usually looks like it worked: POST /v1/tasks returns 200 with a taskId, and the task only fails when you poll it. The two failures you'll actually see are:
{ "code": 200, "message": "success",
"data": { "status": "fail", "model": "gpt-image-2/image-to-image", "storage": "temp", "taskId": "tk-...",
"error": { "code": "INVALID_REQUEST",
"message": "The image URL could not be downloaded. Please provide a reachable public HTTP(S) URL and try again." } } }
{ "code": 200, "message": "success",
"data": { "status": "fail", "model": "gpt-image-2/image-to-image", "storage": "temp", "taskId": "tk-...",
"error": { "code": "TASK_FAILED", "message": "task failed" } } }
The first is what an unreachable URL returns. The second is the generic result you get from a base64 data URI — no hint, just task failed. Note the outer "message": "success" only means the poll request itself succeeded; the real outcome is in data.status.
What's actually going wrong (most common first)
- You pasted base64 — a
data:image/...;base64,...string — into the image field. The field expects a URL the service can fetch, not inline bytes. Ongpt-image-2/image-to-imagea data URI is accepted at submit, then the task endsstatus: failwithTASK_FAILED/task failed. - The URL isn't publicly reachable.
http://localhost,127.0.0.1, a LAN IP, a file behind a login, or an expired pre-signed link all fail withINVALID_REQUEST— "The image URL could not be downloaded…". - You used the wrong field name for that model. The image-input field is not the same across families (see the table). A wrong name is rejected at submit with HTTP
400—additional properties '<field>' not allowed. - You used the bare model id for an edit. The bare id (for example
gpt-image-2) is text-to-image only and defines no image field, so an image field is rejected:400 invalid input: <root>: additional properties 'input_urls' not allowed. Use the image-to-image route id instead (gpt-image-2/image-to-image).
The image-input field name varies by model
Confirmed against each model's current input schema on 2026-10-13 — always check the model's own page, because the field differs by family:
| Model id (image-to-image) | Image-input field |
|---|---|
gpt-image-2/image-to-image | input_urls |
seedream-5.0-pro/image-to-image | image_urls |
Nano-Banana-Pro | image_input |
Put the field inside input, and pass it as an array of strings where the schema says an array (most reference-image models take a list).
Fix checklist
- Host the image at a public HTTP(S) URL. Upload it to any object store or CDN (Cloudflare R2, S3, GCS, etc.) and make the object public, or use a pre-signed URL that is still valid.
- Confirm it's reachable with no auth. From a clean session (or a machine that isn't yours):
curl -sI "<your-url>"should return200and animage/*content type. If it needs a cookie, header, or VPN, the task can't fetch it. - Use the image-to-image model id, not the bare one. For an edit, call
gpt-image-2/image-to-image(or the i2i route for your model), notgpt-image-2. - Use the model's documented field name from the table above (
input_urls/image_urls/image_input), insideinput. - Never send base64. If all you have is bytes, upload them first and send the resulting URL.
- Submit, then poll.
GET /v1/tasks/<taskId>untildata.statusissuccessorfail. Onfail, readdata.error.message— "could not be downloaded" points at the URL, not your prompt.
Minimal verification
First, a zero-cost check that your key and the endpoint are wired up. Listing models creates no task and costs nothing:
curl -s https://api.hiapi.ai/v1/models \
-H "Authorization: Bearer $HIAPI_API_KEY"
A 200 with a data array means your key works. A bad key returns 401 with {"error":{"code":"permission_denied", ... ,"type":"hiapi_error"}} — that's an auth problem, not an input problem. Now a correct image-to-image request — a reachable public URL in the model's field:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2/image-to-image",
"input": {
"prompt": "make the background a clean studio white",
"input_urls": ["https://your-public-host.example.com/source.png"]
}
}'
Replace the input_urls value with your own reachable image URL. A successful submit returns data.taskId; poll GET https://api.hiapi.ai/v1/tasks/<taskId> until data.status is success, then read the result from data.output[0].url.
Related reading
- How to use gpt-image-2 image-to-image — a full working i2i request and response.
- HiAPI 400 Bad Request: invalid parameter errors — when a wrong field name or model id is rejected at submit, before a task runs.
- Are failed HiAPI requests charged? — billing for a task that fails after it starts.
- Pick a model: image inpainting & object removal and best image-to-image APIs.
- Reference: gpt-image-2 and Nano Banana Pro model pages, the API docs, pricing, and your dashboard for a key.
FAQ
Can I send a base64 image to a HiAPI image-to-image model?
No. The image field takes a public HTTP(S) URL that the service downloads. A data:image/...;base64,... string is accepted at submit on some models, but the task then ends status: fail with TASK_FAILED / task failed. Upload the bytes somewhere public and send the URL.
My POST /v1/tasks returned 200 but the task failed — why?
A 200 at submit only means the request was accepted. The image download and generation happen afterward. Poll GET /v1/tasks/<taskId>; a bad image input surfaces there as data.status: fail with error.code INVALID_REQUEST ("The image URL could not be downloaded…") or TASK_FAILED.
Which field do I put the image URL in?
It depends on the model. gpt-image-2/image-to-image uses input_urls, seedream-5.0-pro/image-to-image uses image_urls, and Nano-Banana-Pro uses image_input. Check the model's page for its exact schema before you send.
Does a localhost or private URL work?
No. HiAPI fetches the image from its own servers, so localhost, 127.0.0.1, LAN addresses, links behind a login, and expired pre-signed URLs all fail with "The image URL could not be downloaded." Use a URL that resolves publicly with no auth.
I get additional properties 'input_urls' not allowed — what is that?
You sent an image field to a model that doesn't define it — usually the bare text-to-image id (gpt-image-2) instead of the image-to-image route (gpt-image-2/image-to-image), or a field name from a different model. That's a submit-time 400; see the 400 Bad Request guide above.
Does a failed image task cost credits?
A request rejected at submit with 400 isn't billed, but a task that is accepted (200 + taskId) and then fails can be — see "Are failed HiAPI requests charged?" So fix the URL before you re-run.



