
POST /v1/tasks, poll GET /v1/tasks/<taskId> (or use a callback), download the image from output[0].url.prompt (required), aspect_ratio (13 ratios, default 1:1), resolution (1k | 2k, lowercase, default 1k), output_format (jpeg | png | webp, default jpeg). Anything else is rejected with a 400.quality or rich_detail parameter — the richer detail, materials, and lighting are what this model variant does by default. For cheap volume drafts, its standard-tier sibling grok-imagine/text-to-image exists on the same endpoint.expireAt deadline — download promptly or store persistently.Goal: send a text prompt to grok-imagine-quality/text-to-image and get a finished, hero-grade image file back — first with curl, then as a small Python script you can drop into a job queue.
You need exactly one thing: a hiapi API key. Grab it from your hiapi dashboard and export it:
export HIAPI_API_KEY="sk-..."
Every request authenticates with a standard bearer header: Authorization: Bearer sk-<key>.
Image generation on hiapi is async-only. You never wait on one long HTTP call; you create a task and fetch the result when it's done.
Step 1 — 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": "grok-imagine-quality/text-to-image",
"input": {
"prompt": "Product hero shot of a matte-black espresso machine on a slate counter, dramatic directional lighting, shallow depth of field, premium commercial finish",
"aspect_ratio": "16:9",
"resolution": "2k",
"output_format": "png"
}
}'
You get a taskId back immediately while the model works in the background:
{
"code": 200,
"message": "success",
"data": { "taskId": "tk-hiapi-01HZTQ8BX2N3GM3YFK4Z9D7VQR" }
}
Step 2 — poll until it reaches a terminal state:
curl https://api.hiapi.ai/v1/tasks/tk-hiapi-01HZTQ8BX2N3GM3YFK4Z9D7VQR \
-H "Authorization: Bearer $HIAPI_API_KEY"
A task moves through queued → handling → archiving and ends at success or fail. Only success carries output:
{
"code": 200,
"message": "success",
"data": {
"taskId": "tk-hiapi-01HZTQ8BX2N3GM3YFK4Z9D7VQR",
"model": "grok-imagine-quality/text-to-image",
"status": "success",
"created": 1783130400,
"completed": 1783130460,
"output": [
{
"url": "https://cdn.hiapi.ai/tasks/.../image.png",
"type": "image",
"expireAt": 1783735260
}
]
}
}
Step 3 — download it before expireAt:
curl -o hero.png "https://cdn.hiapi.ai/tasks/.../image.png"
A complete script — create, poll, save. Copy, set HIAPI_API_KEY, run.
import os
import time
import requests
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
payload = {
"model": "grok-imagine-quality/text-to-image",
"input": {
"prompt": (
"Product hero shot of a matte-black espresso machine on a slate "
"counter, dramatic directional lighting, shallow depth of field, "
"premium commercial finish"
),
"aspect_ratio": "16:9",
"resolution": "2k", # lowercase! "2K" is rejected with a 400
"output_format": "png",
},
}
resp = requests.post(BASE, headers=HEADERS, json=payload, timeout=60)
resp.raise_for_status() # 4xx here = the request itself was rejected
task_id = resp.json()["data"]["taskId"]
print("task created:", task_id)
while True:
task = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
status = task["status"]
if status == "success":
image = task["output"][0]
with open("hero.png", "wb") as f:
f.write(requests.get(image["url"], timeout=120).content)
print("saved hero.png")
break
if status == "fail":
err = task["error"]
raise RuntimeError(f"task failed: {err['code']} {err['message']}")
time.sleep(5) # queued / handling / archiving — keep polling
Generation at the 2k tier typically lands within a minute or two; a 5-second poll interval is plenty.
The input schema is strict — unknown fields fail with 400 INVALID_REQUEST: additional properties not allowed rather than being silently ignored. That's good news: typos can't sneak into production. The full surface:
| Field | Required | Values | Default |
|---|---|---|---|
input.prompt | yes | free text | — |
input.aspect_ratio | no | 2:1, 20:9, 19.5:9, 16:9, 4:3, 3:2, 1:1, 2:3, 3:4, 9:16, 9:19.5, 9:20, 1:2 | 1:1 |
input.resolution | no | 1k, 2k (lowercase only) | 1k |
input.output_format | no | jpeg, png, webp | jpeg |
Three things worth knowing before you build on it:
rich_detail, quality, or style field to toggle.1k and 2k are billed as separate tiers. A common pattern: iterate on prompts at 1k, then re-run the winning prompt at 2k for the final asset. Current per-image rates are on the hiapi pricing page.n, no seed, no negative_prompt. One task produces one image. For variations, submit several tasks in parallel — they're independent and queue concurrently.Prefer a callback over polling in production. Pass callback at the top level of the request body (not inside input) and hiapi calls your service when the task reaches a terminal state:
{
"model": "grok-imagine-quality/text-to-image",
"input": { "prompt": "..." },
"callback": {
"url": "https://your-service.example.com/hiapi/callback",
"when": "final"
}
}
With when: "final" you're notified on both success and fail, so deduplicate by taskId on your side. Keep low-frequency polling as a fallback in case a callback is missed — the task detail endpoint is always the source of truth.
Make retries safe with Idempotency-Key. Task creation accepts an Idempotency-Key header (up to 255 bytes). Retrying with the same key never creates a duplicate task — replays return the original taskId:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Idempotency-Key: order-42-hero-image" \
-H "Content-Type: application/json" \
-d '{"model": "grok-imagine-quality/text-to-image", "input": {"prompt": "..."}}'
Handle the three failure shapes distinctly:
401 with {"error": {"code": "permission_denied", "type": "hiapi_error", ...}}. Check the key and whether it's allowed to use this model; don't retry blindly.400 with "error_code": "INVALID_REQUEST" and a message naming the exact field (e.g. resolution: value must be one of '1k', '2k'). Fix the payload; retrying the same body will fail the same way.status: "fail" and a data.error object. Inspect error.code before re-submitting.Mind expireAt. Output URLs are temporary. Download the file as soon as the task succeeds, or pass storage: "persistent" at creation time to keep outputs long-term.
How is grok-imagine-quality different from grok-imagine/text-to-image?
Same endpoint, same request shape, different tier. The quality tier targets hero shots and commercial assets with richer detail and lighting, priced by 1k/2k resolution tier; the standard grok-imagine/text-to-image is the faster, cheaper option for volume and ideation.
Why does "resolution": "2K" return a 400?
The enum is case-sensitive and lowercase: 1k or 2k. Uppercase values are rejected with INVALID_REQUEST.
Can I set exact pixel dimensions like 1920x1080?
No. You control shape with aspect_ratio (13 options from 2:1 ultra-wide to 9:20 extra-tall) and sharpness with the 1k/2k tier. There is no free-form size field on this model.
Can I generate multiple images in one request?
No — there's no n parameter. Submit multiple tasks in parallel instead; each returns its own taskId and they process concurrently.
Does grok-imagine-quality support image-to-image?
Yes, as a separate model id: grok-imagine-quality/image-to-image. See the i2i recipe for its schema — it differs from text-to-image.
How long do result URLs stay valid?
Each output[] entry has an expireAt Unix timestamp. Download before that deadline, or create the task with storage: "persistent" (or promote the output later) to keep it.
What does a queued-but-not-finished task look like?
GET /v1/tasks/<taskId> returns status of queued, handling, or archiving with no output yet. Only success includes downloadable output; fail includes data.error instead.