
grok-imagine/image-to-image takes one or more reference images plus a text prompt and returns an edited image — restyle a product photo, swap a background, or blend up to three references into one output. On hiapi it runs through the unified task API: create a task, poll (or get a callback), download the result. This guide walks through the exact request shape, with copy-paste curl and Python that work as written.
Goal: submit a reference image and a prompt, get back an edited image URL, and download it — first interactively with polling, then the production version with callbacks.
You need one thing: a hiapi API key. Grab it from your dashboard — it looks like sk-... and goes in the Authorization: Bearer header on every call.
Your reference images must be publicly reachable URLs (the model fetches them server-side), so host them on your CDN or object storage first.
Create the task:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine/image-to-image",
"input": {
"prompt": "Turn this product photo into a clean studio shot on a seamless white background with soft shadows",
"image_urls": ["https://your-cdn.com/product.jpg"]
}
}'
Two fields are required — prompt and image_urls — and that's a complete request. The response includes a task ID under data.taskId:
{
"data": {
"taskId": "<task-id>"
}
}
Poll for the result:
curl https://api.hiapi.ai/v1/tasks/<task-id> \
-H "Authorization: Bearer sk-<your-key>"
While the task is running, data.status reports progress. When it flips to success, the image URL is at data.output[0].url. If it's fail, data.error has the code and message.
One important detail: output URLs are signed and expire (the payload carries an expireAt). Download the file as soon as the task succeeds — never store or hotlink the raw output URL.
curl -o result.png "<data.output[0].url>"
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<your-key>"}
payload = {
"model": "grok-imagine/image-to-image",
"input": {
"prompt": (
"Turn this product photo into a clean studio shot "
"on a seamless white background with soft shadows"
),
"image_urls": ["https://your-cdn.com/product.jpg"],
"aspect_ratio": "auto",
"resolution": "1k",
"output_format": "png",
},
}
# 1. Create the task
resp = requests.post(API, json=payload, headers=HEADERS, timeout=60)
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]
print("task:", task_id)
# 2. Poll until it reaches a terminal state
while True:
task = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
if task["status"] == "success":
image_url = task["output"][0]["url"]
break
if task["status"] == "fail":
raise RuntimeError(f"task failed: {task.get('error')}")
time.sleep(5)
# 3. Download immediately — output URLs expire
with open("result.png", "wb") as f:
f.write(requests.get(image_url, timeout=120).content)
print("saved result.png")
| Field | Required | Type | Notes |
|---|---|---|---|
prompt | ✅ | string | What to change / the target look |
image_urls | ✅ | array of URLs | 1–3 reference images, must be publicly reachable |
aspect_ratio | optional | enum | auto, 2:1, 20:9, 19.5:9, 16:9, 4:3, 3:2, 1:1, 2:3, 3:4, 9:16, 9:19.5, 9:20, 1:2 |
resolution | optional | enum | 1k or 2k — lowercase |
output_format | optional | enum | jpeg, png, or webp |
The schema is strict: any field it doesn't know is rejected with a 400. There is no size, no seed, no n — if you're porting code from a model that takes pixel dimensions (like size: "1024x1024"), switch to aspect_ratio + resolution here. Each task returns one image; for variations, submit multiple tasks.
image_urls accepts up to three references, and the model blends them — for example, a product shot plus a background plate plus a style reference. Send more than three and the API rejects the request outright:
invalid input: image_urls: maxItems: got 4, want 3
Put the image that matters most (usually the subject) first, and use the prompt to say what each reference contributes.
aspect_ratio: "auto" lets the model derive the output ratio from your reference image instead of forcing a fixed frame — the right default for product photos and edits where you want the composition preserved. Set an explicit ratio only when the destination demands it (e.g. 9:16 for a story placement, 16:9 for a hero banner).
For batch or server-side workloads, skip the polling loop — register a callback when you create the task and hiapi POSTs the terminal result to your endpoint:
{
"model": "grok-imagine/image-to-image",
"input": {
"prompt": "...",
"image_urls": ["https://your-cdn.com/product.jpg"]
},
"callback": {
"url": "https://your-server.com/hooks/hiapi",
"when": "final"
}
}
when supports only "final" — you get one call when the task reaches success or fail, not intermediate progress events.
Rule of thumb: poll for interactive tools (a user is watching; a 5-second sleep loop is fine), use callbacks for pipelines (dozens of tasks in flight; you don't want a poller per task). Your callback handler should still download the output immediately — the URL it receives expires like any other.
The task API separates "submit" from "result", which makes safe retries straightforward:
taskId the moment create returns. If your process crashes afterwards, resume by polling the stored ID — don't re-create.taskId (network error, 5xx). Re-submitting a request that already returned an ID produces a second, separately billed task.status: "fail" as terminal. Fix the input (or route to a fallback) and submit a new task; the failed one won't restart.| What you see | Meaning | Fix |
|---|---|---|
401 — permission_denied | Bad key, or key lacks access to this model | Check the key in your dashboard |
400 INVALID_REQUEST — missing required field "prompt" / "image_urls" | Required input missing | Both fields are mandatory |
400 INVALID_REQUEST — additional properties '...' not allowed | Unknown field (e.g. size, seed, n) | Strict schema — use only the fields in the table above |
400 INVALID_REQUEST — image_urls: maxItems | More than 3 references | Trim to 3 |
404 — task not found | Wrong or foreign taskId on GET | Check the stored ID |
status: "fail" on poll | Generation failed after acceptance | Read data.error, adjust input, submit fresh |
Note the auth failure is a 401 with "code": "permission_denied" in the error body — worth matching on explicitly, since it also covers keys that exist but aren't allowed on this model.
How many reference images can I pass?
Up to 3 in image_urls. The API hard-rejects more (maxItems: got N, want 3).
Can I set the output size in pixels?
No. There's no size field — you control shape with aspect_ratio (14 enum values) and sharpness with resolution (1k or 2k, lowercase). Anything else is a 400.
What does aspect_ratio: "auto" do?
It lets the model follow your reference image's proportions instead of forcing a fixed frame — the usual choice for edits where composition should survive.
How long are result URLs valid?
They're signed URLs with an expiry (expireAt in the output payload). Download the bytes as soon as the task succeeds; don't hotlink them.
Should I poll or use a callback?
Polling every ~5s is fine for interactive use. For batch pipelines, pass callback: {"url": ..., "when": "final"} and handle one POST per task — "final" is the only supported trigger.
Can I get multiple variations in one call?
No — there's no n parameter. Submit one task per variation; they run in parallel anyway.
What's the difference from grok-imagine-quality/image-to-image? Same request shape, higher-fidelity (and higher-cost) rendering tier. Prototype on the standard tier, re-run keepers on quality; compare per-image cost on the pricing page.