Introduction
/v1/tasks is the single entry point for all async generation models — image, video, and audio. Whatever the model, you submit, poll, and receive terminal callbacks through the same set of endpoints: submitting returns a taskId, then you poll the task detail or wait for a callback to get the final output.
API overview
Section titled “API overview”All requests use the same Base URL:
https://api.hiapi.ai| Method | Path | Purpose |
|---|---|---|
POST | /v1/tasks | Create a task, returns taskId |
GET | /v1/tasks | List tasks (paginated, newest first) |
GET | /v1/tasks/:id | Get task detail: status, output, or failure reason |
Authentication
Section titled “Authentication”Every request carries the account API key in the Authorization header:
Authorization Bearer YOUR_API_KEY required Account API key. Required on every request.
Content-Type application/json required The request body is JSON.
A task must belong to the API key’s account; cross-account queries return 404.
Idempotent retries (Idempotency-Key)
Section titled “Idempotent retries (Idempotency-Key)”POST /v1/tasks accepts an optional Idempotency-Key header (any UTF-8 string, up to 255 bytes). Idempotency is scoped to the account that owns the API key: retrying with the same key under the same account (even after rotating the key) creates the task only once, and replays return the original taskId with the response header Idempotent-Replay: true — network timeouts and client-side auto-retries no longer create duplicate tasks or duplicate charges.
- Retries must resend the exact same request body; the same key with a different body returns
422 IDEMPOTENCY_KEY_MISMATCH - While the first request is still in flight, you get
409 IDEMPOTENCY_KEY_PROCESSING; retry afterRetry-After(5 seconds) - Keys are kept for 24 hours; after that the same key is treated as a new request
- Requests without the header behave exactly as before
See business error codes below for details.
Model and parameters
Section titled “Model and parameters”Set the HiAPI model name via model, and put business parameters in the input object. The fields of input are defined per model — see the relevant model page.
Model routes (route)
Section titled “Model routes (route)”Some models expose multiple routes (e.g. pro, ext) with different pricing or upstream capacity. Select one with the optional top-level route parameter — the preferred spelling over the legacy @ suffix:
{ "model": "gpt-image-2/text-to-image", "route": "pro", "input": { ... } }- Passing model
gpt-image-2/text-to-imagewithroute: "pro"is equivalent to the suffixed namegpt-image-2/text-to-image@pro; the legacy@suffix keeps working - Omitting
route, or sending""/null/"default", all mean the model’s default route - An unknown route fails fast with
400 INVALID_REQUESTwhose message lists the available routes - A
routethat conflicts with an@suffix already present inmodel(e.g.x@ext+route: "official") is also a400 - On the task detail, a task submitted with
routeechoes the field back andmodelholds the resolved full name (x@pro); theroutevalue participates in the idempotency hash, so the sameIdempotency-Keywith a differentroutereturns422
Callbacks
Section titled “Callbacks”Async tasks support two ways to get results:
- Polling — after creating a task, request
GET /v1/tasks/:idat intervals untilstatusis terminal. - Callback (Webhook) — provide
callback.urlwhen creating the task, and HiAPIPOSTs to that URL when the task reaches a terminal state (successorfail).
The callback request:
- Method
POST, Content-Typeapplication/json - Body is identical to the
datafield ofGET /v1/tasks/:id(the task detail object) callback.whencurrently supports onlyfinal(bothsuccessandfailfire a callback)
Signature (optional, recommended)
Section titled “Signature (optional, recommended)”Set a “Webhook signing key” on the account settings page in the HiAPI console (shared secret, 16–256 chars; empty means no signing).
- Not set: callback requests carry no signature header.
- Set: each callback carries
X-HiAPI-Timestamp: Unix seconds (string)X-HiAPI-Signature:hex( HMAC_SHA256(secret, timestamp + "." + body) )
Receiver verification (Python pseudocode):
import hmac, hashlib
ts = request.headers["X-HiAPI-Timestamp"]sig = request.headers["X-HiAPI-Signature"]body = request.raw_body # raw bytes, not JSON-parsed
expected = hmac.new( secret.encode(), (ts + "." + body).encode(), hashlib.sha256,).hexdigest()
assert hmac.compare_digest(expected, sig), "bad signature"# Recommended: reject requests where abs(now - int(ts)) > 5 minutes (replay protection)Retries and idempotency
Section titled “Retries and idempotency”| Item | Behavior |
|---|---|
| Enqueue | Once, when the task reaches success or fail |
| Success | Receiver returns HTTP 2xx |
| Retry | Non-2xx / timeout / network error retries at immediately → +1 min → +5 min → +20 min, up to 4 attempts |
| Terminal | After 4 failed attempts no more retries; does not affect the task’s own state |
Terminal callbacks are delivered “at least once”, not “exactly once”. Deduplicate by taskId on your side.
This covers callback delivery idempotency (HiAPI may deliver twice; you deduplicate by taskId). Idempotency in the submit direction (you retry a request; the platform creates the task only once) is handled by the Idempotency-Key header — the two are independent.
Status codes
Section titled “Status codes”Task status enum
Section titled “Task status enum”| status | Meaning | Returns output? |
|---|---|---|
queued | Enqueued, not started | No |
handling | Processing | No |
archiving | Done, output being prepared | No |
success | Terminal: complete, output available | Yes |
fail | Terminal: failed | No, returns error |
The only terminal states are success / fail; they never convert to each other, and a taskId’s result is fixed once terminal.
HTTP status codes
Section titled “HTTP status codes”| Status | Meaning |
|---|---|
200 | Success (task detail returns 200 even when status=fail; the failure is in the body’s error) |
400 | Invalid request — see error_code below |
402 | Insufficient balance — the task is not created and nothing is charged; top up and retry |
404 | Task does not exist or does not belong to the current account |
409 | Idempotency key conflict: the first request with this key is still in flight; retry after Retry-After |
422 | Idempotency key mismatch: same key with a different request body; fix your key construction, do not retry |
503 | Platform temporarily unavailable; retry later with exponential backoff |
Business error codes
Section titled “Business error codes”The synchronous failure response of POST /v1/tasks (error_code) and a terminal task’s error.code share this enum (codes marked “creation-only” never appear in a terminal task’s error.code):
| code | Meaning | What to do |
|---|---|---|
INVALID_REQUEST | Invalid body (missing field / wrong type / bad callback.url / model rejects the input), or the idempotency key exceeds 255 bytes / is not valid UTF-8 | Fix the request; do not retry the same request |
INSUFFICIENT_QUOTA | Insufficient account balance (HTTP 402, creation-only) — the task is not created and nothing is charged | Top up and retry; you can set balance alerts in the dashboard |
IDEMPOTENCY_KEY_PROCESSING | The first request with this Idempotency-Key is still in flight (HTTP 409, creation-only) | Retry after Retry-After (5 seconds) — see Idempotent retries |
IDEMPOTENCY_KEY_MISMATCH | Same Idempotency-Key with a different request body (HTTP 422, creation-only) | Fix your key construction; do not retry |
MODEL_UNAVAILABLE | Model unavailable (disabled, no permission, temporarily unavailable) | Retry shortly, or switch models |
TASK_FAILED | Task failed | Retry once; contact support if persistent |
TASK_TIMEOUT | Task timed out | Retryable |
STORAGE_UNAVAILABLE | Output storage error | Retryable; contact support if persistent |
Failed tasks are never charged: the amount reserved at creation is automatically refunded in full once a task reaches the failed terminal state.
Failure responses look like:
{ "code": 400, "message": "invalid request", "data": null, "error_code": "INVALID_REQUEST"}State machine
Section titled “State machine” POST /v1/tasks ──► queued ──► handling ──► archiving ──► success (terminal) (200 / taskId) │ │ ▼ ▼ fail fail (terminal)
success / fail triggers the callback (if callback.url was set)