
This guide shows you how to call the seedream 5.0 pro api for text-to-image through hiapi's unified tasks endpoint. Every parameter, enum value, and error message below was verified against the live API — you can copy the requests as-is and they will run.
Goal: submit a text prompt to seedream-5.0-pro/text-to-image, wait for the render, and download the finished image — first with curl, then with dependency-free Python.
You need one thing: a hiapi API key. Grab it from your hiapi dashboard, then export it:
export HIAPI_KEY="sk-your-key-here"
Seedream 5.0 Pro text-to-image runs on the async tasks API: POST /v1/tasks to create, GET /v1/tasks/<id> to poll. The input schema is strict and minimal — exactly three fields, all required:
| Field | Type | Required | Accepted values |
|---|---|---|---|
prompt | string | yes | Your image description. Seedream is strong at rendering literal text — see tips below. |
aspect_ratio | string | yes | 1:1, 4:3, 3:4, 16:9, 9:16, 3:2, 2:3, 21:9 |
resolution | string | yes | 1K, 2K |
Two things that trip people up:
size, n, seed, or image_urls returns 400 INVALID_REQUEST: additional properties ... not allowed. One request = one image; if you want variations, submit multiple tasks.1K/2K; seedream-5.0-lite/text-to-image takes 2K/4K instead, and the image-to-image endpoint adds image_urls and a match_input_image ratio. Don't copy input blocks across models without checking.Create the task:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer $HIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-5.0-pro/text-to-image",
"input": {
"prompt": "A cozy coffee shop storefront at dusk, warm window light, a hand-painted wooden sign that reads \"THE DAILY GRIND\" in serif letters",
"aspect_ratio": "16:9",
"resolution": "2K"
}
}'
A successful create returns the task id at data.taskId:
{
"code": 200,
"data": {
"taskId": "task_abc123..."
}
}
Poll for the result (a 2K render typically takes on the order of a minute):
curl "https://api.hiapi.ai/v1/tasks/task_abc123..." \
-H "Authorization: Bearer $HIAPI_KEY"
While the task is running, data.status reports an in-progress state. It ends in one of two terminal states: success or fail. On success the image URL is at data.output[0].url:
{
"code": 200,
"data": {
"taskId": "task_abc123...",
"status": "success",
"output": [
{ "url": "https://.../image.png?...expireAt=..." }
]
}
}
Download immediately. The output URL is signed and carries an expireAt — treat it as a temporary handle, not permanent storage. Save the bytes to disk or push them to your own bucket as soon as the task succeeds:
curl -o result.png "<the output url>"
No SDK needed — urllib from the standard library covers it:
import json
import os
import time
import urllib.request
API = "https://api.hiapi.ai/v1/tasks"
KEY = os.environ["HIAPI_KEY"]
def api(url, payload=None):
req = urllib.request.Request(
url,
data=json.dumps(payload).encode() if payload else None,
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json",
},
method="POST" if payload else "GET",
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
# 1. Create the task
created = api(API, {
"model": "seedream-5.0-pro/text-to-image",
"input": {
"prompt": ('A minimalist product poster on matte black, '
'bold white headline that reads "SHIP FASTER", '
'small caption "batch v2.1" bottom right'),
"aspect_ratio": "3:4",
"resolution": "2K",
},
})
task_id = created["data"]["taskId"]
print("task:", task_id)
# 2. Poll until terminal state
while True:
task = api(f"{API}/{task_id}")["data"]
if task["status"] == "success":
break
if task["status"] == "fail":
raise RuntimeError(f"task failed: {task}")
time.sleep(5)
# 3. Download before the signed URL expires
url = task["output"][0]["url"]
urllib.request.urlretrieve(url, "result.png")
print("saved result.png")
Seedream 5.0 is one of the strongest model families for literal in-image text (signs, labels, UI copy, packaging). What works in practice:
a sign that reads "OPEN 24 HOURS". Quoted strings render far more reliably than paraphrased ones.2K when text is the point — smaller glyphs survive better at higher resolution.For a deeper set of worked prompt patterns on the lite tier (same family, same prompting behavior), see Seedream 5.0 Lite prompt recipes.
Callback instead of polling. For servers, add a callback block at the top level of the create payload (next to model/input, not inside input) and hiapi will POST to your endpoint when the task reaches a terminal state:
{
"model": "seedream-5.0-pro/text-to-image",
"input": { "...": "..." },
"callback": { "url": "https://your.app/hooks/hiapi", "when": "final" }
}
Rule of thumb: polling is fine for scripts and batch jobs (poll every ~5s); callbacks are better for user-facing flows where you don't want a worker parked on a minute-long wait. In the callback handler, re-fetch GET /v1/tasks/<id> and trust that response rather than acting on the callback body alone.
Idempotency. Creating a task costs money the moment it's accepted, so a crash-then-retry loop can double-bill you. Persist your own (job → taskId) mapping before you retry: if a taskId already exists for the job, resume polling it instead of creating a new task.
Errors you'll actually see (all reproduced live):
| Symptom | Response | Fix |
|---|---|---|
| Missing a required field | 400 INVALID_REQUEST — missing required field "aspect_ratio" | Send all three: prompt, aspect_ratio, resolution |
| Bad enum value | 400 — resolution: value must be one of '1K', '2K' | Use the table above; enums differ per endpoint |
Extra fields like size, n, seed | 400 — additional properties not allowed | Strict schema: exactly the three documented fields |
| Invalid or unauthorized key | 401 — {"error":{"code":"permission_denied","type":"hiapi_error",...}} | Check the key in your dashboard; the response includes a request_id for support |
Cost is flat per image and differs by resolution (1K vs 2K) — current numbers are on the hiapi pricing page. Useful next stops:
Can I generate multiple images in one request?
No. The schema rejects n (additional properties not allowed). Submit one task per image — tasks run asynchronously, so firing several in parallel is the intended pattern.
Can I set a seed for reproducibility?
No, seed is not accepted on this endpoint. If you need consistent variations of an existing image, use seedream-5.0-pro/image-to-image with your reference image instead.
What's the difference from seedream-5.0-lite/text-to-image?
Lite is the cheaper tier and accepts 2K/4K resolutions (not 1K); Pro accepts 1K/2K. The rest of the calling pattern — tasks API, prompt/aspect_ratio/resolution — is the same shape. Compare costs on the pricing page.
How long do output URLs stay valid?
They're signed URLs with an embedded expireAt. Don't hotlink them — download the image as soon as the task succeeds and serve it from your own storage.
Why am I getting 401 permission_denied with a valid-looking key?
The key may not have access to this model group, or it may be exhausted. The 401 body includes a request_id — check your key settings in the dashboard and include that id if you contact support.
Do I need a callback URL to use the API?
No — callbacks are optional. Polling GET /v1/tasks/<id> works fine and is simpler for scripts; callbacks just save you the wait loop in server environments.