
qwen-image-3.0-pro is Alibaba's newest image-generation tier, and it's live on the hiapi qwen-image-3.0-pro page with usage-based pricing starting from $0.035/image (see pricing for the full breakdown). This guide gets you from zero to a downloaded image in one request, then covers the fields that actually validate, callbacks, and the errors you'll hit in production.
You'll send a text prompt to hiapi's task API, poll until it finishes, and download the resulting PNG from the response. That's the whole loop - qwen-image-3.0-pro is text-to-image by default, though it also accepts an optional reference image if you want to steer the output.
You need one thing: an API key from your hiapi dashboard. Every request below authenticates with Authorization: Bearer sk-<your-key>.
Every hiapi model shares one task endpoint. You create a task, then either poll it or let a callback tell you when it's done.
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-3.0-pro",
"input": {
"prompt": "a ceramic teapot on a wooden table, soft morning light, studio photo"
}
}'
prompt is the only required field - model is the bare model id, no vendor prefix or version suffix. The response is a task id:
{ "data": { "taskId": "01K..." } }
Poll it until data.status reaches a terminal value. In practice you'll see handling while it's running, then success or fail:
curl https://api.hiapi.ai/v1/tasks/01K... \
-H "Authorization: Bearer sk-<your-key>"
{
"data": {
"status": "success",
"output": [
{ "url": "https://temp.hiapi.ai/.../01K....png", "expireAt": "2026-08-12T03:00:00Z" }
]
}
}
output[0].url is a signed, temporary link - it expires (see expireAt). Download the bytes immediately and store them yourself; don't hotlink the temp URL from a live page.
import time
import requests
API_KEY = "sk-<your-key>"
BASE = "https://api.hiapi.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def generate(prompt: str, size: str | None = None) -> bytes:
input_payload = {"prompt": prompt}
if size:
input_payload["size"] = size # "WIDTH*HEIGHT", e.g. "1328*1328"
resp = requests.post(
f"{BASE}/tasks",
headers=HEADERS,
json={"model": "qwen-image-3.0-pro", "input": input_payload},
timeout=30,
)
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]
while True:
poll = requests.get(f"{BASE}/tasks/{task_id}", headers=HEADERS, timeout=30)
poll.raise_for_status()
data = poll.json()["data"]
if data["status"] == "success":
image_url = data["output"][0]["url"]
return requests.get(image_url, timeout=30).content
if data["status"] == "fail":
raise RuntimeError(f"task {task_id} failed: {data}")
time.sleep(2)
if __name__ == "__main__":
png_bytes = generate("a ceramic teapot on a wooden table, soft morning light", size="1328*1328")
with open("teapot.png", "wb") as f:
f.write(png_bytes)
qwen-image-3.0-pro's schema is strict - send a field it doesn't recognize and you get an immediate 400 (additional properties 'X' not allowed), which is a fast, free way to confirm what's real before you spend a request on generation:
| Field | Type | Notes |
|---|---|---|
prompt | string | required |
size | string | "WIDTH*HEIGHT", e.g. "1328*1328". Omit it and the model picks a default. |
negative_prompt | string | things to steer away from |
seed | integer | fix it for reproducible output |
watermark | boolean | |
prompt_extend | boolean | lets the model rewrite/expand a short prompt |
image_urls | array of public HTTPS URLs | reference image(s) for image-guided generation |
Two fields you might expect from other hiapi image models are not on qwen-image-3.0-pro: there's no n (one image per task) and no aspect_ratio (use size instead). Sending either returns additional properties 'n' not allowed / additional properties 'aspect_ratio' not allowed.
For anything beyond a one-off script, skip polling and use a callback instead:
{
"model": "qwen-image-3.0-pro",
"input": { "prompt": "a ceramic teapot on a wooden table" },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
when only supports "final" - you get exactly one POST, when the task reaches success or fail. Make your handler idempotent on taskId: retries at the network layer (yours or hiapi's) can redeliver the same callback, and a client-side retry after a timeout can create a second task for the same logical request, so key your own dedup off taskId rather than assuming one task per prompt.
Common errors:
| Status | Meaning | Fix |
|---|---|---|
400 additional properties 'X' not allowed | field doesn't exist on this model | drop it, check the table above |
400 on size at generation time | invalid WIDTH*HEIGHT value | task fails asynchronously (status: "fail") even though the create call returned 200 - always check the polled/callback status, not just the create response |
401 permission_denied | key can't use this model | check the model is enabled for your key in the dashboard |
task stuck in handling | normal - generation is async | keep polling or wait for the callback; don't resubmit |
Is qwen-image-3.0-pro text-to-image only, or can it do image-to-image too?
It's text-to-image by default, but it accepts an optional image_urls array for image-guided generation - pass one or more public HTTPS URLs alongside your prompt.
What's the difference between qwen-image-3.0 and qwen-image-3.0-pro? They're separate models on hiapi with separate pricing - qwen-image-3.0 starts from $0.025/image, qwen-image-3.0-pro from $0.035/image. Check each model page for current pricing and try both against your prompts if quality-per-dollar matters for your use case.
What happens if I don't pass size?
The request is still valid - size is optional and the model falls back to a default. Pass it explicitly ("WIDTH*HEIGHT", e.g. "1328*1328") when you need a specific dimension.
Why did my task return 200 on create but then fail?
Task creation only validates the request shape. Values like size are validated when the task actually runs, so a malformed size still gets a taskId back before failing asynchronously. Always check the polled or callback status, not just the create response.
How much does qwen-image-3.0-pro cost per image? Pricing is usage-based and can change - see the live number on the pricing page or the model page.