Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
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.
flux-2-klein-9b/text-to-image — the bare id flux-2-klein-9b 400s (see below).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.
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}")
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 of 1:1, 4:3, 3:4, 16:9, 9:16. Anything else 400s with value 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 of jpeg, 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.
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.
{"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 nested error object, HTTP 401. Check for both shapes in your error handling; code that only checks resp["code"] will miss the 401 case.permission_denied means the key, not the request. If prompt is present and correctly typed but you still get a 401 permission_denied, the API key itself doesn't have this model enabled — check it in the dashboard rather than re-reading your JSON.storage: "temp" and the expireAt unix 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 the temp.hiapi.ai URL as if it were permanent.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 for when.taskId, not after. A network error or 5xx on the initial POST /v1/tasks is safe to retry — nothing was created yet. Once you have a taskId, resubmitting the same prompt creates a second, unrelated image; poll or wait for the callback instead.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.