Generating an image with flux-2-klein-9b on hiapi is one POST /v1/tasks call plus a poll — the same async task pattern every model on the platform uses. This guide captures a real request and response (task creation, polling, and the final image URL) so the code below is copy-paste runnable, not illustrative.
What you need
- A hiapi API key — create one in the dashboard. It's sent as a Bearer token on every request.
- The full model id, including the endpoint suffix. flux-2-klein-9b is only reachable as
flux-2-klein-9b/text-to-image— the bare idflux-2-klein-9b400s (see below).
The minimal working request (curl)
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": "flux-2-klein-9b/text-to-image",
"input": {
"prompt": "a weathered lighthouse on a rocky coast at sunset, cinematic lighting",
"aspect_ratio": "16:9"
}
}'
Response:
{"code":200,"data":{"taskId":"tk-hiapi-01M0T23CG2GNPENYQV4WEKZJTM"},"message":"success"}
Poll the task until it's done:
curl https://api.hiapi.ai/v1/tasks/tk-hiapi-01M0T23CG2GNPENYQV4WEKZJTM \
-H "Authorization: Bearer sk-<your-key>"
The finished response:
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01M0T23CG2GNPENYQV4WEKZJTM",
"status": "success",
"model": "flux-2-klein-9b/text-to-image",
"created": 1787581018,
"completed": 1787581030,
"storage": "temp",
"output": [
{
"type": "image",
"url": "https://temp.hiapi.ai/7c6ttvrbpt/01M0T23CG2GNPENYQV4WEKZJTM-0.png",
"artifactId": "96193",
"expireAt": 1788185830
}
]
},
"message": "success"
}
output[0].url is the PNG. In testing, the task went from submitted to success in about 12 seconds. Note expireAt — see the storage note below before you build anything that relies on this URL staying alive.
The same flow in Python
import time
import requests
API_KEY = "sk-<your-key>"
BASE_URL = "https://api.hiapi.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_image(prompt: str, aspect_ratio: str = "1:1") -> str:
resp = requests.post(
f"{BASE_URL}/tasks",
headers=HEADERS,
json={
"model": "flux-2-klein-9b/text-to-image",
"input": {"prompt": prompt, "aspect_ratio": aspect_ratio},
},
timeout=30,
)
body = resp.json()
if resp.status_code != 200 or "error" in body:
raise RuntimeError(f"create failed: {body}")
return body["data"]["taskId"]
def wait_for_image(task_id: str, timeout_s: int = 120, interval_s: int = 3) -> str:
deadline = time.time() + timeout_s
while time.time() < deadline:
resp = requests.get(f"{BASE_URL}/tasks/{task_id}", headers=HEADERS, timeout=30)
data = resp.json()["data"]
if data["status"] == "success":
return data["output"][0]["url"]
if data["status"] == "failed":
raise RuntimeError(f"task {task_id} failed: {data}")
time.sleep(interval_s)
raise TimeoutError(f"task {task_id} still running after {timeout_s}s")
if __name__ == "__main__":
task_id = create_image("a weathered lighthouse on a rocky coast at sunset, cinematic lighting", "16:9")
image_url = wait_for_image(task_id)
image_bytes = requests.get(image_url, timeout=60).content
with open("output.png", "wb") as f:
f.write(image_bytes)
print(f"saved output.png from task {task_id}")
Parameters flux-2-klein-9b actually validates
The input schema is strict — send an unlisted field and the API rejects the whole request before it ever queues:
prompt(string, required) — the only required field.aspect_ratio(string, optional) — one of1:1,4:3,3:4,16:9,9:16. Anything else 400s withvalue must be one of ....seed(integer, optional) — pass the same seed with the same prompt to get a reproducible result.output_format(string, optional) — one ofjpeg,png,webp.
That's the whole schema. There's no size, quality, strength, or n field — sending any of them fails with:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: <root>: additional properties 'size' not allowed"}
flux-2-klein-9b is text-to-image only. image_urls isn't accepted either — for image-to-image, use the separate flux-2-klein-9b/image-to-image model id instead, which takes a different input shape.
The model id needs its endpoint suffix
Some hiapi models resolve from a bare id (gpt-image-2), but flux-2-klein-9b isn't one of them. Drop the suffix and the task never gets created:
{"code":400,"data":null,"error_code":"MODEL_UNAVAILABLE","message":"model not available via /v1/tasks"}
Always send the full flux-2-klein-9b/text-to-image string as model.
Production notes
- Two different error shapes. A validation error (missing field, bad enum, unknown key) comes back as
{"code":400,"error_code":"INVALID_REQUEST","message":"..."}at the top level. An auth/permission error comes back as{"error":{"code":"permission_denied","message":"...","request_id":"..."}}— a nestederrorobject, HTTP 401. Check for both shapes in your error handling; code that only checksresp["code"]will miss the 401 case. permission_deniedmeans the key, not the request. Ifpromptis present and correctly typed but you still get a 401permission_denied, the API key itself doesn't have this model enabled — check it in the dashboard rather than re-reading your JSON.- Output storage is temporary.
storage: "temp"and theexpireAtunix timestamp on each output — in the captured example, about a week after creation. Download the file or push it to your own storage as soon as the task succeeds; don't store thetemp.hiapi.aiURL as if it were permanent. - Use a callback for batches. Polling one image is fine; polling fifty is fifty repeated round-trips. Set
callback: {"url": "...", "when": "final"}at the top level of the create request and let hiapi push the result to you once, when the task actually finishes."final"is currently the only supported value forwhen. - Retry before you have a
taskId, not after. A network error or 5xx on the initialPOST /v1/tasksis safe to retry — nothing was created yet. Once you have ataskId, resubmitting the same prompt creates a second, unrelated image; poll or wait for the callback instead.
Related
- flux-2-klein-9b model page — current pricing and a live playground.
- flux-2-klein-9b docs reference — the model-specific reference entry.
- hiapi pricing — per-model rates across the catalog.
- Async task API docs — the full create/poll contract every hiapi model shares.
FAQ
Why do I get MODEL_UNAVAILABLE when I call flux-2-klein-9b?
The model id needs its endpoint suffix. Use flux-2-klein-9b/text-to-image, not the bare flux-2-klein-9b.
What aspect ratios does flux-2-klein-9b support?
Five: 1:1, 4:3, 3:4, 16:9, 9:16. Any other value 400s with a value must be one of message listing exactly those five.
Can I control output quality or resolution?
No — the schema only accepts prompt, aspect_ratio, seed, and output_format. There's no size or quality parameter; sending one returns an additional properties 400.
Can I use flux-2-klein-9b for image-to-image editing?
Not through this model id. Use flux-2-klein-9b/image-to-image instead, which has its own separate input schema.
How long is the output URL valid?
storage is "temp" and each output carries an expireAt unix timestamp roughly a week out in practice. Download or re-upload the file right after the task succeeds — don't treat the URL as permanent.
Why does the key that works for other models 401 on this one?
Model access is per-key. A 401 with error_code: "permission_denied" means this specific key hasn't been granted flux-2-klein-9b — enable it in the dashboard, it's not a bug in your request body.








