The minimal text-to-video request, then the production-shaped flow with callbacks, idempotency, and real error responses.
Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
minimax-h3-max sounds like it could be a chat model — it isn't. On hiapi it's a text-to-video model: you send a prompt and get back an MP4. This post is the minimal request that returns a real video, then the production-shaped version with callbacks, idempotency, and the exact error shapes you'll hit.
A script that submits a text-to-video job to minimax-h3-max, waits for it to finish, and downloads the resulting .mp4 URL. Everything below was run against the live API — the parameter names, the enum values, and the error responses are copied from real requests, not from marketing copy.
Prerequisite: an API key. Grab one from the hiapi dashboard and export it:
export HIAPI_API_KEY="sk-..."
Every request authenticates with Authorization: Bearer sk-....
minimax-h3-max runs on hiapi's unified async task endpoint. You create a task, then poll (or get a callback) until it finishes.
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimax-h3-max",
"input": {
"prompt": "a red panda sipping tea on a rainy Tokyo balcony, cinematic lighting",
"duration": 6,
"resolution": "768P",
"aspect_ratio": "16:9"
}
}'
This returns a taskId immediately — the video renders asynchronously:
{"code": 0, "data": {"taskId": "tk-hiapi-..."}, "message": "success"}
Poll the task until it reaches a terminal state:
curl -s "https://api.hiapi.ai/v1/tasks/tk-hiapi-..." \
-H "Authorization: Bearer $HIAPI_API_KEY"
When data.status is success, the video URL is at data.output[0].url (data.output[0].type is "video"). That URL is temporary — the response includes an expireAt timestamp, so download the file (or re-host it on your own storage) as soon as the task completes.
The model's real schema, confirmed by probing the live endpoint (sending deliberately invalid values to force a validation error rather than guessing from docs alone):
| Field | Type | Notes |
|---|---|---|
prompt | string | required |
duration | integer | seconds; rejected outside the model's supported range |
resolution | enum | "480P" | "768P" |
aspect_ratio | enum | "21:9", "16:9", "4:3", "1:1", "3:4", "9:16" |
There is no working image-input field for minimax-h3-max on this endpoint — it's text-to-video only. If you need image-to-video, use a model whose model page advertises an image_urls input.
import os
import time
import urllib.request
import json
API_KEY = os.environ["HIAPI_API_KEY"]
BASE = "https://api.hiapi.ai/v1/tasks"
def _request(method, url, body=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(url, data=data, method=method, headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
})
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
def create_task(prompt: str) -> str:
resp = _request("POST", BASE, {
"model": "minimax-h3-max",
"input": {
"prompt": prompt,
"duration": 6,
"resolution": "768P",
"aspect_ratio": "16:9",
},
})
return resp["data"]["taskId"]
def wait_task(task_id: str, timeout: int = 300, interval: int = 5) -> dict:
deadline = time.time() + timeout
while time.time() < deadline:
resp = _request("GET", f"{BASE}/{task_id}")
status = resp["data"]["status"]
if status in ("success", "failed"):
return resp["data"]
time.sleep(interval)
raise TimeoutError(f"task {task_id} did not finish in {timeout}s")
if __name__ == "__main__":
task_id = create_task("a red panda sipping tea on a rainy Tokyo balcony, cinematic lighting")
result = wait_task(task_id)
if result["status"] == "success":
print(result["output"][0]["url"])
else:
print("failed:", result)
A one-off script can poll in a loop. A production integration needs three more things.
Use a callback instead of polling. Add a callback object to the create-task request and hiapi will POST to it once the task reaches a terminal state — no polling loop, no wasted requests:
{
"model": "minimax-h3-max",
"callback": {"url": "https://your-app.example.com/hiapi/callback", "when": "final"},
"input": {"prompt": "...", "duration": 6, "resolution": "768P", "aspect_ratio": "16:9"}
}
when currently only supports "final" (fires on both success and failure) and defaults to it if omitted. Polling is simpler for a script you run by hand; a callback is the right call once you're submitting more than a handful of jobs per minute, since you stop paying for idle polling requests.
Set an Idempotency-Key header. The create-task endpoint accepts an optional Idempotency-Key header (up to 255 bytes). Retrying a request with the same key under the same account creates the task only once — a replay returns the original taskId instead of billing a second render. Use this any time a request might be retried after a timeout, so a network hiccup doesn't turn into a duplicate charge:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Idempotency-Key: order-42-video-render" \
-H "Content-Type: application/json" \
-d '{"model": "minimax-h3-max", "input": {"prompt": "...", "duration": 6, "resolution": "768P", "aspect_ratio": "16:9"}}'
Handle the two real error shapes. An invalid or missing API key returns HTTP 401:
{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}
An invalid field (bad enum value, wrong type, out-of-range duration) returns HTTP 400:
{"code": 400, "data": null, "error_code": "INVALID_REQUEST", "message": "..."}
Branch on these explicitly — permission_denied means fix your key, INVALID_REQUEST means fix your payload, and neither is worth retrying without a change.
minimax-h3-max at 480P and 768PIs minimax-h3-max a chat or language model? No. Despite the name, it's a text-to-video model on hiapi — the input is a text prompt and the output is an MP4.
Does minimax-h3-max support image-to-video?
Not through this API. Every image-input field name we tried was rejected by the model's schema; it only accepts a text prompt. Use a model whose model page lists image_urls if you need image-to-video.
What resolutions and aspect ratios are supported?
resolution is "480P" or "768P". aspect_ratio is one of "21:9", "16:9", "4:3", "1:1", "3:4", "9:16".
How much does minimax-h3-max cost? It's billed per output second, with 768P priced higher than 480P. Check the pricing page for the current rate — hiapi updates pricing independently of this post.
Should I poll or use a callback?
Poll for scripts and low-volume testing. Switch to callback.url once you're generating enough clips that a polling loop would waste requests or add latency to your own workflow.
Why did I get a 401 with permission_denied?
Your Authorization: Bearer sk-... header is missing, malformed, or the key is invalid/revoked. Regenerate a key from the dashboard and confirm the header is set exactly as shown above.
Why did I get a 400 with INVALID_REQUEST?
One of your input fields doesn't match the schema — usually an out-of-range duration or a resolution/aspect_ratio value outside the enum. The error message names the offending field.
Can I use this from Node.js instead of Python or curl?
Yes — it's the same POST /v1/tasks / GET /v1/tasks/{taskId} pair with any HTTP client; only the request-building syntax changes.