alibaba/happy-horse/video-edit
Input
Hint: Drag and drop video files from your computer, video from web pages, paste from clipboard (Ctrl/Cmd+V), or provide a URL. Accepted file types: mp4, mov, webm, m4v, gif
Hint: Drag and drop files from your computer, images from web pages, paste from clipboard (Ctrl/Cmd+V), or provide a URL.
Customize your input with more control.
Result
What would you like to do next?
For every second of 720p video you generated, you will be charged $0.28/second ($0.14 per input second and 0.14 per output second). For 1080p video you will be charged $0.56/second.
Logs
Run Happy Horse 1.0: AI Video Editor API
Edit an existing video using natural language instructions. Supports both local edits (targeted changes to specific elements) and global edits (style, color, atmosphere) with optional reference images to guide the result.
Model ID: `alibaba/happy-horse/video-edit`
Provider: fal.ai
Commercial rights: Full commercial rights on all outputs
About the model
The video-edit endpoint exposes Happy Horse's Video-to-Video (V2V) capability: the model takes a source clip and a text prompt, then modifies the video while preserving its original structure, composition, and motion.
Two editing modes are supported:
- Global edits: apply a change across the entire clip: recoloring the sky, shifting the visual style, changing the season or time of day, altering the mood or lighting
- Local edits: target a specific region or element: swapping an object, changing a character's outfit, replacing a background, making surgical fixes to a single area
You can supply up to 5 reference images alongside the prompt, referencing them in the prompt as `@Image1`, `@Image2`, etc. — for example, to specify what a replaced subject should look like, or to lock in a visual style from another image.
Specifications
| Property | Value |
|---|---|
| Input formats | MP4, MOV (H.264 recommended) |
| Input duration | 3–60 seconds |
| Output duration | Matches input, capped at 15 seconds (longer inputs truncated to first 15 s) |
| Input resolution | Longer side ≤ 2160px, shorter side ≥ 320px |
| Input aspect ratio | Between 1:2.5 and 2.5:1 |
| Input frame rate | > 8 fps |
| Input max file size | 100 MB |
| Output resolution | 720p, 1080p (aspect ratio preserved from source) |
| Reference images | Up to 5, min 300px, max 10 MB each |
| Prompt length | Up to 2,500 characters |
Pricing
| Resolution | Price |
|---|---|
| 720p | $0.14 / second |
| 1080p | $0.28 / second |
Pricing is based on the output video duration, not the input.
Prompting tips
Global edits: describe the overall change you want applied to the whole clip.
`"Recolor the sky to a deep purple sunset."`
`"Transform the scene to look like it was shot in winter — snow on the ground, frosted breath."`
`"Apply a cinematic noir grade: high contrast, desaturated, blue shadows."`
Local edits: be specific about what to change and where.
`"Replace the jacket the person is wearing with a red leather jacket."`
`"Swap the coffee cup on the table with a glass of wine."`
Reference image edits: use `@Image1`, `@Image2`, etc. to refer to uploaded reference images.
`"Replace the character's face with @Image1, preserving all motion and expression."`
`"Apply the visual style of @Image1 to the scene lighting and color palette."`
Quickstart
Install
JavaScript:
bashnpm install @fal-ai/client
Python:
bashpip install fal-client
Set your API key
bashexport FAL_KEY="YOUR_API_KEY"
Submit a request
JavaScript:
jsimport { fal } from "@fal-ai/client"; const result = await fal.subscribe("alibaba/happy-horse/video-edit", { input: { video_url: "https://example.com/your-video.mp4", prompt: "Recolor the sky to a deep purple sunset.", resolution: "1080p", audio_setting: "auto", }, logs: true, onQueueUpdate: (update) => { if (update.status === "IN_PROGRESS") { update.logs.map((log) => log.message).forEach(console.log); } }, }); console.log(result.data.video.url);
Python:
pythonimport fal_client def on_queue_update(update): if isinstance(update, fal_client.InProgress): for log in update.logs: print(log["message"]) result = fal_client.subscribe( "alibaba/happy-horse/video-edit", arguments={ "video_url": "https://example.com/your-video.mp4", "prompt": "Recolor the sky to a deep purple sunset.", "resolution": "1080p", "audio_setting": "auto", }, with_logs=True, on_queue_update=on_queue_update, ) print(result["video"]["url"])
With reference images
JavaScript:
jsimport { fal } from "@fal-ai/client"; const result = await fal.subscribe("alibaba/happy-horse/video-edit", { input: { video_url: "https://example.com/your-video.mp4", prompt: "Replace the character's outfit with the style shown in @Image1.", reference_image_urls: [ "https://example.com/outfit-reference.jpg", ], resolution: "1080p", audio_setting: "origin", }, logs: true, onQueueUpdate: (update) => { if (update.status === "IN_PROGRESS") { update.logs.map((log) => log.message).forEach(console.log); } }, }); console.log(result.data.video.url);
Python:
pythonimport fal_client def on_queue_update(update): if isinstance(update, fal_client.InProgress): for log in update.logs: print(log["message"]) result = fal_client.subscribe( "alibaba/happy-horse/video-edit", arguments={ "video_url": "https://example.com/your-video.mp4", "prompt": "Replace the character's outfit with the style shown in @Image1.", "reference_image_urls": [ "https://example.com/outfit-reference.jpg", ], "resolution": "1080p", "audio_setting": "origin", }, with_logs=True, on_queue_update=on_queue_update, ) print(result["video"]["url"])
Note: Reference images must be publicly accessible URLs. Use
`fal.storage.upload()`(JS) or`fal_client.upload_file()`(Python) to upload local files first.
Input parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
`video_url` | string | required | Source video to edit. MP4 or MOV, H.264 recommended. Max 100 MB, 3–60 s, frame rate > 8 fps. |
`prompt` | string | required | Text description of the desired edit. Reference images with `@Image1`–`@Image5`. Max 2,500 characters. |
`reference_image_urls` | list<string> | — | Up to 5 images to guide the edit. JPEG, PNG, or WEBP. Min 300px, max 10 MB each, aspect ratio 1:2.5–2.5:1. |
`resolution` | `"720p"` | `"1080p"` | `"1080p"` | Output video resolution. |
`audio_setting` | `"auto"` | `"origin"` | `"auto"` | `"auto"`: model decides whether to regenerate audio. `"origin"`: preserve the original audio track from the input. |
`seed` | integer (0–2,147,483,647) | — | Set for reproducible outputs. |
`enable_safety_checker` | boolean | `true` | Content moderation on input and output. |
Output
json{ "video": { "url": "https://...", "content_type": "video/mp4", "file_name": "output.mp4", "file_size": 4404019, "width": 1920, "height": 1080, "fps": 24, "duration": 5.0, "num_frames": 120 }, "seed": 1234567 }
The output preserves the source aspect ratio. Duration matches the input, capped at 15 seconds.
Queue API (long-running requests)
Video editing can take longer than generation. Use the queue API to avoid blocking.
JavaScript:
jsimport { fal } from "@fal-ai/client"; // Submit const { request_id } = await fal.queue.submit("alibaba/happy-horse/video-edit", { input: { video_url: "https://example.com/your-video.mp4", prompt: "Transform the scene to winter: snow on the ground, frosted breath, cold blue light.", resolution: "1080p", audio_setting: "origin", }, webhookUrl: "https://your-server.com/webhook", }); // Poll status const status = await fal.queue.status("alibaba/happy-horse/video-edit", { requestId: request_id, logs: true, }); // Fetch result once complete const result = await fal.queue.result("alibaba/happy-horse/video-edit", { requestId: request_id, }); console.log(result.data.video.url);
Python:
pythonimport fal_client # Submit handler = fal_client.submit( "alibaba/happy-horse/video-edit", arguments={ "video_url": "https://example.com/your-video.mp4", "prompt": "Transform the scene to winter: snow on the ground, frosted breath, cold blue light.", "resolution": "1080p", "audio_setting": "origin", }, webhook_url="https://your-server.com/webhook", ) request_id = handler.request_id # Poll status status = fal_client.status("alibaba/happy-horse/video-edit", request_id, with_logs=True) # Fetch result once complete result = fal_client.result("alibaba/happy-horse/video-edit", request_id) print(result["video"]["url"])
Uploading files
If your source video or reference images aren't already hosted, upload them before submitting.
JavaScript:
jsimport { fal } from "@fal-ai/client"; const videoFile = new File([videoBuffer], "source.mp4", { type: "video/mp4" }); const videoUrl = await fal.storage.upload(videoFile); const imageFile = new File([imageBuffer], "reference.jpg", { type: "image/jpeg" }); const imageUrl = await fal.storage.upload(imageFile);
Python:
pythonimport fal_client video_url = fal_client.upload_file("path/to/source.mp4") image_url = fal_client.upload_file("path/to/reference.jpg")
Client-side usage
Security: Never expose your
`FAL_KEY`in browser or mobile code. Route requests through a server-side proxy: set`FAL_KEY`as a server environment variable and have your frontend call your own backend endpoint, which forwards the request to fal.
Related models
| Model | Use case |
|---|---|
`alibaba/happy-horse/text-to-video` | Generate video from a text prompt |
`alibaba/happy-horse/image-to-video` | Animate a still image as the first frame |
`alibaba/happy-horse/reference-to-video` | Generate video with subject consistency from 1–9 reference images |
