# Create Video Edit

`POST https://api.vultrinference.com/v1/videos/edits`

Redraws part of a video and keeps the rest: the area a mask marks white is drawn again from the prompt and any `input_references` (for example a person replaced by the one in a reference picture), and everything outside the mask is the video's own pixels. Answers `202` with a job, polled and fetched exactly as a `POST /v1/videos` job. `GET /v1/videos/models` lists `edit` in a model's `operations`, with what its edits take under `edit`.

**The video and the mask:** the first `duration` seconds of `video` are edited, scaled to cover the output size and centre-cropped. `mask` is white where the picture may change, as an image (every frame) or a video (frame by frame); the model redraws in cells of 16 by 16 pixels, so its edge is coarse, and a mask with some room around the subject leaves space for a differently built one. A box around a person, grown by about 50 pixels, works.

**Sound:** `audio: keep` (the default) copies the video's soundtrack onto the edit; `generate` makes a new one.

**Motion guide:** `motion_guide: true` adds the video with the masked area blurred as a reference video, which keeps the redrawn subject where the original stood and moving as it moved. Name it in the prompt.

**minimax-h3:** 768p only. Write the prompt in its layout with `[video editing + reference generation]` (or `[video editing]` without references), say what replaces what, and that everything outside the edited area stays as it is.

**Billing:** as `POST /v1/videos`: per second of `duration`, at the mode's and tier's price.

## Body (application/json)

- `model` (string, required)
- `prompt` (string, required): Must be a string (a list or number is a 400). [at most 8000 characters]
- `duration` (integer): Seconds of video. minimax-h3 takes 4-15; default 6. A whole number sent as text (`"8"`) is accepted.
- `seconds` (integer): OpenAI/Sora's name for `duration`; `duration` wins when both are sent.
- `resolution` (string): minimax-h3: `2K` renders the 768p preset and enlarges it 2x (16:9 is 2688x1536). It bills 3 times the seconds times the `quality` tier, takes about 3 times as long, and runs at every tier: `quality` `standard`, `max` or `ultra`, or base `num_inference_steps`, renders the 768p preset on the base weights before the same upscale. `4K` is the 2K render enlarged 1.5x by SeedVR2 after the engine (16:9 is 4032x2304, the soundtrack kept): it bills as 2K (the upscale is free), runs at Turbo only (any other tier at 4K is a 400), and takes about 18 s more per second of video, plus any wait for the upscaler; the job's `output.upscale` says it was applied. [default 768p, 768p, 2K, 4K]
- `aspect_ratio` (string) [default 16:9, 16:9, 9:16, 4:3, 3:4, 1:1, 21:9]
- `size` (string): Width x height; an alternative to `aspect_ratio`, and must agree with it when both are sent. The second six are 2K and the last six 4K (see `resolution`). [1344x768, 768x1344, 1024x768, 768x1024, 768x768, 1536x672, 2688x1536, 1536x2688, 2048x1536, 1536x2048, 1536x1536, 3072x1344, 4032x2304, 2304x4032, 3072x2304, 2304x3072, 2304x2304, 4608x2016]
- `frame_images` (array of objects) [at most 2 items]
  - `type` (string, required) [image_url]
  - `image_url` (object, required)
    - `url` (string, required): An https URL or a base64 data URI.
  - `frame_type` (string, required) [first_frame, last_frame]
- `input_references` (array of objects): Reference media for the subject, style or sound. Ignored when `frame_images` is sent.
  - `type` (string, required) [image_url, video_url, audio_url]
  - `image_url` (object)
    - `url` (string, required): An https URL or a base64 data URI.
  - `video_url` (object)
    - `url` (string, required): An https URL or a base64 data URI.
  - `audio_url` (object)
    - `url` (string, required): An https URL or a base64 data URI.
  - `role` (string): `driving` and `control` are reserved for models that support them. [default reference, reference]
