Learn how to edit and transform existing videos with AI.
Transform and edit existing videos using text prompts, reference images, or both. Upload a video, describe the changes you want, and Lucy 2.1 will modify it while preserving motion and temporal consistency — perfect for style transfers, object modifications, character replacements, and visual transformations.
import asynciofrom decart import DecartClient, modelsasync def edit_video(): async with DecartClient(api_key="your-api-key-here") as client: with open("input-video.mp4", "rb") as video_file: result = await client.queue.submit_and_poll({ "model": models.video("lucy-latest"), "prompt": "Change the car to a vintage motorcycle", "data": video_file, "on_status_change": lambda job: print(f"Status: {job.status}"), }) if result.status == "completed": with open("edited-video.mp4", "wb") as f: f.write(result.data)asyncio.run(edit_video())
import { createDecartClient, models } from "@decartai/sdk";import { readFileSync, writeFileSync } from "fs";const client = createDecartClient({ apiKey: "your-api-key-here" });const videoBuffer = readFileSync("input-video.mp4");const videoBlob = new Blob([videoBuffer], { type: "video/mp4" });const result = await client.queue.submitAndPoll({ model: models.video("lucy-latest"), prompt: "Change the car to a vintage motorcycle", data: videoBlob, onStatusChange: (job) => console.log(`Status: ${job.status}`),});if (result.status === "completed") { const buffer = Buffer.from(await result.data.arrayBuffer()); writeFileSync("edited-video.mp4", buffer);}
import requestsimport osimport timeheaders = {"X-API-KEY": os.getenv("DECART_API_KEY")}# Submit jobwith open("input-video.mp4", "rb") as video: response = requests.post( "https://api.decart.ai/v1/jobs/lucy-latest", headers=headers, files={"data": video}, data={"prompt": "Change the car to a vintage motorcycle"} )job_id = response.json()["job_id"]# Poll until completedwhile True: status_response = requests.get(f"https://api.decart.ai/v1/jobs/{job_id}", headers=headers) status = status_response.json()["status"] print(f"Status: {status}") if status == "completed": break elif status == "failed": raise Exception("Job failed") time.sleep(2)# Download resultresult = requests.get(f"https://api.decart.ai/v1/jobs/{job_id}/content", headers=headers)with open("edited-video.mp4", "wb") as f: f.write(result.content)
const headers = { "X-API-KEY": process.env.DECART_API_KEY };// Submit jobconst formData = new FormData();formData.append("data", videoFile);formData.append("prompt", "Change the car to a vintage motorcycle");const submitResponse = await fetch("https://api.decart.ai/v1/jobs/lucy-latest", { method: "POST", headers, body: formData,});const { job_id } = await submitResponse.json();// Poll until completedwhile (true) { const statusResponse = await fetch(`https://api.decart.ai/v1/jobs/${job_id}`, { headers }); const { status } = await statusResponse.json(); console.log(`Status: ${status}`); if (status === "completed") break; if (status === "failed") throw new Error("Job failed"); await new Promise((r) => setTimeout(r, 2000));}// Download resultconst resultResponse = await fetch(`https://api.decart.ai/v1/jobs/${job_id}/content`, { headers });const editedVideo = await resultResponse.blob();
prompt (required) — Text description of the changes to apply. For Lucy 2.1, send an empty string if you want to use only a reference image.
reference_image (optional) — An image to guide the edit. Use this to show the model exactly what you want to add or change (e.g., a specific pair of sunglasses, a logo, or a style reference).
Lucy Clip is a previous-generation model. We recommend Lucy 2.1 (lucy-2.1) for all new video editing projects. It is faster, supports unlimited duration, and costs the same or less.
Lucy 2.1 responds best when your prompts follow specific patterns for each edit type. Focus only on what needs to change — you don’t need to describe the entire scene.
Edit type
Prompt template
Character transformation
”Substitute the character in the video with <description>.”
Add
”Add <description of object> to <where to add it>.”
Replace
”Change <object to change> with <description of new object>.”
Change attribute
”Change <object> to <description of new attribute>.”
Style transfer
”Transform to <style> with <specific visual details>.”
Swap the person in the video with a different character. When using a reference image, describe the character’s appearance in the prompt for best results.
“Substitute the character with an older man with pale skin, light blue eyes, a powdered white wig, and a dark formal coat with a white ruffled neckpiece.”
“Substitute the character with a young person wearing a pink top with white ribbon ties, loose pink pants, and short brown hair.”
Be specific — “a red knit sweater with a white emblem on the chest” outperforms “a red sweater”
Describe the reference image — when using character transformation, describe what you see in the reference (skin, hair, clothing, features)
One edit per prompt — combining multiple edits can produce unpredictable results
Detail improves results — detailed prompts (20–40 words) describing style, appearance, and context produce significantly better results than short instructions
For more precise edits, Lucy 2.1 supports an optional reference_image parameter. This allows you to provide a visual reference that guides the edit—perfect for when you want to add a specific item (like a particular style of sunglasses) or match a specific look.
Reference images are supported with Lucy 2.1 (lucy-2.1) and the previous-generation Lucy Clip (lucy-clip).
With Lucy 2.1, you can use a reference image with an empty prompt — the model will transform the video to match the reference image directly:
# Submit job with reference image and empty promptJOB_ID=$(curl -s -X POST https://api.decart.ai/v1/jobs/lucy-latest \ -H "X-API-KEY: $DECART_API_KEY" \ -F "data=@input-video.mp4" \ -F "prompt=" \ -F "reference_image=@style-reference.jpg" | jq -r '.job_id')# Poll and download as before...
import asynciofrom decart import DecartClient, modelsasync def edit_video_with_reference(): async with DecartClient(api_key="your-api-key-here") as client: with open("input-video.mp4", "rb") as video_file: with open("style-reference.jpg", "rb") as reference_image: result = await client.queue.submit_and_poll({ "model": models.video("lucy-latest"), "prompt": "", "data": video_file, "reference_image": reference_image, "on_status_change": lambda job: print(f"Status: {job.status}"), }) if result.status == "completed": with open("edited-video.mp4", "wb") as f: f.write(result.data)asyncio.run(edit_video_with_reference())
# Submit job with reference imageJOB_ID=$(curl -s -X POST https://api.decart.ai/v1/jobs/lucy-latest \ -H "X-API-KEY: $DECART_API_KEY" \ -F "data=@input-video.mp4" \ -F "prompt=Add these sunglasses to her face" \ -F "reference_image=@sunglasses.jpg" | jq -r '.job_id')# Poll and download as before...
import asynciofrom decart import DecartClient, modelsasync def edit_video_with_reference(): async with DecartClient(api_key="your-api-key-here") as client: with open("input-video.mp4", "rb") as video_file: with open("sunglasses.jpg", "rb") as reference_image: result = await client.queue.submit_and_poll({ "model": models.video("lucy-latest"), "prompt": "Add these sunglasses to her face", "data": video_file, "reference_image": reference_image, "on_status_change": lambda job: print(f"Status: {job.status}"), }) if result.status == "completed": with open("edited-video.mp4", "wb") as f: f.write(result.data)asyncio.run(edit_video_with_reference())
import { createDecartClient, models } from "@decartai/sdk";import { readFileSync, writeFileSync } from "fs";const client = createDecartClient({ apiKey: "your-api-key-here" });const videoBuffer = readFileSync("input-video.mp4");const videoBlob = new Blob([videoBuffer], { type: "video/mp4" });const imageBuffer = readFileSync("sunglasses.jpg");const imageBlob = new Blob([imageBuffer], { type: "image/jpeg" });const result = await client.queue.submitAndPoll({ model: models.video("lucy-latest"), prompt: "Add these sunglasses to her face", data: videoBlob, reference_image: imageBlob, onStatusChange: (job) => console.log(`Status: ${job.status}`),});if (result.status === "completed") { const buffer = Buffer.from(await result.data.arrayBuffer()); writeFileSync("edited-video.mp4", buffer);}