What you'll build
A working integration that calls the hiapi seedream-5.0-flash image model from curl and from Python: submit a text-to-image (or image-edit) request, poll for the result, and download the output URL. By the end you'll have a script you can drop into your own project.
Prerequisites
- A hiapi account and API key. Grab one from the hiapi dashboard — keys look like
sk-.... curlor Python 3.8+ (pip install requests).- No image-editing library required —
seedream-5.0-flashaccepts a plain prompt, or a prompt plus an input image URL for editing.
Full model reference: seedream-5.0-flash on hiapi and the API doc page.
Minimal runnable example
hiapi exposes every generation model through one unified endpoint: you create a task, then poll it (or get a callback) until it finishes.
curl
# 1. Create the task
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-5.0-flash",
"input": {
"prompt": "a minimalist ceramic teapot on a wooden table, soft morning light, product photography",
"size": "2K"
}
}'
# => {"code":0,"data":{"taskId":"task_xxx","status":"pending", ...}}
# 2. Poll until it finishes
curl -s https://api.hiapi.ai/v1/tasks/task_xxx \
-H "Authorization: Bearer sk-<your-api-key>"
# => {"code":0,"data":{"taskId":"task_xxx","status":"success",
# "output":[{"url":"https://...signed-url...","expireAt":"..."}]}}
Python
import time
import requests
API_KEY = "sk-<your-api-key>"
BASE_URL = "https://api.hiapi.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def generate_image(prompt: str, size: str = "2K") -> str:
create = requests.post(
f"{BASE_URL}/tasks",
headers=HEADERS,
json={"model": "seedream-5.0-flash", "input": {"prompt": prompt, "size": size}},
timeout=30,
)
create.raise_for_status()
task_id = create.json()["data"]["taskId"]
while True:
poll = requests.get(f"{BASE_URL}/tasks/{task_id}", headers=HEADERS, timeout=30)
poll.raise_for_status()
data = poll.json()["data"]
if data["status"] == "success":
return data["output"][0]["url"]
if data["status"] == "fail":
raise RuntimeError(f"task failed: {data}")
time.sleep(2)
if __name__ == "__main__":
image_url = generate_image("a minimalist ceramic teapot on a wooden table, soft morning light")
print(image_url)
Notes on the input object:
prompt(required, 1–5000 chars) — what to generate, or what to change if you also passimage.image(optional, string or array of up to 10 URLs/base64data:image/...URIs) — pass this to edit/reference an existing image instead of generating from scratch. Omit it for pure text-to-image.size(optional, default"2K") — one of the presets1K/1.5K/2K, or a customWIDTHxHEIGHTstring like"1536x1024"(921,600–4,624,220 total pixels, aspect ratio between 1:16 and 16:1).output_format(optional) —"jpeg"or"png".background(optional, default"opaque") — set"transparent"only when editing an image that has an alpha channel.
The response output[].url is a signed, expiring link (expireAt field) — download or re-host it promptly rather than storing the hot URL long-term.
Production patterns
Use a callback instead of polling
For server-to-server integrations, skip polling and let hiapi notify you when the task finishes. Add a top-level callback object alongside model/input (not nested inside input):
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-5.0-flash",
"input": {"prompt": "a minimalist ceramic teapot on a wooden table"},
"callback": {"url": "https://yourapp.example.com/hooks/hiapi", "when": "final"}
}'
when: "final" is currently the only supported value — your endpoint receives exactly one POST when the task reaches a terminal state (success or fail). Dedupe on taskId in case of retried deliveries, and verify your endpoint is reachable over HTTPS before relying on it in production.
Poll vs. callback
- Poll when you're prototyping, running a short-lived script, or don't have a public HTTPS endpoint yet. Simple, but adds latency and request volume for slow tasks.
- Callback when you're running a service — no wasted polling requests, and you find out the instant the task completes. Requires a publicly reachable HTTPS URL.
Idempotency
Image generation itself isn't idempotent (the same prompt won't return the same image twice), so if your workflow needs "don't double-charge on retry" semantics, track taskId on your side and check task status before resubmitting an identical request after a timeout, rather than blindly retrying.
Error handling
-
401 with
permission_denied— your API key is missing, malformed, or revoked:{"error":{"code":"permission_denied","message":"This API key is invalid. Check that it is correct or use another API key and try again.","type":"hiapi_error"}}Double-check you're sending
Authorization: Bearer sk-...(not just the raw key) and that the key hasn't been rotated. -
400 with
INVALID_REQUEST— a field failed validation. Themessagetells you exactly which field and why, e.g. an out-of-rangesize. Fix the field and resubmit; don't retry as-is. -
Task-level failure — the HTTP call can succeed (
200) while the task still ends instatus: "fail"once processing completes. Always checkdata.statuson your poll/callback response, not just the HTTP status code of the create call.
Related internal links
- seedream-5.0-flash model page — pricing, limits, and a live try-it panel.
- Create a task (docs) — full request schema for
POST /v1/tasks. - Get task detail (docs) — polling response shapes and status values.
- Authentication (docs) — how API keys and headers work across all hiapi endpoints.
- hiapi pricing — current per-image rates for seedream-5.0-flash and every other model.
- seedream-5.0-pro text-to-image API guide — if you need higher-resolution output, see the Pro tier's higher-fidelity sibling model.
FAQ
Is seedream-5.0-flash free to use? No — it's billed per image through your hiapi API key. See pricing for current rates; there's no production access without a valid key.
Does seedream-5.0-flash support image-to-image editing, not just text-to-image?
Yes. Add an image field (a URL or base64 data:image/... string, or an array of up to 10) alongside your prompt and the model will edit/blend based on the reference image(s) instead of generating from scratch.
Why does my request return 200 but no image?
A 200 from POST /v1/tasks only means the task was accepted — generation is asynchronous. Poll GET /v1/tasks/<id> (or wait for your callback) and check data.status; the image URL only appears once status is "success".
Can I request a custom resolution instead of 1K/1.5K/2K?
Yes — pass size as "WIDTHxHEIGHT" (e.g. "1536x1024") instead of a preset. It must total between 921,600 and 4,624,220 pixels with an aspect ratio between 1:16 and 16:1, or the request returns a 400.
What happens to the output URL after a while?
The returned output[].url is a signed URL with an expireAt timestamp — it is not permanent storage. Download the bytes or copy them to your own storage shortly after the task succeeds.
My callback never fires — what should I check first?
Confirm your callback.url is a publicly reachable HTTPS endpoint (not localhost or an internal-only address), and that callback is a top-level field in your request body — not nested inside input.



