HiAPI
Blog

Tutorial

How to Use the grok-imagine-quality Text-to-Image API: curl, Python, and a Working Request

Jul 11, 2026
How to Use the grok-imagine-quality Text-to-Image API: curl, Python, and a Working Request

TL;DR

  • What this is: a working recipe for the grok-imagine-quality/text-to-image API on hiapi — create a task on POST /v1/tasks, poll GET /v1/tasks/<taskId> (or use a callback), download the image from output[0].url.
  • The schema is strict and small: 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" is the tier, not a knob. There is no 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.
  • Output URLs carry an expireAt deadline — download promptly or store persistently.

What you're building and what you need

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>.

The minimal working request (curl)

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"

The same flow in Python

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.

Parameters that actually exist

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:

  • The "quality" in the name is the tier, not a parameter. You may see this model described as having richer detail, materials, and lighting — that's the model variant itself. There is no 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.
  • No n, no seed, no negative_prompt. One task produces one image. For variations, submit several tasks in parallel — they're independent and queue concurrently.

Production notes: callbacks, idempotency, and errors

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:

  1. Auth failure — HTTP 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.
  2. Invalid input — HTTP 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.
  3. Task-level failure — the create call succeeded, but the task ends with 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.

Related pages

FAQ

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.

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