FLUX 3 Max Keyframes to Video API
blackforestlabs/flux-3-max/keyframes-to-videoFLUX 3 Max Keyframes to Video turns 1–10 images pinned to exact frames into a single 5–20 second 720p or 1080p clip with native audio. Each image appears at the frame_index you set on a 24 fps timeline, so the story beats land where you planned them.
Drag and drop files, paste from clipboard, or provide a URL. Required. Add 1–10 JPEG, PNG or WebP images, then set each Frame Index on the 24 fps timeline.
3/10 files added
Each Frame Index is a unique whole number from 0 to 192 (duration × 24).
REST API
Quick Start
Make your first FLUX 3 Max Keyframes to Video request in three steps. The example pins three images to frames 0, 96 and 192 of an 8-second clip and returns a task_id.
Connect to the Vidgo API
Create an API key, keep it on your server, and send it in the Authorization header as a Bearer token.
- Endpoint
- POST
https://api.vidgo.ai/api/generate/submit - Authentication
- Authorization: Bearer VIDGO_API_KEY
Submit one generation task
Run the short example with a prompt and at least one keyframe.
curl --request POST \
--url "https://api.vidgo.ai/api/generate/submit" \
--header "Authorization: Bearer $VIDGO_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"model": "blackforestlabs/flux-3-max/keyframes-to-video",
"input": {
"prompt": "A paper lantern festival on a quiet river at night, filmed as one smooth drifting shot. Follow the three keyframes in order: a single lantern being lit on the bank, the lantern floating among many others mid-river, then a wide view of hundreds of lanterns passing under a stone bridge. Keep the water calm and the lantern colours warm. Native audio: soft water lapping, faint crowd murmur, wooden boat creak. No text, no logos, no watermark.",
"duration": 8,
"resolution": "720p",
"aspect_ratio": "auto",
"sound": true,
"keyframes": [
{
"image_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v1/01/input-keyframe-01.webp",
"frame_index": 0
},
{
"image_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v1/01/input-keyframe-02.webp",
"frame_index": 96
},
{
"image_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v1/01/input-keyframe-03.webp",
"frame_index": 192
}
]
}
}'Wait for the result
Use the returned task_id to check the same task until it reaches finished or failed. On success, read data.files[].file_url.
Track status
GET https://api.vidgo.ai/api/generate/status/{task_id}Poll every few seconds while the status is not_started or running, and stop on finished or failed. On success, read data.files[].file_url. For longer clips, lengthen the interval gradually or add callback_url to the submit request.
not_startedrunningfinishedfailed{
"code": 200,
"data": {
"task_id": "PLO2R6GLU1NN669K",
"status": "not_started",
"created_time": "2026-11-05T07:04:46"
}
}{
"code": 200,
"data": {
"task_id": "PLO2R6GLU1NN669K",
"status": "finished",
"files": [
{
"file_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v2/01/output-01.mp4",
"file_type": "video"
}
],
"created_time": "2026-11-05T07:04:46",
"error_message": null,
"progress": 100
}
}Complete runnable example
Open this after the three-step flow is clear. It combines submission, polling, terminal-state handling and result extraction in one script.
set -euo pipefail
: "${VIDGO_API_KEY:?Set VIDGO_API_KEY in your environment}"
REQUEST_BODY=$(cat <<'JSON'
{
"model": "blackforestlabs/flux-3-max/keyframes-to-video",
"input": {
"prompt": "A paper lantern festival on a quiet river at night, filmed as one smooth drifting shot. Follow the three keyframes in order: a single lantern being lit on the bank, the lantern floating among many others mid-river, then a wide view of hundreds of lanterns passing under a stone bridge. Keep the water calm and the lantern colours warm. Native audio: soft water lapping, faint crowd murmur, wooden boat creak. No text, no logos, no watermark.",
"duration": 8,
"resolution": "720p",
"aspect_ratio": "auto",
"sound": true,
"keyframes": [
{
"image_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v1/01/input-keyframe-01.webp",
"frame_index": 0
},
{
"image_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v1/01/input-keyframe-02.webp",
"frame_index": 96
},
{
"image_url": "https://cdn.vidgo.ai/apis/models/blackforestlabs/flux-3-max/keyframes-to-video/v1/01/input-keyframe-03.webp",
"frame_index": 192
}
]
}
}
JSON
)
SUBMIT_RESPONSE=$(curl --silent --show-error --fail-with-body \
--request POST \
--url "https://api.vidgo.ai/api/generate/submit" \
--header "Authorization: Bearer $VIDGO_API_KEY" \
--header "Content-Type: application/json" \
--data "$REQUEST_BODY")
TASK_ID=$(printf '%s' "$SUBMIT_RESPONSE" | jq -r '.data.task_id // .task_id // empty')
if [ -z "$TASK_ID" ]; then
printf 'Submit response did not include task_id:
%s
' "$SUBMIT_RESPONSE" >&2
exit 1
fi
while true; do
STATUS_RESPONSE=$(curl --silent --show-error --fail-with-body \
--url "https://api.vidgo.ai/api/generate/status/$TASK_ID" \
--header "Authorization: Bearer $VIDGO_API_KEY")
STATUS=$(printf '%s' "$STATUS_RESPONSE" | jq -r '.data.status // .status // empty')
case "$STATUS" in
finished)
printf '%s' "$STATUS_RESPONSE" | jq -r '(.data.files // .files // [])[]?.file_url'
break
;;
failed)
printf '%s' "$STATUS_RESPONSE" | jq -r '.data.error_message // .error_message // "Generation failed"' >&2
exit 1
;;
not_started|running)
sleep 2
;;
*)
printf 'Unexpected task status: %s
' "$STATUS" >&2
exit 1
;;
esac
doneRequest Parameters
model and callback_url belong at the request root; generation parameters belong inside input.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| model | string | Yes | — | Fixed to blackforestlabs/flux-3-max/keyframes-to-video. |
| callback_url | string (URL) | No | — | A public HTTP(S) URL that receives the task result when it finishes. |
| input | object | Yes | — | Generation parameters: a prompt and 1–10 keyframes, plus optional duration, resolution, aspect_ratio and sound. |
| input.prompt | string | Yes | — | Text prompt for the shot, including audio cues when sound is on. |
| input.keyframes | object[] | Yes | — | 1–10 items of {image_url, frame_index}; frame_index unique, 0 to duration × 24. |
| input.duration | integer | No | 5 | Whole seconds, 5–20. |
| input.resolution | string | No | 720p | 720p or 1080p. |
| input.aspect_ratio | string | No | auto | auto, 21:9, 2:1, 16:9, 4:3, 1:1, 3:4, 9:16. |
| input.sound | boolean | No | true | Native audio on or off; does not change the price. |
Response Fields
Submission returns the task identity immediately. Status responses add progress, output files or a failure message as the task advances.
| Field | Type | Description |
|---|---|---|
| code | integer | Application result code. Successful responses use 0 or 200. |
| message | string | Human-readable response or error message when present. |
| data.task_id | string | Task identifier used in the status endpoint path. |
| data.status | string | Current state: not_started, running, finished or failed. |
| data.created_time | string | Task creation time in date-time format. |
| data.progress | number | Completion percentage when the provider reports progress. |
| data.files[] | array | Generated output files returned after a successful task. |
| data.files[].file_url | string | Public URL of the generated video. |
| data.files[].file_type | string | Generated file type, such as video. |
| data.files[].watermark_url | string | null | Watermarked output URL when one is available. |
| data.error_message | string | null | Failure detail when status is failed. |
Task Lifecycle
Treat not_started and running as non-terminal states. finished and failed are terminal; stop polling when either is returned.
not_startedThe task has been accepted and is queued to start.
runningThe video is being generated. Keep polling the status endpoint.
finishedThe task is complete. Read the video URL from data.files[].file_url.
failedGeneration failed. Read data.error_message for details and stop polling.
Polling and Errors
- Authentication401 means the Bearer API key is missing or invalid. Check your key in the dashboard before retrying.
- Validation400 means a request field is invalid or belongs to another endpoint. Check each keyframes item and its frame_index range.
- Polling intervalPoll every few seconds while the task runs, and lengthen the interval gradually for longer clips.
- Terminal statesPoll only while the status is not_started or running. Stop on finished or failed.
- Callback optionAdd callback_url at the request root to receive the final result without continuous polling.
Model Specifications
| Specification | Value | Description |
|---|---|---|
| Model | blackforestlabs/flux-3-max/keyframes-to-video | Model identifier for asynchronous keyframe-guided video generation. |
| Duration | 5–20 | Whole seconds; defaults to 5. |
| Resolution | 720p | 1080p | Defaults to 720p. |
| Aspect ratio | auto | 21:9 | 2:1 | 16:9 | 4:3 | 1:1 | 3:4 | 9:16 | Defaults to auto. |
| Pricing | 34 / 58 credits/s | 720p / 1080p, per output second; audio is included. |
Related Models
FLUX 3 Max Keyframes to Video API FAQ
What is the FLUX 3 Max Keyframes to Video API?
FLUX 3 Max Keyframes to Video is a video model post-trained by fal in collaboration with Black Forest Labs for generating video from timed keyframes. It turns 1–10 images pinned to frame positions into one 5–20 second 720p or 1080p clip with native audio. Built on the FLUX 3 video base, it holds each image at its frame on a 24 fps timeline while generating continuous motion and sound between them. You can call it programmatically or try it from the playground above.
How do I calculate frame_index for FLUX 3 Max Keyframes to Video?
Multiply the time in seconds by 24. For a 5-second clip, frames 0, 60 and 120 mark the start, middle and end; for 10 seconds the last frame is 240. Each value must be unique.
How many keyframes can FLUX 3 Max Keyframes to Video take?
Send 1 to 10 keyframes. Each item contains only image_url and frame_index, and the frames can be in any position up to duration × 24.
What happens when a FLUX 3 Max keyframe sits beyond the clip length?
The request is rejected with a 400 error that names the keyframe. Increase duration or move the frame so frame_index stays within 0 to duration × 24.
Which aspect ratio does FLUX 3 Max Keyframes to Video use?
With aspect_ratio on auto, the output follows the first keyframe image. Pick one of the seven fixed ratios to frame the sequence for a specific platform.
Does the number of keyframes change the FLUX 3 Max price?
No. FLUX 3 Max Keyframes to Video costs 34 credits per output second at 720p and 58 at 1080p, so an 8-second 720p clip is 272 credits with one keyframe or ten.
How do FLUX 3 Max keyframes compare with start and end frames?
FLUX 3 Max Image to Video fixes only the opening and closing frames. Keyframes to Video adds intermediate images at exact times, which gives more control over pacing and the order of events.















