by 0G Foundation
MiniMax-H3 (Hailuo-03) text-to-video and image-to-video, with first/last-frame control and multimodal references (images, video, audio). 4–15 second clips at 2K. Billed per second of output, plus per second of reference video; reference images beyond five are charged per image.
$0.195
per generated second
2K
billing tiers — not the size you send
Async API — Video generation uses an asynchronous flow: submit a job, poll until it completes, then download the file from a separate endpoint. Generation takes minutes, so poll every 3-5 seconds. The examples below show all three steps.
# 1. Submit the job. model, prompt and seconds are required. Each account may hold only a small number of
# unfinished jobs at once — 5 by default, set per deployment — and one over that returns 429
# video_jobs_in_flight_limit.
#
# The model's own options go under "metadata"; every field and the values it accepts are listed below.
# Media references go in metadata.content as {type, <type>: {url}, role}. Images, reference videos and reference
# audio each take a URL or a base64 data: URI; label audio data:audio/mp3 or data:audio/wav (with
# data:audio/mpeg the job is accepted, then fails unbilled). The whole request is capped at 24 MB, so send a
# large video as a URL. First/last frame and reference media cannot be mixed in one request.
curl "https://router-api.0g.ai/v1/videos" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-d '{
"model": "minimax-h3",
"prompt": "A cat surfing a wave at sunset",
"seconds": 5,
"metadata": {
"resolution": "2K",
"ratio": "16:9",
"aigc_watermark": false
}
}'
# Response: {"id": "v0_426354818031858", "status": "queued", "provider_address": "0x..."}
# 2. Poll until status is "completed". Generation takes minutes — 3-5s is a sane interval. The poll answers
# 200 and reports the job's own status, so a failure arrives as 200 + {"status":"failed"}, NOT as an error
# code. The SUBMIT response echoes what you sent; the POLL response reports what was actually rendered — so
# reconcile against the poll. Which fields carry that answer depends on the upstream:
# usage.output_video_duration when the provider reported a duration, size/seconds on some providers and not
# others. Read what is present rather than assuming a particular field.
curl "https://router-api.0g.ai/v1/videos/<VIDEO_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>"
# 3. Download the finished file. This is where 409 / 424 / 429 live: 409 = not ready yet, 429 = too many
# concurrent downloads (both retryable), 424 = the job failed and has no output (terminal).
curl "https://router-api.0g.ai/v1/videos/<VIDEO_ID>/content" \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-o video.mp4
| Input | role | Type | Max | Scenario |
|---|---|---|---|---|
| First frame | first_frame | Image | 1 | First / last frame |
| Last frameRequires First frame | last_frame | Image | 1 | First / last frame |
| Reference image | reference_image | Image | 9 | Multimodal reference |
| Reference video | reference_video | Video | 3 | Multimodal reference |
| Reference audio | reference_audio | Audio | 3 | Multimodal reference |
Media references go in metadata.content as {type, <type>: {url}, role}. Images, reference videos and reference audio each take a URL or a base64 data: URI; label audio data:audio/mp3 or data:audio/wav (with data:audio/mpeg the job is accepted, then fails unbilled). The whole request is capped at 24 MB, so send a large video as a URL. First/last frame and reference media cannot be mixed in one request.
| Field | Values | Default |
|---|---|---|
| metadata.resolution | 2K | 2K |
| metadata.ratio | adaptive · 21:9 · 16:9 · 4:3 · 1:1 · 3:4 · 9:16 | adaptive |
| seconds | 4–15s | 5 |
| metadata.aigc_watermark | true · false | false |
Billed per second of output plus per second of reference video; reference images beyond 5 are charged per image.
Provider Routing
Control which provider handles your request using the X-0G-Provider-* request headers.
X-0G-Provider-Address pins a provider; X-0G-Provider-Identity pins which of its upstreams to route through — both values come from the address and provider_identity fields of GET /v1/providers. Together they address one specific upstream of one provider. Send neither and the router picks by price and health, exactly as before.
curl "https://router-api.0g.ai/v1/videos" \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-H "X-0G-Provider-Address: <PROVIDER_ADDRESS>" \
-H "X-0G-Provider-Identity: <UPSTREAM>" \
-H "X-0G-Provider-Sort: latency" \
-H "X-0G-Provider-Allow-Fallbacks: true" \
-d '{
"model": "minimax-h3",
"prompt": "A cat surfing a wave at sunset",
"seconds": 6
}'