# Videos API vLLM-Omni provides an OpenAI-compatible video generation API for diffusion video models. The API supports asynchronous video jobs through `/v1/videos` and a synchronous benchmark-oriented endpoint through `/v1/videos/sync`. Each server instance runs a single model specified at startup with `vllm serve --omni`. ## Quick Start ### Start the Server ```bash vllm serve Wan-AI/Wan2.2-T2V-A14B-Diffusers --omni --port 8091 ``` ### Create a Video Job ```bash create_response=$(curl -s http://localhost:8091/v1/videos \ -F "prompt=A cinematic tracking shot of a mountain lake at sunrise" \ -F "width=1280" \ -F "height=720" \ -F "num_frames=80" \ -F "fps=16" \ -F "num_inference_steps=40") video_id=$(echo "${create_response}" | jq -r '.id') ``` ### Poll and Download ```bash curl -s "http://localhost:8091/v1/videos/${video_id}" | jq . curl -L "http://localhost:8091/v1/videos/${video_id}/content" -o output.mp4 ``` ## API Reference ### Endpoints | Endpoint | Method | Description | | ---------- | -------- | ------------- | | `/v1/videos` | `POST` | Create an asynchronous video generation job | | `/v1/videos/sync` | `POST` | Generate a video synchronously and return raw video bytes | | `/v1/videos/{video_id}` | `GET` | Retrieve job status and metadata | | `/v1/videos` | `GET` | List stored video jobs | | `/v1/videos/{video_id}/content` | `GET` | Download generated video content | | `/v1/videos/{video_id}` | `DELETE` | Delete a video job and stored output | ### Request Parameters `POST /v1/videos` and `POST /v1/videos/sync` accept `multipart/form-data`. #### OpenAI-style fields | Parameter | Type | Default | Description | | ----------- | ------ | --------- | ------------- | | `prompt` | string | **required** | Text prompt for video generation | | `model` | string | server's model | Optional model name | | `seconds` | string | null | Requested clip duration in seconds | | `size` | string | null | Requested output size in `WIDTHxHEIGHT` format | | `user` | string | null | Optional user identifier | #### vLLM-Omni extension fields | Parameter | Type | Default | Description | | ----------- | ------ | --------- | ------------- | | `input_reference` | file | null | Uploaded reference image or video for image-to-video/video-to-video requests | | `control_reference` | file | null | Optional uploaded image/video control, up to 512 MiB, for models that declare control-upload support | | `control_type` | string | null | Model control name associated with `control_reference`; currently Cosmos3 supports `edge`, `blur`, `depth`, `seg`, and `wsm` | | `image_reference` | string | null | JSON-encoded reference image payload; do not combine with `input_reference` or `video_reference` | | `video_reference` | string | null | JSON-encoded reference video payload; do not combine with `input_reference` or `image_reference` | | `audio_reference` | string | null | JSON-encoded audio reference for speech-to-video: `{"audio_url": "..."}` — supports HTTP(s) URLs or base64 data URLs | | `width` | integer | model default | Output video width | | `height` | integer | model default | Output video height | | `num_frames` | integer | 1 | Number of generated frames | | `fps` | integer | model default | Output frames per second | | `num_inference_steps` | integer | model default | Number of diffusion steps | | `guidance_scale` | number | null | CFG guidance scale for the low-noise stage | | `guidance_scale_2` | number | null | CFG guidance scale for the high-noise stage | | `boundary_ratio` | number | null | Boundary split ratio for multi-stage denoising | | `flow_shift` | number | null | Scheduler flow-shift value | | `true_cfg_scale` | number | null | True CFG scale when supported by the model | | `seed` | integer | null | Random seed for reproducibility | | `generate_sound` | boolean | false | Request model-generated audio for video models that support sound generation | | `sound_duration` | number | null | Duration in seconds for generated audio; defaults to generated video duration | | `negative_prompt` | string | null | Text describing what to avoid in the generated video | | `enable_frame_interpolation` | boolean | null | Enable post-generation frame interpolation | | `frame_interpolation_exp` | integer | null | Interpolation exponent; `1=2x`, `2=4x`, and so on | | `frame_interpolation_scale` | number | null | RIFE inference scale | | `frame_interpolation_model_path` | string | null | Local path or Hugging Face repo for the interpolation model | | `lora` | string | null | JSON-encoded LoRA configuration object | | `extra_params` | string | null | JSON-encoded object for additional model-specific parameters | ### Create Response `POST /v1/videos` returns a job record: ```json { "id": "video-123", "status": "queued", "created_at": 1701234567 } ``` The final content is available from `/v1/videos/{video_id}/content` after the job status becomes `completed`. `queued` means the request is still waiting for diffusion scheduler admission. The status changes to `in_progress` when the scheduler first selects the request for execution. `DELETE /v1/videos/{video_id}` issues a bounded engine abort (`VLLM_OMNI_ABORT_TIMEOUT`, default 2s), then cancels the frontend task. Cancellation cleanup is also bounded and best-effort: it confirms the abort was queued, and the current request batch may still drain. The job is then re-read so a completed save is not orphaned. ### Synchronous Response `POST /v1/videos/sync` blocks until generation finishes and returns raw video bytes. It is useful for benchmarks and simple scripts that do not need job storage or polling. ## Examples ### Image-to-Video ```bash curl -s http://localhost:8091/v1/videos \ -F "prompt=animate this image with subtle camera movement" \ -F "input_reference=@input.png" \ -F "width=1280" \ -F "height=720" \ -F "num_frames=80" \ -F "fps=16" ``` ### Video-to-Video For models that support video conditioning, upload the reference video with `input_reference`: ```bash curl -s http://localhost:8091/v1/videos \ -F "prompt=continue this motion with consistent subjects and lighting" \ -F "input_reference=@input.mp4;type=video/mp4" \ -F "width=1280" \ -F "height=720" \ -F "num_frames=80" \ -F "fps=16" ``` You can also pass a JSON-safe video URL or `data:video/...;base64,...` payload through `video_reference`. Do not send `video_reference` together with `input_reference` or `image_reference`. ```bash curl -s http://localhost:8091/v1/videos \ -F "prompt=continue this motion with consistent subjects and lighting" \ -F 'video_reference={"video_url":"https://example.com/input.mp4"}' \ -F "width=1280" \ -F "height=720" \ -F "num_frames=80" \ -F "fps=16" ``` JSON references currently support `image_url`/`video_url`; `file_id` references are not implemented yet. Models may expose additional V2V controls through `extra_params`. For example, Cosmos3 supports `condition_frame_indexes_vision` and `condition_video_keep` to select which decoded reference frames are used as clean conditioning. Cosmos3 transfer mode also accepts `edge`, `blur`, `depth`, `seg`, or `wsm` control hints. Each hint may specify its own `control_path` and `control_weight`. Request-level transfer options include `control_guidance`, `control_guidance_interval`, `emphasize_control_in_prompt`, `num_video_frames_per_chunk`, `num_conditional_frames`, `show_control_condition`, and `show_input`. Transfer uses its transfer-specific system prompt and, by default, appends a control-adherence directive to the positive prompt. It adds duration/FPS and resolution metadata to both CFG branches but does not add a negative prompt automatically. The Cosmos3 recipe includes an optional reference negative prompt and shows how to pass it. Set `emphasize_control_in_prompt`, `use_duration_template`, or `use_resolution_template` to `false` to disable the corresponding addition. `negative_metadata_mode` accepts `same`, `inverse`, or `none` and defaults to `same` for transfer. See the Cosmos3 recipe for complete examples. A client that cannot place the control on the server filesystem can upload one control with `control_reference` and identify it with `control_type`. The API streams the upload to request-scoped storage, supplies its path to the model, and removes it after synchronous or asynchronous generation completes. Other options for the selected control can remain in `extra_params`; do not also set `control` or `control_path` there. Uploads larger than 512 MiB are rejected. ```bash curl -s http://localhost:8091/v1/videos/sync \ -F "prompt=Preserve the scene while following the world-state control" \ -F "input_reference=@input.mp4;type=video/mp4" \ -F "control_reference=@wsm.mp4;type=video/mp4" \ -F "control_type=wsm" \ -F 'extra_params={"wsm":{"control_weight":1.0}}' \ -o output.mp4 ``` HTTP redirects for `image_reference.image_url` follow vLLM's `VLLM_MEDIA_URL_ALLOW_REDIRECTS` setting. Before starting the server, set it to `1` (the default) to allow redirects or `0` to reject them. A redirect target can differ from the original host, and vLLM-Omni does not yet fully implement upstream vLLM's media URL allowlist protection. Deployments should therefore treat remote media URLs as untrusted and choose this setting as part of their URL access policy. ### Speech-to-Video For models that support audio-driven generation (e.g., Wan2.2-S2V), pass both an image reference and an audio reference. The `audio_reference` field accepts a JSON string with `audio_url` pointing to an HTTP(s) URL or base64 data URL. ```bash curl -s http://localhost:8091/v1/videos \ -F "prompt=A person singing" \ -F 'image_reference={"image_url": "https://example.com/face.png"}' \ -F 'audio_reference={"audio_url": "https://example.com/speech.mp3"}' \ -F "width=832" \ -F "height=480" \ -F "num_inference_steps=40" \ -F "guidance_scale=4.5" \ -F "fps=16" ``` ### Synchronous Generation ```bash curl -X POST http://localhost:8091/v1/videos/sync \ -F "prompt=A small robot walking through a neon city" \ -F "width=854" \ -F "height=480" \ -F "num_frames=80" \ -F "fps=16" \ -o output.mp4 ``` ## Output Encoding These `extra_params` control how the server turns decoded frames into MP4 bytes. | Field | Type | Default | Description | | --- | --- | --- | --- | | `preencode_mp4` | boolean | false | Encode the MP4 on the worker while the VAE is still decoding, instead of after the full video is materialized | | `video_codec_options` | object | null | Encoder options passed through to the H.264 encoder, such as `{"preset": "ultrafast", "threads": "0"}` | With `preencode_mp4` enabled, each committed VAE chunk leaves the accelerator and is encoded while later chunks are still decoding, so host transfer and CPU encoding overlap the remaining decode instead of following it. The response is unchanged: the same complete MP4, byte-for-byte equivalent frames. ```bash curl -X POST http://localhost:8091/v1/videos/sync \ -F "prompt=A small robot walking through a neon city" \ -F 'extra_params={"preencode_mp4": true, "video_codec_options": {"preset": "ultrafast"}}' \ -o output.mp4 ``` `preencode_mp4` applies to the complete-MP4 response paths only. The `/v1/realtime/video` WebSocket endpoint rejects it, because that path already overlaps encoding through its own incremental fragmented-MP4 encoder. Support is per model: MiniMax-H3 implements it, and other models ignore the flag and take the full-decode path. ## Storage Set `VLLM_OMNI_SERVER_STORAGE__PATH` to control where asynchronous video outputs are stored: ```bash export VLLM_OMNI_SERVER_STORAGE__PATH=/var/tmp/vllm-omni-videos ``` > `VLLM_OMNI_STORAGE_PATH` is deprecated and will be removed in a future release; > use `VLLM_OMNI_SERVER_STORAGE__PATH` instead. ## Model-Specific Examples For complete text-to-video, image-to-video, and model-specific video-to-video walkthroughs, see: - [Text-to-Video](../user_guide/examples/online_serving/text_to_video.md) - [Image-to-Video](../user_guide/examples/online_serving/image_to_video.md) - [Speech-to-Video](../user_guide/examples/online_serving/speech_to_video.md) for Wan2.2-S2V audio-driven lip-sync generation - [Cosmos3 recipes](https://github.com/vllm-project/vllm-omni/blob/main/recipes/cosmos3/Cosmos3-Nano.md) for model-specific video-to-video examples and conditioning controls