Try MiniMax H3 Max: Reference to Video in the Workbench
Run this model interactively, tune parameters, and compare outputs.
minimax-h3-max-reference-to-video
MiniMax H3 Max (Reference to Video) is a post-trained variant of MiniMax H3, tuned by fal for stronger prompt adherence and better aesthetics. It generates video from a text prompt guided by multimodal references: up to 9 subject or style images, 3 motion video clips, and 3 audio clips.
It generates 5 to 15 second clips at 480P or 768P resolution with audio, including lip-synced dialogue, keeping subjects consistent with their reference images while following referenced motion and voices. References may total at most 12 files; video and audio clips run 2 to 15 seconds each with at most 15 seconds combined per type, and audio cannot be the only reference. Prompt expansion modes trade latency for prompt fidelity.
Example request
Use the Workbench as a request builder: configure parameters for this model in the UI, then open the API tab to copy the exact cURL or Python call.
- Sync
- Async
- Async with SSE
This blocks until the video is ready (typically 5-15 minutes). Prefer Async or Async with SSE for anything beyond quick experimentation.See the video generation reference for more details.
- Minimal
- Basic parameters
- All parameters
curl -X POST https://hub.oxen.ai/api/ai/videos/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>"
}'
import os
import requests
response = requests.post(
"https://hub.oxen.ai/api/ai/videos/generate",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['OXEN_API_KEY']}",
},
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>"
},
)
response.raise_for_status()
print(response.json())
curl -X POST https://hub.oxen.ai/api/ai/videos/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
]
}'
import os
import requests
response = requests.post(
"https://hub.oxen.ai/api/ai/videos/generate",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['OXEN_API_KEY']}",
},
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
]
},
)
response.raise_for_status()
print(response.json())
curl -X POST https://hub.oxen.ai/api/ai/videos/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
],
"aspect_ratio": "adaptive",
"resolution": "768P",
"duration": 5,
"prompt_expansion_mode": "balanced",
"enable_safety_checker": true,
"sync_mode": false
}'
import os
import requests
response = requests.post(
"https://hub.oxen.ai/api/ai/videos/generate",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['OXEN_API_KEY']}",
},
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
],
"aspect_ratio": "adaptive",
"resolution": "768P",
"duration": 5,
"prompt_expansion_mode": "balanced",
"enable_safety_checker": true,
"sync_mode": false
},
)
response.raise_for_status()
print(response.json())
See the async queue reference for more details.
- Minimal
- Basic parameters
- All parameters
# Enqueue, capture the generation id.
GEN_ID=$(curl -s -X POST https://hub.oxen.ai/api/ai/queue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>"
}' | jq -r '.generations[0].generation_id')
# Poll until the generation reaches a terminal status.
while true; do
STATUS=$(curl -s -H "Authorization: Bearer $OXEN_API_KEY" \
"https://hub.oxen.ai/api/ai/queue/$GEN_ID" | jq -r '.status')
echo "Status: $STATUS"
case $STATUS in succeeded|failed|cancelled) break;; esac
sleep 5
done
# Print the result.
curl -s -H "Authorization: Bearer $OXEN_API_KEY" \
"https://hub.oxen.ai/api/ai/queue/$GEN_ID" | jq .
import os
import time
import requests
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['OXEN_API_KEY']}",
}
enqueue = requests.post(
"https://hub.oxen.ai/api/ai/queue",
headers=HEADERS,
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>"
},
)
enqueue.raise_for_status()
generation_id = enqueue.json()["generations"][0]["generation_id"]
while True:
data = requests.get(
f"https://hub.oxen.ai/api/ai/queue/{generation_id}",
headers=HEADERS,
).json()
if data["status"] in {"succeeded", "failed", "cancelled"}:
break
time.sleep(5)
if data["status"] == "succeeded":
print(f"Result: {data['result_url']}")
else:
print(f"Generation {data['status']}: {data.get('error_message')}")
# Enqueue, capture the generation id.
GEN_ID=$(curl -s -X POST https://hub.oxen.ai/api/ai/queue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
]
}' | jq -r '.generations[0].generation_id')
# Poll until the generation reaches a terminal status.
while true; do
STATUS=$(curl -s -H "Authorization: Bearer $OXEN_API_KEY" \
"https://hub.oxen.ai/api/ai/queue/$GEN_ID" | jq -r '.status')
echo "Status: $STATUS"
case $STATUS in succeeded|failed|cancelled) break;; esac
sleep 5
done
# Print the result.
curl -s -H "Authorization: Bearer $OXEN_API_KEY" \
"https://hub.oxen.ai/api/ai/queue/$GEN_ID" | jq .
import os
import time
import requests
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['OXEN_API_KEY']}",
}
enqueue = requests.post(
"https://hub.oxen.ai/api/ai/queue",
headers=HEADERS,
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
]
},
)
enqueue.raise_for_status()
generation_id = enqueue.json()["generations"][0]["generation_id"]
while True:
data = requests.get(
f"https://hub.oxen.ai/api/ai/queue/{generation_id}",
headers=HEADERS,
).json()
if data["status"] in {"succeeded", "failed", "cancelled"}:
break
time.sleep(5)
if data["status"] == "succeeded":
print(f"Result: {data['result_url']}")
else:
print(f"Generation {data['status']}: {data.get('error_message')}")
# Enqueue, capture the generation id.
GEN_ID=$(curl -s -X POST https://hub.oxen.ai/api/ai/queue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
],
"aspect_ratio": "adaptive",
"resolution": "768P",
"duration": 5,
"prompt_expansion_mode": "balanced",
"enable_safety_checker": true,
"sync_mode": false
}' | jq -r '.generations[0].generation_id')
# Poll until the generation reaches a terminal status.
while true; do
STATUS=$(curl -s -H "Authorization: Bearer $OXEN_API_KEY" \
"https://hub.oxen.ai/api/ai/queue/$GEN_ID" | jq -r '.status')
echo "Status: $STATUS"
case $STATUS in succeeded|failed|cancelled) break;; esac
sleep 5
done
# Print the result.
curl -s -H "Authorization: Bearer $OXEN_API_KEY" \
"https://hub.oxen.ai/api/ai/queue/$GEN_ID" | jq .
import os
import time
import requests
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['OXEN_API_KEY']}",
}
enqueue = requests.post(
"https://hub.oxen.ai/api/ai/queue",
headers=HEADERS,
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
],
"aspect_ratio": "adaptive",
"resolution": "768P",
"duration": 5,
"prompt_expansion_mode": "balanced",
"enable_safety_checker": true,
"sync_mode": false
},
)
enqueue.raise_for_status()
generation_id = enqueue.json()["generations"][0]["generation_id"]
while True:
data = requests.get(
f"https://hub.oxen.ai/api/ai/queue/{generation_id}",
headers=HEADERS,
).json()
if data["status"] in {"succeeded", "failed", "cancelled"}:
break
time.sleep(5)
if data["status"] == "succeeded":
print(f"Result: {data['result_url']}")
else:
print(f"Generation {data['status']}: {data.get('error_message')}")
See the async queue reference for more details.
- Minimal
- Basic parameters
- All parameters
# Enqueue, capture the generation id.
GEN_ID=$(curl -s -X POST https://hub.oxen.ai/api/ai/queue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>"
}' | jq -r '.generations[0].generation_id')
# Stream the SSE channel, grab the data line that follows a
# media_generation_completed event for our id, and pretty-print it.
curl -sN -H "Authorization: Bearer $OXEN_API_KEY" https://hub.oxen.ai/api/events \
| awk -v id="$GEN_ID" '
/^event: media_generation_completed$/ { expect=1; next }
/^data: / && expect {
payload = substr($0, 7)
if (index(payload, "\"generation_id\":\"" id "\"")) { print payload; exit }
expect = 0
}
' | jq .
import json
import os
import requests
API_KEY = os.environ["OXEN_API_KEY"]
AUTH = {"Authorization": f"Bearer {API_KEY}"}
enqueue = requests.post(
"https://hub.oxen.ai/api/ai/queue",
headers={**AUTH, "Content-Type": "application/json"},
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>"
},
)
enqueue.raise_for_status()
generation_id = enqueue.json()["generations"][0]["generation_id"]
with requests.get(
"https://hub.oxen.ai/api/events",
headers=AUTH,
stream=True,
) as stream:
event_name = None
for line in stream.iter_lines(decode_unicode=True):
if line.startswith("event: "):
event_name = line.removeprefix("event: ")
elif line.startswith("data: ") and event_name == "media_generation_completed":
payload = json.loads(line.removeprefix("data: "))
if payload.get("generation_id") == generation_id:
print(payload)
break
# Enqueue, capture the generation id.
GEN_ID=$(curl -s -X POST https://hub.oxen.ai/api/ai/queue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
]
}' | jq -r '.generations[0].generation_id')
# Stream the SSE channel, grab the data line that follows a
# media_generation_completed event for our id, and pretty-print it.
curl -sN -H "Authorization: Bearer $OXEN_API_KEY" https://hub.oxen.ai/api/events \
| awk -v id="$GEN_ID" '
/^event: media_generation_completed$/ { expect=1; next }
/^data: / && expect {
payload = substr($0, 7)
if (index(payload, "\"generation_id\":\"" id "\"")) { print payload; exit }
expect = 0
}
' | jq .
import json
import os
import requests
API_KEY = os.environ["OXEN_API_KEY"]
AUTH = {"Authorization": f"Bearer {API_KEY}"}
enqueue = requests.post(
"https://hub.oxen.ai/api/ai/queue",
headers={**AUTH, "Content-Type": "application/json"},
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
]
},
)
enqueue.raise_for_status()
generation_id = enqueue.json()["generations"][0]["generation_id"]
with requests.get(
"https://hub.oxen.ai/api/events",
headers=AUTH,
stream=True,
) as stream:
event_name = None
for line in stream.iter_lines(decode_unicode=True):
if line.startswith("event: "):
event_name = line.removeprefix("event: ")
elif line.startswith("data: ") and event_name == "media_generation_completed":
payload = json.loads(line.removeprefix("data: "))
if payload.get("generation_id") == generation_id:
print(payload)
break
# Enqueue, capture the generation id.
GEN_ID=$(curl -s -X POST https://hub.oxen.ai/api/ai/queue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OXEN_API_KEY" \
-d '{
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
],
"aspect_ratio": "adaptive",
"resolution": "768P",
"duration": 5,
"prompt_expansion_mode": "balanced",
"enable_safety_checker": true,
"sync_mode": false
}' | jq -r '.generations[0].generation_id')
# Stream the SSE channel, grab the data line that follows a
# media_generation_completed event for our id, and pretty-print it.
curl -sN -H "Authorization: Bearer $OXEN_API_KEY" https://hub.oxen.ai/api/events \
| awk -v id="$GEN_ID" '
/^event: media_generation_completed$/ { expect=1; next }
/^data: / && expect {
payload = substr($0, 7)
if (index(payload, "\"generation_id\":\"" id "\"")) { print payload; exit }
expect = 0
}
' | jq .
import json
import os
import requests
API_KEY = os.environ["OXEN_API_KEY"]
AUTH = {"Authorization": f"Bearer {API_KEY}"}
enqueue = requests.post(
"https://hub.oxen.ai/api/ai/queue",
headers={**AUTH, "Content-Type": "application/json"},
json={
"model": "minimax-h3-max-reference-to-video",
"prompt": "<prompt>",
"input_images": [
"https://hub.oxen.ai/api/repos/elau/assets/file/main/bloxy/bloxy_cropped_512x512.png"
],
"input_videos": [
"https://hub.oxen.ai/api/repos/ox/Oxen-AI-Assets/file/main/images/winter_summer_ox.mp4"
],
"input_audios": [
"https://example.com/audio.mp3"
],
"aspect_ratio": "adaptive",
"resolution": "768P",
"duration": 5,
"prompt_expansion_mode": "balanced",
"enable_safety_checker": true,
"sync_mode": false
},
)
enqueue.raise_for_status()
generation_id = enqueue.json()["generations"][0]["generation_id"]
with requests.get(
"https://hub.oxen.ai/api/events",
headers=AUTH,
stream=True,
) as stream:
event_name = None
for line in stream.iter_lines(decode_unicode=True):
if line.startswith("event: "):
event_name = line.removeprefix("event: ")
elif line.startswith("data: ") and event_name == "media_generation_completed":
payload = json.loads(line.removeprefix("data: "))
if payload.get("generation_id") == generation_id:
print(payload)
break
Fetch model details
The models endpoint returns the full model object, including itsjson_request_schema.
curl -H "Authorization: Bearer $OXEN_API_KEY" https://hub.oxen.ai/api/ai/models/minimax-h3-max-reference-to-video
Request parameters
Required parameters
| Field | Type | Default | Description |
|---|---|---|---|
prompt | string | — | Text prompt for video generation. Use @Image1, @Image2, … to reference the reference images in order, @Video1, … for the reference videos, and @Audio1, … for the reference audio; each type is numbered separately. |
Optional parameters
| Field | Type | Default | Description |
|---|---|---|---|
input_images | array<string> | — | Reference images for subjects or style, up to 9. Order maps to @Image1, @Image2, etc. in the prompt. Reference images, videos, and audio must total at most 12 files. |
input_videos | array<string> | — | Reference video clips for motion or style, up to 3 clips of 2 to 15 seconds (15 seconds combined). Order maps to @Video1, @Video2, etc. in the prompt. Reference images, videos, and audio must total at most 12 files. |
input_audios | array<string> | — | Reference audio for voices or sound, up to 3 clips of 2 to 15 seconds (15 seconds combined). Order maps to @Audio1, @Audio2, etc. in the prompt. Cannot be the only reference input; provide at least one reference image or video with it. |
aspect_ratio | string | "adaptive" | The aspect ratio of the generated video. One of: adaptive, 21:9, 16:9, 4:3, 1:1, 3:4, 9:16. |
resolution | string | "768P" | The native generation resolution of the video. One of: 480P, 768P. |
duration | integer | 5 | The duration of the video in seconds. Range: 5 – 15. |
prompt_expansion_mode | string | "balanced" | How much effort to spend rewriting the prompt before generation. ‘balanced’ returns in about a second. ‘quality’ spends up to ~30s on a richer prompt. One of: balanced, quality. |
enable_safety_checker | boolean | true | If set to true, the safety checker will be enabled. |
sync_mode | boolean | false | Return the generated video as base64 instead of a CDN URL. |
seed | integer | — | Random seed. A random seed is selected when omitted. |