
POST /v1/tasks, poll GET /v1/tasks/<taskId> (or use a callback), download the image from output[0].url.prompt (required), plus three optional knobs — aspect_ratio (14 ratios, e.g. 1:1, 16:9, 9:16, auto), resolution (1k | 2k), and quality (low | medium). Send anything else and the API rejects the whole request with a 400.expireAt timestamp) — download or re-upload to your own storage right after the task completes.error.code: "permission_denied".Goal: send a text prompt to grok-imagine-image-2.0/text-to-image and get back a finished image URL — first with curl, then as a small Python script you can drop into a job queue.
You need one thing: a hiapi API key. Grab it from your hiapi dashboard and export it:
export HIAPI_API_KEY="sk-..."
Every request authenticates with Authorization: Bearer sk-<key>.
Image generation on hiapi is async-only — every model, including grok-imagine-image-2.0/text-to-image, runs behind the same unified task API. You create a task, then fetch the result once 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-image-2.0/text-to-image",
"input": {
"prompt": "A red apple on a wooden table, studio lighting, shallow depth of field",
"aspect_ratio": "16:9",
"resolution": "2k"
}
}'
You get a taskId back immediately:
{
"code": 200,
"message": "success",
"data": { "taskId": "tk-hiapi-01M01JGSGB818MX59GVZZCVH67" }
}
Step 2 — poll until the task reaches a terminal state:
curl https://api.hiapi.ai/v1/tasks/tk-hiapi-01M01JGSGB818MX59GVZZCVH67 \
-H "Authorization: Bearer $HIAPI_API_KEY"
status moves from handling to success (or failed). On success you get the image:
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01M01JGSGB818MX59GVZZCVH67",
"status": "success",
"storage": "temp",
"output": [
{
"type": "image",
"url": "https://temp.hiapi.ai/7c6ttvrbpt/01M01JGSGB818MX59GVZZCVH67-0.jpg",
"artifactId": "76710",
"expireAt": 1787364206
}
]
},
"message": "success"
}
storage: "temp" and expireAt are the important fields here — the URL is not permanent. Download it (or copy it into your own object storage) as soon as the task finishes.
input only accepts these fields — anything else fails schema validation with a 400:
| Field | Required | Type | Notes |
|---|---|---|---|
prompt | yes | string | Text description of the image |
aspect_ratio | no | enum | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 2:1, 1:2, 19.5:9, 9:19.5, 20:9, 9:20, auto |
resolution | no | enum | 1k, 2k |
quality | no | enum | low, medium |
There is no n, size, output_format, seed, or negative_prompt on this model — the API returns additional properties '<field>' not allowed for any of those. Check current per-call pricing (it varies by resolution/quality) on the pricing page before wiring a production budget.
import os
import time
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE = "https://api.hiapi.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def generate_image(prompt: str, aspect_ratio: str = "1:1", resolution: str = "1k") -> str:
create = requests.post(
f"{BASE}/tasks",
headers=HEADERS,
json={
"model": "grok-imagine-image-2.0/text-to-image",
"input": {"prompt": prompt, "aspect_ratio": aspect_ratio, "resolution": resolution},
},
timeout=30,
)
create.raise_for_status()
task_id = create.json()["data"]["taskId"]
while True:
poll = requests.get(f"{BASE}/tasks/{task_id}", headers=HEADERS, timeout=30)
poll.raise_for_status()
data = poll.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(2)
if __name__ == "__main__":
url = generate_image("A red apple on a wooden table, studio lighting")
print(url)
Use a callback instead of polling. For anything beyond a quick script, don't poll in a tight loop — attach a callback to the task creation call and let hiapi push the result to your own endpoint when it's ready:
{
"model": "grok-imagine-image-2.0/text-to-image",
"input": { "prompt": "..." },
"callback": { "url": "https://your-service.example.com/hooks/hiapi", "when": "final" }
}
when: "final" fires exactly once, when the task reaches success or failed — no partial/progress events to filter out. This is the right default for a batch pipeline; reserve polling for interactive flows (e.g. a UI waiting on one task) where you already have an open request-response cycle.
Persist output immediately. storage: "temp" output expires at expireAt (roughly a week out) — treat the URL as a pointer you consume once, then re-upload the bytes to your own storage (S3, R2, GCS) for anything that needs to outlive that window.
Idempotency. The task API doesn't take a client-supplied idempotency key — if a request to POST /v1/tasks times out on your end mid-flight, you can't safely assume it didn't create a task. Keep your own record of taskId per logical job before you consider the create call "sent", and treat a duplicate task (two images for one job) as a state your retry logic should detect and clean up rather than something the API prevents for you.
Error handling. A missing or invalid key returns HTTP 401 with a JSON body like:
{
"error": {
"code": "permission_denied",
"message": "This API key cannot use the selected model. Please check permissions or use another key.",
"request_id": "..."
}
}
Schema violations (missing prompt, an unknown field, an out-of-enum value) come back as HTTP 400 with error_code: "INVALID_REQUEST" and a message that names the offending field — cheap to handle: log the message and don't retry, since retrying an invalid request just repeats the same 400.
What model ID do I use — with or without a version suffix?
Use the bare model ID exactly as returned by the platform: grok-imagine-image-2.0/text-to-image. Don't add extra suffixes like -preview or -latest; those aren't valid IDs for this model.
Why did my request fail with "additional properties not allowed"?
The input schema is strict — only prompt, aspect_ratio, resolution, and quality are accepted. Fields that other image models support (size, n, seed, negative_prompt, output_format) aren't part of this model's schema and will fail validation.
Can I generate multiple images in one call?
No — there's no n parameter. Issue one task per image; run them concurrently client-side if you need a batch.
How long is the output URL valid?
It's temporary storage with an expireAt Unix timestamp in the response. Download or re-host the image right after the task succeeds rather than storing the URL long-term.
Do I need to poll, or can I just wait synchronously?
The create call returns as soon as the task is queued — it doesn't block until the image is ready. Poll GET /v1/tasks/<taskId> for short-lived scripts, or set a callback for anything running in production so you're not holding a connection open or spinning a poll loop.
What does a failed task look like?
status will be failed in the task response instead of success, with output absent or empty. Treat failed as terminal — don't keep polling a task that's already failed.
Key Takeaways