Skip to content
English

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.

All requests use the same Base URL:

https://api.hiapi.ai
MethodPathPurpose
POST/v1/tasksCreate a task, returns taskId
GET/v1/tasksList tasks (paginated, newest first)
GET/v1/tasks/:idGet task detail: status, output, or failure reason

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.

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

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.

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-image with route: "pro" is equivalent to the suffixed name gpt-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_REQUEST whose message lists the available routes
  • A route that conflicts with an @ suffix already present in model (e.g. x@ext + route: "official") is also a 400
  • On the task detail, a task submitted with route echoes the field back and model holds the resolved full name (x@pro); the route value participates in the idempotency hash, so the same Idempotency-Key with a different route returns 422

Async tasks support two ways to get results:

  1. Polling — after creating a task, request GET /v1/tasks/:id at intervals until status is terminal.
  2. Callback (Webhook) — provide callback.url when creating the task, and HiAPI POSTs to that URL when the task reaches a terminal state (success or fail).

The callback request:

  • Method POST, Content-Type application/json
  • Body is identical to the data field of GET /v1/tasks/:id (the task detail object)
  • callback.when currently supports only final (both success and fail fire a callback)

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)
ItemBehavior
EnqueueOnce, when the task reaches success or fail
SuccessReceiver returns HTTP 2xx
RetryNon-2xx / timeout / network error retries at immediately → +1 min → +5 min → +20 min, up to 4 attempts
TerminalAfter 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.

statusMeaningReturns output?
queuedEnqueued, not startedNo
handlingProcessingNo
archivingDone, output being preparedNo
successTerminal: complete, output availableYes
failTerminal: failedNo, returns error

The only terminal states are success / fail; they never convert to each other, and a taskId’s result is fixed once terminal.

StatusMeaning
200Success (task detail returns 200 even when status=fail; the failure is in the body’s error)
400Invalid request — see error_code below
402Insufficient balance — the task is not created and nothing is charged; top up and retry
404Task does not exist or does not belong to the current account
409Idempotency key conflict: the first request with this key is still in flight; retry after Retry-After
422Idempotency key mismatch: same key with a different request body; fix your key construction, do not retry
503Platform temporarily unavailable; retry later with exponential backoff

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):

codeMeaningWhat to do
INVALID_REQUESTInvalid body (missing field / wrong type / bad callback.url / model rejects the input), or the idempotency key exceeds 255 bytes / is not valid UTF-8Fix the request; do not retry the same request
INSUFFICIENT_QUOTAInsufficient account balance (HTTP 402, creation-only) — the task is not created and nothing is chargedTop up and retry; you can set balance alerts in the dashboard
IDEMPOTENCY_KEY_PROCESSINGThe 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_MISMATCHSame Idempotency-Key with a different request body (HTTP 422, creation-only)Fix your key construction; do not retry
MODEL_UNAVAILABLEModel unavailable (disabled, no permission, temporarily unavailable)Retry shortly, or switch models
TASK_FAILEDTask failedRetry once; contact support if persistent
TASK_TIMEOUTTask timed outRetryable
STORAGE_UNAVAILABLEOutput storage errorRetryable; 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"
}
POST /v1/tasks ──► queued ──► handling ──► archiving ──► success (terminal)
(200 / taskId) │ │
▼ ▼
fail fail (terminal)
success / fail triggers the callback (if callback.url was set)