
You submitted a job to POST /v1/tasks, got back a task_id, and now GET /v1/tasks/{id} is sitting in "status": "pending" for several minutes — or worse, the response just came back with "status": "timeout". This guide walks through what's actually happening, the four causes you'll see in practice, and a retry strategy that won't make things worse.
A typical "hung" task response looks like this:
{
"id": "task_01HXYZ...",
"status": "pending",
"created_at": 1717000000,
"updated_at": 1717000000,
"output": null,
"error": null
}
updated_at equals created_at and no error field has been populated — the task hasn't moved at all since you submitted it.
A timed-out task looks like this:
{
"id": "task_01HXYZ...",
"status": "timeout",
"error": {
"code": "task_timeout",
"type": "hiapi_error",
"message": "Task exceeded maximum execution time",
"request_id": "req_..."
}
}
Either way: don't immediately re-submit the same body. Diagnose first.
When a popular model — image generation models like gpt-image-2, video models, or anything compute-heavy — is under load, your task waits in a per-model queue. The task is healthy; it just hasn't been picked up yet.
status is pending, error is null, time since created_at is under ~2 minutes.The model picked up your task but errored out internally.
status is failed, and error.code is something like model_error, provider_error, or a model-specific code.Some endpoints will accept your POST, return a task_id, and only fail validation when the worker actually picks the job up.
status is failed, error.code is invalid_request, input_too_large, or unsupported_format.If you submitted with a callback_url, the task itself may have completed, but hiapi couldn't deliver the result.
status is succeeded with output populated, but you never got a callback.GET /v1/tasks/{id} if your endpoint is behind a firewall, slow, or returns 5xx.GET /v1/tasks/{id}
├─ status == "pending" → wait, poll again in 5–10s (cause 1)
├─ status == "failed" → read error.code (cause 2 or 3, code tells you which)
├─ status == "timeout" → cause 1 took too long, OR cause 2
└─ status == "succeeded" + no callback → cause 4
You should never be guessing. The error.code field tells you exactly which bucket you're in.
This curl confirms your key works and the task endpoint is reachable. Use the smallest, fastest model you have access to (e.g. a text model) to verify auth before you debug a heavy image/video task:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"input": {
"prompt": "a single red apple on a white background, studio lighting"
}
}'
You'll get back something like {"id":"task_01HXYZ...","status":"pending",...}. Then poll:
TASK_ID=task_01HXYZ...
curl -s "https://api.hiapi.ai/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $HIAPI_API_KEY"
If this returns a 401 with {"error":{"code":"permission_denied","type":"hiapi_error",...}}, your key is the issue — not the task. See the Invalid API Key Errors guide before going any further.
If the model id itself is rejected, see hiapi 'model not available' errors. Pass the bare model id (e.g. gpt-image-2) — no /text-to-image suffix.
When you do retry, do it carefully:
task_id and potentially burns credits. If you need true idempotency, ask whether your client library supports an idempotency header.429, slow your submission rate, not your poll rate. See How to Fix hiapi Rate Limit (429) Errors.A reasonable Python retry loop:
import time, requests, os
def submit_and_wait(body, max_polls=30, poll_interval=5):
headers = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
r = requests.post("https://api.hiapi.ai/v1/tasks", json=body, headers=headers, timeout=30)
r.raise_for_status()
task_id = r.json()["id"]
for _ in range(max_polls):
time.sleep(poll_interval)
r = requests.get(f"https://api.hiapi.ai/v1/tasks/{task_id}", headers=headers, timeout=30)
data = r.json()
if data["status"] in ("succeeded", "failed", "timeout"):
return data
return {"status": "polling_exhausted", "task_id": task_id}
error.code → it's not transient; fix the input or the model choice.pending for over 5 minutes with no error and no progress → check the hiapi dashboard for any platform notices, then open a ticket with your request_id.status is succeeded → stop relying on callbacks for that endpoint; switch to polling. Details in Why your hiapi task callback isn't firing.Q: How long until a task is considered timed out?
A: It depends on the model — image models typically time out after a few minutes, video models can run longer. Check the response error.message when you see status: "timeout"; it will name the limit that was hit.
Q: My task is pending for 30 seconds — should I cancel and retry?
A: No. 30 seconds is well inside the normal range for any non-trivial model. Wait at least 2 minutes before considering retry.
Q: Can I cancel an in-flight task? A: Cancellation isn't generally supported; even if it were, you'd already have paid for the queue slot. Just let it complete and check the result.
Q: Why did the same request work yesterday and hang today? A: Almost always queue backlog (cause 1) at the model layer. Try a different model with similar capability, or retry in 5–10 minutes when the queue drains.
Q: I'm getting timeouts on every task — is it my account? A: Check your hiapi dashboard first for any account-level notices, then verify your key isn't being rate-limited (see the 429 guide). If both look clean and it's a single model failing, it's almost certainly that model's queue or backend, not your account.
Q: Does retrying a timed-out task cost me twice?
A: A new POST /v1/tasks is a new billable task. A timed-out task that never produced output typically isn't charged for the output, but the queue/compute slot may be. Check your usage page after the fact if it matters.
Key Takeaways