Imagen 4 Ultra is Google's highest-detail tier in the Imagen 4 family, and on hiapi it runs text-to-image through the same unified async task API as every other image model. This guide gives you one request that actually works — a real curl call, a Python poll loop, the exact parameters the server accepts, and the error cases verified against a live request. Everything below was run against the production endpoint before publishing; the sample taskId and output URL are from that run.
What you'll build
A single call that turns a text prompt into a finished image: submit a task to POST /v1/tasks, poll until it's done, and read the image URL from output[0].url. You need a hiapi API key — grab one from your dashboard and export it:
export HIAPI_API_KEY="your-hiapi-key"
Minimal working example (curl)
The model takes a prompt and an optional aspect_ratio. Create the task:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "imagen4-ultra",
"input": {
"prompt": "Macro product photo of a matte ceramic coffee mug on a pale oak table, soft window light from the left, shallow depth of field, a few coffee beans in the foreground, no text, no logos",
"aspect_ratio": "4:3"
}
}'
The create call returns immediately with a task id — generation continues in the background:
{"code":200,"data":{"taskId":"tk-hiapi-01M5CVC8R62M0Z7CCM8WB38B61"},"message":"success"}
Poll the task with the id you got back:
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01M5CVC8R62M0Z7CCM8WB38B61" \
-H "Authorization: Bearer $HIAPI_API_KEY"
Once status is success, the image is in output[0].url:
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01M5CVC8R62M0Z7CCM8WB38B61",
"model": "imagen4-ultra",
"status": "success",
"storage": "temp",
"created": 1792506471,
"completed": 1792506479,
"output": [
{
"artifactId": "279760",
"type": "image",
"url": "https://temp.hiapi.ai/7c6ttvrbpt/01M5CVC8R62M0Z7CCM8WB38B61-0.webp",
"expireAt": 1793111279
}
]
},
"message": "success"
}
That run completed in about 8 seconds and returned a single WebP (not PNG/JPEG). The URL lives on temporary storage ("storage": "temp") with expireAt set 7 days out — download it promptly rather than hotlinking.
Production: Python with a poll loop
The create response doesn't contain the image — only a taskId. A real integration submits, then polls (or uses a callback). This script does the full round trip and downloads the result immediately, since the URL is temporary:
import os, time, requests
API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
def generate(prompt, aspect_ratio="1:1", negative_prompt=None, seed=None):
body = {"model": "imagen4-ultra", "input": {"prompt": prompt, "aspect_ratio": aspect_ratio}}
if negative_prompt:
body["input"]["negative_prompt"] = negative_prompt
if seed is not None:
body["input"]["seed"] = str(seed) # seed is a STRING, not a number
r = requests.post(API, headers=HEADERS, json=body, timeout=60)
r.raise_for_status()
created = r.json()
if created.get("code") != 200:
raise RuntimeError(f"create rejected: {created}")
task_id = created["data"]["taskId"]
# Poll. Statuses other than success/fail (e.g. queued, running, archiving) mean "keep waiting".
for _ in range(60):
time.sleep(3)
try:
d = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
except requests.RequestException:
continue # tolerate a transient read; re-poll the same id
status = d.get("status")
if status == "success":
return d["output"][0]["url"]
if status in ("fail", "failed", "error"):
raise RuntimeError(f"task failed: {d}")
raise TimeoutError(f"task {task_id} did not finish in time")
url = generate(
"A high-detail aerial landscape of terraced rice fields at golden hour, long shadows",
aspect_ratio="16:9",
)
img = requests.get(url, timeout=60).content
open("out.webp", "wb").write(img)
print(f"saved {len(img)} bytes from {url}")
Notes that matter in production:
- Poll vs callback. Polling is shown above. To avoid polling, attach a top-level
callbackobject ({"url": "...", "when": "final"}) and hiapi will POST you when the task reaches a terminal state. See the Unified Async API docs for the callback contract. - Re-poll the saved id, don't recreate. Reading a task is idempotent; a dropped connection is a retry on
GET /v1/tasks/<id>, never a secondPOST(which bills a second image). - Download on success.
output[0].urlis temporary (7-day TTL in this run). Fetch the bytes and push them to your own storage.
Parameters this model actually accepts
imagen4-ultra is text-to-image only — reference images are not supported (confirmed on the model page; this run only exercised text-to-image). The input schema is closed: unknown fields are rejected, so you can't sneak in extra params.
| Field | Type | Required | Notes |
|---|---|---|---|
prompt | string | yes | 1–5,000 characters. Empty string fails (minLength: got 0, want 1). |
aspect_ratio | enum | no | One of 1:1, 16:9, 9:16, 3:4, 4:3, auto. Defaults to 1:1. |
negative_prompt | string | no | Up to 5,000 characters; describe what to exclude. |
seed | string | no | Up to 500 characters. It's a string — passing a number is rejected. |
One request produces exactly one image.
Verified error cases
These were produced against the live endpoint, so your error handling can rely on them. Rejected requests return an HTTP 400 with a flat envelope and no taskId:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: aspect_ratio: value must be one of '1:1', '16:9', '9:16', '3:4', '4:3', 'auto'"}
A few behaviors worth coding around:
- The validator reports every field error at once, joined by
;. Send a number forseedand a badaspect_ratioand you get both in one message:invalid input: seed: got number, want string; aspect_ratio: value must be one of .... Don't assume one error per response. - Unknown fields fail the whole request with
<root>: additional properties '<name>' not allowed. The schema is closed. - Empty prompt fails with
prompt: minLength: got 0, want 1. - A successful
createiscode: 200withdata.taskId; a rejection hasdata: null. Branch on that before you try to read a task id.
Which Imagen 4 tier should you call?
imagen4-ultra is the top of three tiers; pick by job, not by habit (aspect ratio, negative prompt and seed do not change what you pay — see pricing):
- Imagen 4 Fast — cheapest, for draft sweeps and exploring directions in bulk.
- Imagen 4 — the everyday middle tier for product, lifestyle and content images.
- Imagen 4 Ultra (this model) — the priciest of the three, positioned for finished-quality detail: macro texture, material rendering and lettering where the final image has to hold up. Reach for Ultra when the output ships as-is; use Fast or the base tier when you're iterating.
Related
- Imagen 4 Ultra model page — live schema, examples and pricing.
- Unified Async API docs — create/list/get-task and the callback contract.
- Quickstart — auth and your first task in a couple of minutes.
- Pricing — per-image cost across the Imagen 4 tiers.
FAQ
How many images does one request return? One. The output array held a single item in the verified run.
What format is the output? WebP. The returned file was a .webp on temp.hiapi.ai.
How long is the output URL valid? About 7 days — expireAt was set 7 days after completion. Treat it as temporary and download right away.
Can I send a reference image? No. imagen4-ultra is text-to-image only; the model page lists reference images as not supported.
Why does my request fail with "additional properties not allowed"? The input schema is closed. Send only prompt, aspect_ratio, negative_prompt and seed; any other key fails the whole request.
Do negative prompt, seed or aspect ratio change the price? No. Billing is per image; those parameters don't affect cost.





