Search

Search HiAPI models, tools, and resources.

Back to blog
Tutorial

flux-2-klein-9b API: curl & Python Guide

hiapiflux-2-klein-9bImage APITutorialTask API

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

  1. A hiapi API key — create one in the dashboard. It's sent as a Bearer token on every request.
  2. 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 id flux-2-klein-9b 400s (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 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.

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 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.
  • Output storage is temporary. 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.
  • 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 for when.
  • Retry before you have a 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.

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.

Generate it with HiAPI

Choose a model, enter your prompt, and see the result.

HiAPI Blog

Related articles

HiAPI

Generate it with HiAPI

Start generating