Skip to main content
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.

Quick start

Here’s a simple example using lucy-latest, which always targets the newest video editing model (currently Lucy 2.5).
# Submit job
JOB_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=Change the car to a vintage motorcycle" | jq -r '.job_id')

# Poll until completed
while true; do
  STATUS=$(curl -s -H "X-API-KEY: $DECART_API_KEY" \
    https://api.decart.ai/v1/jobs/$JOB_ID | jq -r '.status')
  echo "Status: $STATUS"
  [ "$STATUS" = "completed" ] && break
  [ "$STATUS" = "failed" ] && exit 1
  sleep 2
done

# Download result
curl -H "X-API-KEY: $DECART_API_KEY" \
  https://api.decart.ai/v1/jobs/$JOB_ID/content --output edited-video.mp4
import asyncio
from decart import DecartClient, models

async 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 requests
import os
import time

headers = {"X-API-KEY": os.getenv("DECART_API_KEY")}

# Submit job
with 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 completed
while 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 result
result = 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 job
const 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 completed
while (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 result
const resultResponse = await fetch(`https://api.decart.ai/v1/jobs/${job_id}/content`, { headers });
const editedVideo = await resultResponse.blob();

Parameters

  • data (required) — Input video file to edit.
  • 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).
  • resolution (optional) — Output resolution: 720p (default: 720p).
You can drive edits with text, a visual reference, or a combination of both.

Video Requirements

  • Format: MP4 (H.264 or VP8 codec)
  • Aspect Ratio: 16:9 (landscape) or 9:16 (portrait)
  • Duration: Unlimited with Lucy 2.1. Previous-generation Lucy Clip supports clips up to 5 seconds.
  • File Size: Maximum 200MB
  • Output Resolution:
    • 720p: 720×1280 (portrait) or 1280×720 (landscape)
For complete API documentation including response formats and error codes, see the API Reference.

Available Models and Options

Decart provides different editing models to balance precision, creativity, and cost depending on your use case. We currently offer:
  • Lucy 2.1 (Recommended) — our latest and most advanced editing model. Fastest speed, excellent quality, unlimited duration, supports prompt and optional reference image.
  • Lucy Clip (Previous generation) — older editing model. Limited to 5-second clips.
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.

Model Comparison

ModelStatusQualitySpeedReference ImageDurationCostBest For
Lucy 2.1RecommendedExcellentFastest✅ SupportedUnlimited$0.04/secAll video editing use cases (latest)
Lucy ClipLegacyExcellentStandard✅ SupportedMax 5 sec$0.15/sec

Prompting Guide

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 typePrompt 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>.”

Character Transformation

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.”

Adding & Replacing Objects

Add new elements or swap existing ones. Always specify placement for additions.
  • “Add a red conical hat covered in sequins with a white fluffy trim to the person’s head.”
  • “Change the person’s sweater to a red knit sweater with a white-outlined gold emblem on the chest.”
  • “Replace the car with a futuristic hoverbike, metallic finish.”

Changing Attributes

Modify a property of an existing object — color, texture, material — without replacing it entirely.
  • “Change the wall’s color to light blue, natural consistent paint finish.”
  • “Change the shirt’s texture to knitted, woven fabric.”

Style Transfer

Apply consistent visual styles across all frames.
  • “Transform to anime style with vibrant colors and cel shading.”
  • “Apply film noir aesthetic with high contrast black and white.”

Prompt Tips

  • 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

Using Reference Images

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 prompt
JOB_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 asyncio
from decart import DecartClient, models

async 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())
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("style-reference.jpg");
const imageBlob = new Blob([imageBuffer], { type: "image/jpeg" });

const result = await client.queue.submitAndPoll({
  model: models.video("lucy-latest"),
  prompt: "",
  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);
}
# Submit job with reference image
JOB_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 asyncio
from decart import DecartClient, models

async 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);
}

Endpoint and Full API Reference

Next steps

Now that you know the basics of video editing, you might want to check out one of these resources next.

Edit a video in the Platform

Try out video editing and other models in the interactive platform.

Full API reference

Check out all the options for video editing in the API reference.

Virtual Try-On

Dress people in videos with text prompts or garment reference images.