Create a task, poll or use a callback, download the image — the exact schema qwen-image-2.0-pro accepts on hiapi, with copy-paste curl and Python.

qwen-image-2.0-pro is the Pro tier of Alibaba's Qwen image family — the variant you reach for when text rendering fidelity and photorealistic detail matter more than per-image cost. On hiapi it runs on the unified async task endpoint (/v1/tasks), so the integration is: create a task, poll (or receive a callback), download the result. This guide gives you a copy-paste curl request, a complete Python script, the exact parameter schema the model accepts today, and the differences from the base qwen-image-2.0.
Goal: send a text prompt to qwen-image-2.0-pro and get back an image file. You need exactly one thing — a hiapi API key (sk-...), which you can create in the dashboard. Every request below authenticates with the same header:
Authorization: Bearer sk-<your-key>
There is no synchronous endpoint for this model — qwen-image-2.0-pro only supports the task interface, so a generation is always two HTTP calls (create, then poll) or one call plus a webhook.
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": "qwen-image-2.0-pro",
"input": {
"prompt": "A storefront window sign that reads \"GRAND OPENING - 50% OFF\" in bold red letters, photorealistic, golden-hour light",
"size": "2048*2048"
}
}'
A successful create returns a task ID:
{"code": 200, "data": {"taskId": "task_..."}}
Poll it until it reaches a terminal state:
curl https://api.hiapi.ai/v1/tasks/<taskId> \
-H "Authorization: Bearer sk-<your-key>"
When data.status becomes success, the image URL is at data.output[0].url. If it becomes fail, data.error tells you why. One important detail: the output URL is temporary (it carries an expireAt), so download the bytes immediately and store them yourself — don't hotlink it.
The task API validates input strictly — unknown keys are rejected with HTTP 400, so this table is exhaustive:
| Field | Required | Notes |
|---|---|---|
prompt | yes | Missing it returns 400: missing required field "prompt". |
size | no | One of 2688*1536, 2368*1728, 2048*2048, 1728*2368, 1536*2688. Note the * separator — 2048x2048 or arbitrary dimensions won't validate. |
negative_prompt | no | What to steer away from. |
seed | no | Integer, for reproducible outputs. |
watermark | no | Boolean; set false to disable the watermark. |
prompt_extend | no | Boolean; lets the backend expand short prompts before generation. Turn it off when you need literal prompt control. |
Three things this model does not accept, which trip people up coming from other models on the platform:
aspect_ratio field. Some hiapi models take aspect_ratio + resolution; the Qwen 2.0 family takes an explicit size from the enum above. Sending aspect_ratio returns 400: additional properties 'aspect_ratio' not allowed.n (batch count). One task produces one image; fan out by creating multiple tasks.qwen-image-2.0-pro is text-to-image only — image_input / reference-image fields are rejected, so it can't be used for image editing.A complete script you can run as-is (only requests needed):
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<your-key>"}
def generate(prompt: str, size: str = "2048*2048", timeout_s: int = 600) -> bytes:
resp = requests.post(
API,
headers=HEADERS,
json={
"model": "qwen-image-2.0-pro",
"input": {"prompt": prompt, "size": size, "watermark": False},
},
timeout=60,
)
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]
deadline = time.time() + timeout_s
while time.time() < deadline:
task = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
if task["status"] == "success":
url = task["output"][0]["url"] # expires — download now, store the bytes
return requests.get(url, timeout=120).content
if task["status"] == "fail":
raise RuntimeError(f"task {task_id} failed: {task.get('error')}")
time.sleep(5)
raise TimeoutError(f"task {task_id} still not terminal after {timeout_s}s")
if __name__ == "__main__":
png = generate(
'A minimalist conference poster, headline "SHIP FASTER" in a bold grotesque, '
"off-white background, single red accent shape"
)
with open("qwen-pro.png", "wb") as f:
f.write(png)
print(f"saved qwen-pro.png ({len(png)} bytes)")
The structure to copy even if you rewrite it in another language: create → poll every ~5s → on success download immediately → treat fail and timeout as distinct errors.
Callbacks instead of polling. For anything beyond a script, register a webhook when you create the task and skip polling entirely:
{
"model": "qwen-image-2.0-pro",
"input": {"prompt": "...", "size": "2048*2048"},
"callback": {"url": "https://your-app.com/hooks/hiapi", "when": "final"}
}
With "when": "final" you get exactly one POST when the task reaches a terminal state. Polling is fine for scripts and low volume; callbacks are better once you have concurrent tasks or a queue, because a fleet of pollers is both slower to react and noisier against your rate limits. Keep a polling fallback for the rare case your webhook endpoint is down.
Idempotency. The create call is not idempotent — retrying a timed-out create can produce two billable tasks. Persist the taskId as soon as you receive it, and key your own job table by a request ID you generate, so a retry checks for an existing task before creating a new one.
Error handling. The API uses two error shapes, worth matching on both:
401 with a nested error object, e.g. {"error": {"code": "permission_denied", "message": "This API key cannot use the selected model...", "type": "hiapi_error"}}. You'll see this for a missing key, an invalid key, or a key without access to this model.400 with a flat shape: {"code": 400, "error_code": "INVALID_REQUEST", "message": "invalid input: size: value must be one of ..."}. The message names the offending field, so log it verbatim.404 with "task not found".Cost. Pro-tier image generation is billed per image at a higher rate than the base model — check current numbers on the pricing page rather than hardcoding them into your budget math.
The two models share an identical request schema — same required prompt, same five-value size enum, same optional fields — so switching between them is literally a one-word change to model. That makes a cheap workflow possible:
seed if you want stability) against qwen-image-2.0-pro for the higher-fidelity output — it's positioned for stronger in-image text rendering and photorealistic detail, which is exactly where the base model is most likely to fall short on final assets.If your images never contain legible text and don't need photorealism, the base model may be all you need — see our qwen-image-2.0 prompt recipes for six tested prompts, and the ecommerce product images walkthrough for a real pipeline built on the base model.
Does the qwen image 2.0 pro API support image-to-image or editing?
No. The /v1/tasks input schema for qwen-image-2.0-pro accepts text fields only; reference-image fields like image_input are rejected with a 400. For editing workflows, pick a model that supports image input.
What image sizes does qwen-image-2.0-pro support?
Exactly five: 2688*1536, 2368*1728, 2048*2048, 1728*2368, 1536*2688 (landscape → square → portrait). Arbitrary dimensions and aspect_ratio values are not accepted.
How do I make outputs reproducible?
Pass an integer seed in input. Same model + prompt + size + seed gives you a stable starting point when you move a prompt from the base model to Pro.
How do I remove the watermark?
Set "watermark": false in input.
Why am I getting 401 permission_denied with a valid key?
The key exists but can't use this model — 401 covers missing keys, malformed keys, and keys without access to qwen-image-2.0-pro. Check the key's model permissions in the dashboard.
Can I generate several images in one request?
No — there's no n parameter. Create one task per image; they run concurrently, so fan-out is just multiple create calls.
How long do output URLs stay valid?
They expire (the task payload includes an expireAt). Download the image as soon as the task succeeds and store it in your own bucket.
Key Takeaways