HiAPI
Blog

Tutorial

How to Use imagen-4.0-ultra via the HiAPI API: curl, Python, and a Working Request

从 HiAPI 的 OpenAI 兼容端点调用最高画质图像模型分步指南

Nov 3, 2026 11 min read
文章封面图:深青底色上以平面切割面构成的水晶矿物截面,青绿与墨绿刻面均衡排布。

What imagen-4.0-ultra Is and When to Choose It

imagen-4.0-ultra is the highest-fidelity tier in the imagen-4.0 family, exposed through HiAPI as an image generation model on the same OpenAI-compatible endpoint you already use for chat and other model families. The mental model is simple: you send a structured JSON body describing an image, and you get image data back — not a chat completion.

Choosing between tiers is mostly about the job, not the ranking:

  • Drafting and iteration. If you are exploring prompts, testing compositions, or generating thumbnails to pick a direction, a lighter image model returns faster and costs less per generation. Save ultra for the pass that matters.
  • Final assets. Hero images, marketing visuals, and anything a human will look at full-size benefit from the ultra tier's detail, prompt adherence, and handling of complex scenes with multiple subjects or text-like elements.
  • Batch pipelines with a quality gate. A common pattern is to draft broadly on a lighter model, then re-run only the selected prompts on imagen-4.0-ultra. Because HiAPI keeps the model ID in the request body, switching tiers is a one-string change, not a new integration.

A few expectations to set up front. Image generation is not instantaneous; a single ultra request can take noticeably longer than a text completion, so design your client with generous timeouts and no assumption of sub-second latency. And because the response carries binary image data rather than a choices array, any parsing code written for chat models will not transfer — plan for that early.

If you are unsure whether you need ultra, start with a minimal request on it, inspect the output at the resolution you actually plan to ship, and only then compare against a lighter tier. Judging image quality from downscaled previews is misleading.

Prerequisites and Authentication Setup

Before the first request, confirm three things: you have a HiAPI API key, you know the base URL of the OpenAI-compatible API endpoint, and your tooling can make HTTPS requests (curl, or Python with requests, or even httpx).

Keep the key out of your code

Store the key in an environment variable rather than pasting it into a script:

export HIAPI_API_KEY="your_api_key_here"

In production, use your platform's secret manager and inject the value at runtime. Committing a key to a repository is the single most common way credentials leak, and rotating it is far more painful than reading it from the environment.

The authentication header

Every request carries a standard Bearer token header:

Authorization: Bearer $HIAPI_API_KEY
Content-Type: application/json

Both headers matter. A missing Content-Type can cause the server to reject or misparse the body, which sometimes surfaces as a confusing generic error rather than a clear validation message.

The same key and the same header pattern work across model families on HiAPI, which is the practical payoff of the unified endpoint: one credential, one client, many models.

A quick sanity check

If you have a chat-capable model ID handy, sending a trivial text request first is a cheap way to confirm that your key and header are correct before you debug an image payload. Separating auth problems from request-body problems saves real time. If that text request succeeds and the image request does not, you know the issue is in the image payload.

Building the Request Body

Start with the smallest body that can produce an image: the model ID and a prompt. Everything else is optional refinement.

{
  "model": "imagen-4.0-ultra",
  "prompt": "A sunlit reading room with tall windows, a wooden desk, and shelves of worn books, soft dust motes in the light"
}

Two principles keep this maintainable. First, the model ID must match exactly what HiAPI documents for this model — no spaces, no version suffixes you invented. Second, the prompt is plain natural language; write it the way you would describe the image to a photographer, favoring concrete nouns and lighting or composition cues over abstract adjectives.

Prompt shape that tends to work

  • Subject first, then setting, then style. "A fox in a snowy birch forest, morning light, shallow depth of field" reads more reliably than a pile of style keywords.
  • Be concrete about composition. Mention camera angle, framing, or layout when it matters to your use case.
  • Avoid contradictions. Asking for "minimalist" and "intricate ornamental detail" in the same sentence tends to produce a compromise you did not want.

Sizing and aspect ratio

Image models commonly accept width/height or aspect-ratio options, but the exact parameter names and supported values are model-specific. Rather than guessing, check the model's parameter documentation on HiAPI and use only the values listed there. If you pass a size or ratio the model does not support, expect a validation error — not a silent fallback.

A safe workflow is to begin with the defaults, confirm a successful response, then add one sizing parameter at a time and inspect the output. If a request starts failing after you add parameters, you have isolated the cause to the last change.

Keep optional parameters out of your first request entirely. A body with two fields that works is a better foundation than a body with ten fields that fails.

A Working curl Request

With the key exported, this is the request shape to copy and adapt:

curl -sS -X POST "$HIAPI_BASE_URL/images/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "imagen-4.0-ultra",
    "prompt": "A sunlit reading room with tall windows, a wooden desk, and shelves of worn books, soft dust motes in the light"
  }'

What each part does:

  • -X POST — image generation is a create operation, not a read.
  • The URL — the image generation route on the HiAPI OpenAI-compatible endpoint. Set HIAPI_BASE_URL to the base URL shown in the HiAPI docs so the path stays correct if the host changes.
  • The two headers — Bearer auth and JSON content type, as described above.
  • The -d body — the same JSON you will reuse in Python. Quoting matters: single quotes around the payload keep the shell from expanding anything inside it.

