# Create Video

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

Starts a video render and answers `202` at once with the job; poll its `polling_url`, or pass `callback_url` to be notified. The request follows OpenRouter's video API, so the OpenRouter SDKs work by changing the base URL and key. OpenAI/Sora field names are accepted too: `seconds` for `duration`, `size`, and `input_reference` (an uploaded file in `multipart/form-data`, an https URL string, or `{image_url}`) for the first frame. `GET /v1/videos/models` lists the models with what each accepts.

**Modes (minimax-h3):** text only; `frame_images` with a `first_frame`, a `last_frame` or both; or `input_references` (up to 9 images, 3 videos and 3 audio clips, 12 in all). When both `frame_images` and `input_references` are sent, `frame_images` is used.

**Media:** an https URL or a base64 data URI. Images JPG, PNG, WEBP, HEIC or HEIF up to 30 MB; videos MP4 or MOV up to 50 MB, 2-15 s; audio WAV or MP3 up to 15 MB, 2-15 s.

**Sizes (minimax-h3):** 4 to 15 seconds at 24 fps, with stereo AAC audio. Send `aspect_ratio` or `size`:

| `aspect_ratio` | `size` |
|---|---|
| `16:9` (default) | `1344x768` |
| `9:16` | `768x1344` |
| `4:3` | `1024x768` |
| `3:4` | `768x1024` |
| `1:1` | `768x768` |
| `21:9` | `1536x672` |

**Output:** duration snaps to the model's frame grid, so a 4 s request is about 4.4 s; read `output.duration`. Videos have a generated soundtrack unless `generate_audio` is `false`.

**Prompts** are used as written. MiniMax's and fal's APIs rewrite prompts by default, so the same prompt can give a different video here.

**Expected wait:** while a job is `pending`, `queue_position` counts the jobs ahead of it for the same model. Once it starts, a minimax-h3 render takes about 3 seconds per second of video for text and frame jobs, and about 5 seconds per second with `input_references`: a 6 s text job is ready in about 20 s. At 2K a render takes about 3 times as long: a 15 s text job takes about 140 s. `progress` is estimated from these times.

**Quality (minimax-h3):** `quality` picks a tier, the same in every mode. `turbo` (or `auto`, `low`; the default) uses H3's Turbo LoRA: 4 denoising steps, or 8 with `input_references`. `standard` (or `medium`) runs the full base model at 20 steps and bills 3 times the seconds; `max` (or `high`) runs 40 steps and bills 5 times. For finer control send `num_inference_steps` from 10 to 50 instead (OpenRouter's SDKs: `provider.options.stealth.quality` or `.num_inference_steps`); more steps take longer and cost more. Sending a tier and a different step count is a 400.

**Billing:** per second of requested `duration` at the model's price (`pricing_skus` in `GET /v1/videos/models`; `standard_per_second` and `max_per_second` are the tiers' prices, `2K_per_second` the 2K price), only when the job completes. `standard` bills 3 times the seconds and `max` 5 times; `resolution: 2K` (Turbo only) 4 times. A raw `num_inference_steps` bills steps / 6 times the seconds, never more than the next tier up (19 steps bill as `standard`), rounded up to a whole second: a 10 s job at 25 steps bills 42 s. `usage.billed_seconds` shows the result. Failed and cancelled jobs are not billed.

## Body (application/json)

- `model` (string, required)
- `prompt` (string, required): Must be a string (a list or number is a 400). [at most 7000 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 4 times the seconds, takes about 3 times as long, and runs on the Turbo schedule only: `quality` `standard` or `max`, or base `num_inference_steps`, at 2K is a 400. [default 768p, 768p, 2K]
- `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 larger six are 2K (see `resolution`). [1344x768, 768x1344, 1024x768, 768x1024, 768x768, 1536x672, 2688x1536, 1536x2688, 2048x1536, 1536x2048, 1536x1536, 3072x1344]
- `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 steps / 6 times the seconds, capped at the next `quality` tier up (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 3x; `max` (`high`) is 40 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, 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.

## 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)

## Responses

- `202`: Accepted. The job, pending, with its polling_url and queue_position. (video-job-payload)
- `400`: A field the model does not support or a malformed input. The message names the field. Fields are typed, not coerced; prompt must be a string, generate_audio a boolean (true or false), input_reference a URL string or an object - and a whole number may be sent as text ("20"). (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)
- `503`: The model has no valid capability spec at the moment and is not served. (error-payload)
- `413`: A media input is over its size limit (error-payload)

## Example

```bash
curl https://api.vultrinference.com/v1/videos \
  -H "Authorization: Bearer $VULTR_INFERENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax-h3",
    "prompt": "A red fox trots through fresh snow at dawn, breath steaming.",
    "duration": 6,
    "aspect_ratio": "16:9",
    "callback_url": "https://example.com/hooks/video"
  }'
```

```python
import os
import requests

response = requests.post(
    "https://api.vultrinference.com/v1/videos",
    headers={"Authorization": f"Bearer {os.environ['VULTR_INFERENCE_API_KEY']}"},
    json={
        "model": "minimax-h3",
        "prompt": "A red fox trots through fresh snow at dawn, breath steaming.",
        "duration": 6,
        "aspect_ratio": "16:9",
        "callback_url": "https://example.com/hooks/video",
    },
)
print(response.json())
```

```javascript
const response = await fetch("https://api.vultrinference.com/v1/videos", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VULTR_INFERENCE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "model": "minimax-h3",
    "prompt": "A red fox trots through fresh snow at dawn, breath steaming.",
    "duration": 6,
    "aspect_ratio": "16:9",
    "callback_url": "https://example.com/hooks/video"
  }),
});
console.log(await response.json());
```