- `input_reference` (string or object): OpenAI/Sora's first frame, as an https URL (or base64 data URI) string or as `{image_url}`. Ignored when `frame_images` is sent. Any other type is a 400.
- `generate_audio` (boolean): Strictly `true` or `false` (in `multipart/form-data` the text `true` or `false`); anything else is a 400. [default true]
- `seed` (integer)
- `num_inference_steps` (integer): minimax-h3: the number of denoising steps. Omit it for the Turbo LoRA (4 steps, or 8 with `input_references`); 10 to 50 runs the full base model, which takes longer and bills the `quality` tier whose step range holds the count: 10 to 20 as `standard` (1.5x), 21 to 40 as `max` (3x), 41 to 50 as `ultra` (5x; see Billing). Sending the Turbo count itself changes nothing. Prefer `quality`. Also accepted under `provider.options` (see `provider`). [range 4–50]
- `quality` (string): minimax-h3: the quality tier, the same in every mode. `turbo` (`auto`, `low`; the default) is the Turbo LoRA at 1x the price; `standard` (`medium`) is 20 base steps at 1.5x; `max` (`high`) is 40 base steps at 3x; `ultra` is 50 base steps at 5x. The job stores the tier's own name. Sending `num_inference_steps` too is a 400 unless it is the tier's count. Also accepted under `provider.options` (see `provider`). [turbo, standard, max, ultra, auto, low, medium, high]
- `start_time_seconds` (number): minimax-h3: where to start reading each reference video in `input_references`, in seconds. Also accepted under `provider.options` (see `provider`). [at least 0]
- `callback_url` (string): An https URL notified on completion, failure, cancellation and expiry. See `GET /v1/webhooks/secret`.
- `provider` (object): OpenRouter's provider preferences. The routing fields (`order`, `only`, `ignore`, `sort`, `allow_fallbacks`, ...) are accepted and ignored. `options` carries the model's own settings (`quality`, `num_inference_steps`, `start_time_seconds` for minimax-h3; `GET /v1/videos/models` lists each model's `allowed_passthrough_parameters`) for clients that cannot add top-level fields. From OpenRouter's SDKs use `options.stealth`: they keep only the slugs on their closed list and drop any other, `vultr` included, without an error. The first form present wins, the top level before any option; a key the model does not declare is a 400 naming its path.
- `video` (string or object, required): The video to edit, an https URL or a base64 data URI (MP4 or MOV, up to 50 MB), at least `duration` seconds long.
- `mask` (string or object, required): What may change, white on black, at any size (fitted as the video is). An image is the same mask for every frame; a video is one mask per frame, its last held if it ends early. An https URL or a base64 data URI.
- `audio` (string): `keep` copies the video's own soundtrack onto the edit (the model still hears it, so mouths follow it); `generate` makes a new one. [default keep, keep, generate]
- `motion_guide` (boolean): Adds the video with the masked area blurred as the last reference video, `<Video N>` where N is one more than the reference videos you send, so the redrawn subject keeps the original's place and movement. Name it in the prompt (for example "<Video 1> is the original shot with the person blurred; it gives where the person stands and how they move"). It counts toward the model's reference videos. [default false]

## Body (multipart/form-data)

- `model` (string, required)
- `prompt` (string, required)
- `seconds` (string)
- `size` (string)
- `input_reference` (string (binary)): The first frame.
- `generate_audio` (string) [true, false]
- `callback_url` (string)
- `video` (string (binary), required): The video to edit.
- `mask` (string (binary), required): What may change, white on black, an image or a video.
- `audio` (string) [keep, generate]
- `motion_guide` (string) [true, false]

## Responses

- `202`: Accepted. The job, pending, with its polling_url and queue_position. (video-job-payload)
- `400`: A missing video or mask, a model that does not edit, frame_images sent, a resolution the model does not edit at, or anything POST /v1/videos refuses. The message names the field. (error-payload)
- `401`: The API key is missing, unknown or not active. Send it as Authorization: Bearer <key>. (error-payload)
- `404`: Unknown video model (error-payload)
- `413`: A media input is over its size limit (error-payload)
- `429`: Too many videos for this model from your subscription, as on POST /v1/videos. Wait for Retry-After, then retry. (error-payload)
- `503`: The model has no valid capability spec at the moment and is not served. (error-payload)

## Example

```bash
curl https://api.vultrinference.com/v1/videos/edits \
  -H "Authorization: Bearer $VULTR_INFERENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax-h3",
    "prompt": "[video editing + reference generation] In the edited area, the man in <Picture 1> takes the place of the woman and does what she does at the same moments. Everything outside the edited area stays as it is.",
    "duration": 6,
    "aspect_ratio": "16:9",
    "video": "https://example.com/source.mp4",
    "mask": "https://example.com/mask.mp4",
    "input_references": [
      {
        "type": "image_url",
        "image_url": {
          "url": "https://example.com/character.png"
        }
      }
    ]
  }'
```

```python
import os
import requests

response = requests.post(
    "https://api.vultrinference.com/v1/videos/edits",
    headers={"Authorization": f"Bearer {os.environ['VULTR_INFERENCE_API_KEY']}"},
    json={
        "model": "minimax-h3",
        "prompt": "[video editing + reference generation] In the edited area, the man in <Picture 1> takes the place of the woman and does what she does at the same moments. Everything outside the edited area stays as it is.",
        "duration": 6,
        "aspect_ratio": "16:9",
        "video": "https://example.com/source.mp4",
        "mask": "https://example.com/mask.mp4",
        "input_references": [
            {
                "type": "image_url",
                "image_url": {
                    "url": "https://example.com/character.png",
                },
            },
        ],
    },
)
print(response.json())
```

```javascript
const response = await fetch("https://api.vultrinference.com/v1/videos/edits", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VULTR_INFERENCE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "model": "minimax-h3",
    "prompt": "[video editing + reference generation] In the edited area, the man in <Picture 1> takes the place of the woman and does what she does at the same moments. Everything outside the edited area stays as it is.",
    "duration": 6,
    "aspect_ratio": "16:9",
    "video": "https://example.com/source.mp4",
    "mask": "https://example.com/mask.mp4",
    "input_references": [
      {
        "type": "image_url",
        "image_url": {
          "url": "https://example.com/character.png"
        }
      }
    ]
  }),
});
console.log(await response.json());
```