Debugging tips

Add -i to include response headers, which tells you the HTTP status even when the body is large. If you want to confirm what you actually sent, add -v and read the outgoing request line — it quickly exposes a malformed URL or a missing header.

If the output is a wall of base64 or binary noise in your terminal, that is a sign the request worked. Do not pipe that into your logs; see the response-handling section for a cleaner approach. Redirecting the raw response to a file (> out.json) is fine for a one-off check.

Once curl returns a success status and a parseable body, you have a validated contract. The Python step should reproduce this exact JSON, byte for byte in structure, so any later failure is attributable to code rather than to a different request.

The Same Request in Python

Using requests, the equivalent call stays close to the curl version, which makes cross-checking trivial:

import os
import requests

base_url = os.environ["HIAPI_BASE_URL"]
api_key = os.environ["HIAPI_API_KEY"]

payload = {
    "model": "imagen-4.0-ultra",
    "prompt": (
        "A sunlit reading room with tall windows, a wooden desk, "
        "and shelves of worn books, soft dust motes in the light"
    ),
}

response = requests.post(
    f"{base_url}/images/generations",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=120,
)
response.raise_for_status()
result = response.json()

Notes that matter in practice:

  • json=payload serializes the dict and sets the content type for you. You can still pass the header explicitly for clarity or parity with curl.
  • timeout=120 is not decoration. Image generation can be slow; without an explicit timeout you inherit library defaults that may be shorter than you expect, and a client-side abort looks like a server failure.
  • raise_for_status() turns 4xx and 5xx responses into exceptions instead of letting malformed error bodies flow into your parsing code.

Wrapping it for reuse

Put the call in a small function that takes a prompt and optional parameters, returns the parsed result, and logs only the model ID, prompt length, status code, and elapsed time. That log line is enough to debug most issues without storing megabytes of image data in your log aggregator.

Because the payload is plain JSON, the same function works for other image models on HiAPI — swap the model ID and, if needed, the supported sizing parameters. Keep a few sample prompts in a test script so you can re-run a known-good request after any client upgrade and get a fast signal.

Handling the Image Response

The response is an image payload, not a chat completion. Do not look for choices[0].message.content — write parsing code that matches what the image route actually returns, as documented on HiAPI.

A defensive parse

The response typically carries the image data as a base64 string, sometimes alongside metadata. Decode only after confirming the field exists:

import base64

item = result["data"][0]
image_b64 = item.get("b64_json")
if image_b64 is None:
    # Some responses may carry a URL or another format; check the docs
    raise ValueError(f"Unexpected image payload keys: {list(item)}")

image_bytes = base64.b64decode(image_b64)
with open("output.png", "wb") as f:
    f.write(image_bytes)

The list(item) in the error message is the useful part: when a payload changes, it tells you the actual keys instead of making you guess.

Logging without dumping binary

Never log the raw response body for image requests — it is large and mostly unreadable. Log the status code, content length, model ID, and a hash or byte count of the decoded image. That is enough to verify you got a distinct image per prompt without bloating storage.

Operational habits

  • Validate before storing. Confirm the decoded bytes start with a recognizable image signature, and catch decode errors where you write the file.
  • Write atomically. Save to a temporary path and rename on success, so a crash mid-write does not leave a truncated file that looks valid.
  • Keep prompts with outputs. Store the prompt, model ID, and any sizing parameters beside each image. When a stakeholder asks for a variation later, you need the original request.
  • Set a retention rule. Generated images add up; decide early where they live and how long they stay.

Building the Request Body

Most failed first requests fall into three buckets. Check them in this order before touching your prompt.

Authentication failures

A 401 or 403 almost always means the header is wrong. Verify the scheme is exactly Bearer followed by a space and the key, with no trailing whitespace or stray quotes. Confirm the environment variable is actually set in the shell or process that runs the request — a key exported in one terminal does not exist in another. If you recently rotated the key, make sure the old value is not still cached in a config file or deployment secret.

Model ID errors

A model-not-found response means the model field does not match HiAPI's registered ID. Copy the identifier from the HiAPI model documentation rather than retyping it; a single wrong character or an invented version suffix is enough to fail. If you switched model IDs to compare tiers, check that the swap landed in the payload and not only in a comment.

Rejected parameters

A validation error naming a field usually means an unsupported size, aspect ratio, or an optional parameter the model does not accept. Remove every optional field, confirm the minimal two-field request succeeds, then add parameters back one at a time. This isolates the offender immediately.

Other symptoms

  • Empty or unparseable body with a 200 — you may be reading the wrong field. Inspect the top-level keys before indexing into the payload.
  • Client timeout but a server-side charge — your timeout may be too short; raise it and retry once with the same prompt before assuming failure.
  • Different JSON in curl and Python — print the serialized payload in Python and compare it to the curl -d string. Divergence here explains most "it worked in curl" reports.

Work through these in order and you will resolve the overwhelming majority of first-integration issues without changing a word of your prompt. From there, run a known-good prompt through a small test script after each client or SDK upgrade to catch regressions early.

Related articles

View all articles →

Start building with persistent AI artifacts

Get your API key and call image, video and audio models through one task-based API.

Read the API docs