AI/ML API Video Keyframes (Kling / Veo / Runway)
Kling, Veo 3.1, or Runway Gen-4 in one node
- first_image
- last_image
- video_url
- generation_id
Some shots are about the motion between two fixed moments: a camera push, a character turning, a scene morphing from one state to another. This node handles exactly that - you hand it a start frame and an end frame, it produces the video that connects them. It's the pack's keyframe node, and it wraps three different closed video models behind one interface, which is honestly the most useful thing about it: Kling, Google Veo 3.1 (fast first/last), and Runway Gen-4 Turbo, all in a single dropdown.
Mechanically it shares the pack's video pipeline - POST to https://api.aimlapi.com/v2/video/generations, poll every poll_interval seconds up to poll_timeout (default 480s), surface failure states like canceled/expired, and emit video_url + generation_id. The detail worth knowing is in the code: the field name for the end frame isn't universal. Kling and Veo expect last_image_url, but Runway's API calls it tail_image_url - the node picks the right field per model, which is the kind of thing that would otherwise turn into a cryptic 400 error.
The inputs that matter. api_key, model, prompt (the high-level direction for the interpolation - "smooth cinematic camera push between both frames" is the sensible default), plus:
first_image/first_image_url- always required, your starting frame. Give a tensor (encoded to a data URI) or a direct HTTPS URL to skip encoding.last_image/last_image_url- required for Veo first/last, optional for Kling and Runway (both of which happily animate from a single frame into motion).duration(4–12s),aspect_ratio(sent to all three),resolution(only used by Veo - 720p or 1080p),seed(Runway only, -1 for random), andgenerate_audio(only Veo supports it here).
The output pairing is where this shines in a real workflow: video_url for playback or a downloader node, and generation_id so you can reconcile a failed or lost job against AI/ML API's dashboard.
Installing it. Same as every node in this pack: ComfyUI Manager search comfyui-aimlapi-custom-nodes, or
cd ComfyUI/custom_nodes
git clone https://github.com/konradbjk/comfyui-aimlapi-custom-nodes
Restart, look under "AI/ML API". The deps are just requests, pillow, and numpy; no models to download, and the actual generation happens on AI/ML API's servers for per-job billing. Key goes in the masked api_key widget from https://aimlapi.com/app/keys.
What usually bites people. The number one stumble is forgetting the second frame on Veo - the node throws a clear "Last image is required" error, but only because the code explicitly validates it. Also know what you're paying for: all three of these are closed commercial models, and Kling and Runway sit at the premium end of video pricing, so an 12-second keyframe interpolation is not a free experiment. And as with the other video nodes, this one blocks your ComfyUI queue while polling - if your job is slow, raise poll_timeout rather than assuming it's hung.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| api_key | STRING | Your AI/ML API key from https://aimlapi.com/app/keys | |
| model | COMBO | kling-ai/video-o1-image-to-video | Select which keyframe-aware model to run |
| prompt | STRING | Smooth cinematic camera push between both frames | High-level direction for the interpolation |
| first_imageopt | IMAGE | Starting frame tensor | |
| first_image_urlopt | STRING | HTTPS link or data URI if no tensor is provided | |
| last_imageopt | IMAGE | Ending frame tensor | |
| last_image_urlopt | STRING | HTTPS link or data URI for the ending frame | |
| durationopt | INT | 84–12 | Video duration (seconds) |
| aspect_ratioopt | COMBO | 16:9 | Framing passed to models that support it |
| resolutionopt | COMBO | 1080p | Only used by Google Veo first/last |
| seedopt | INT | -1-1–999999999 | Seed (Runway Gen-4 Turbo only, -1 for random) |
| generate_audioopt | BOOLEAN | false | Toggle audio generation if the model supports it |
| poll_intervalopt | FLOAT | 51–30 | Seconds between status checks |
| poll_timeoutopt | INT | 48060–1800 | Maximum seconds to wait before timing out |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| video_url | STRING | — |
| generation_id | STRING | — |