Videos
POST /v1/videos, polling, content download, list, and delete.
On this page
Create an asynchronous video generation job, poll its status, and retrieve the completed asset. Configure a video deployment and object storage before using this endpoint.
Example request
Set GATEWAY to the gateway origin and API_KEY to your virtual key. Replace the example
public model with one from Model discovery.
curl -X POST "$GATEWAY/v1/videos" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "video-default", "prompt": "a serene mountain landscape at sunset", "duration": 8, "aspect_ratio": "16:9", "resolution": "720p" }'POST /v1/videos creates an async generation job and returns a local video_* id. Poll that local id
with GET /v1/videos/{video_id}. Download the generated bytes with
GET /v1/videos/{video_id}/content; the default variant is video.
Request parameters
There is no industry standard for video generation, so the gateway defines its own surface and stays accept-compatible with OpenAI's Sora shape:
| Parameter | Notes |
|---|---|
model, prompt | Required. |
task | Optional explicit workflow: text_to_video, image_to_video, reference_to_video, edit, or extend. Models declare which tasks they support. |
duration | Length in seconds (integer). seconds (string or int) is the OpenAI-style alias; send one or the other. |
size | Exact WIDTHxHEIGHT. Interchangeable with aspect_ratio + resolution; send one form. |
aspect_ratio / resolution | 16:9, 9:16, ... / 360p–4K. Reverse-mapped to an exact size for providers that need one. |
input_references | Array of {type: "image_url"|"audio_url"|"video_url", ...} parts. input_reference (single, image_url or file_id) is the OpenAI-style alias. |
frame_images | First/last frame conditioning (frame_type: "first_frame"|"last_frame") for models that support it. |
seed, generate_audio | Forwarded only when the model profile declares support. |
quality | Gateway extension on the shared ladder: auto, low, medium, high, xhigh, or max (native is still accepted and normalized to max). Reconciled with the model's declared qualities under the parameter policy, and forwarded only when the profile declares rungs. |
user | Gateway-side attribution only. It is stored with the job and never forwarded upstream. |
extra_body | Provider-specific passthrough merged into the upstream request body. |
Every parameter is validated against the model profile before routing, so unsupported parameters fail
fast with unsupported_parameter instead of a provider error mid-pool.
Image, audio, and video references accept either public HTTPS URLs or base64 data: URLs. AI Gateway
downloads every HTTPS reference with SSRF protection, validates its MIME type and binary limits, and
sends a data: URL to the selected upstream; remote URLs are never delegated to providers. The
combined decoded media limit is 50 MB. Large data URLs are redacted before the request body is
persisted. Multipart uploads (the OpenAI SDK's file-typed input_reference) are not supported — use
the JSON reference forms.
Image generation itself has no attachment field. Image edits accept multipart image files; AI Gateway streams those files into request-scoped temporary storage, validates them as PNG/JPEG/WebP, and only then invokes an upstream adapter.
Storage
Videos use the gateway-wide object storage layer, configured with one connection string:
OBJECT_STORAGE_URL=file:.source/object-storage for development, or
OBJECT_STORAGE_URL=s3://key:secret@host/bucket?region=auto for S3, R2 or MinIO. The
gateway stores generated video bytes for 24 hours by default (VIDEOS_ASSET_RETENTION_HOURS=24) and
keeps metadata in Postgres.
Model profiles
Video-capable deployments declare catalogEntry.operations["video.generate"]. The profile controls
accepted tasks, durations, sizes (with per-provider aspectRatio/resolution mappings), quality,
reference support (supportsImageUrl, supportsAudioUrl, supportsVideoUrl, supportsFileId,
supportsFrameImages, maxInputReferences), and supportsSeed/supportsGenerateAudio.
Adapters expose video generation through transports: videos (OpenAI-shaped Videos API),
videos_async (async job API: submit /videos, poll /videos/{jobId}, download
/videos/{jobId}/content, delete /videos/{jobId}), generate_videos (Google Veo long-running
operations), and interactions (Google Interactions API). Adapter defaults pick the common path;
use transportOverrides when a model needs another transport.
Gemini Omni Flash
gemini-omni-1.1-flash uses the Google interactions transport. Configure its deployment with
transportOverrides: { "video.generate": "interactions" }.
- Text-to-video may omit
taskor settext_to_video. - Image-to-video and first/last-frame interpolation use image references or
frame_images. - Reference-to-video uses
task: "reference_to_video"and image/video references. - Editing and extension require
task: "edit"ortask: "extend"plus exactly onevideo_url.
The integration intentionally does not use Google's Files API. Inputs are materialized as validated
inline data, and completed output is fetched inline directly into AI Gateway object storage without
being persisted in Postgres. Consequently, the stock profile exposes 360p and 720p only, rejects
file_id, and limits each input asset to 20 MB. Uploaded videos for edit/extend remain subject to
Google's 10-second and regional restrictions.
Stateful continuation is also intentionally unavailable. previous_interaction_id is blocked in
extra_body; accepting a raw provider id would bypass AI Gateway ownership and deployment-affinity
checks. A future implementation should expose a local source video id instead.
Omni bills video output by token (videoOutputCentsPerMTokens; 360p is 1,931 and 720p is 5,792 tokens
per second). A virtual key with a token or budget limit reserves an upper bound at submit, from the
requested duration and size (the longest duration and densest size when omitted). When the job
reaches a terminal state the reservation is settled to the usage Google reports, or released if the
job failed. A completed job whose provider reports no usage is charged the estimate for its duration
and size. Settlement is recorded in the job's billing, and an unsettled reservation expires with its
lease. A deployment with no pricing still fails budget-capped virtual keys closed with
budget_pricing_unavailable.
Lifecycle
GET /v1/videoslists videos visible to the caller (virtual keys see their own; the master key sees all).GET /v1/videos/{video_id}refreshes upstream status when needed.GET /v1/videos/{video_id}/contentstreams bytes from object storage and supports byte ranges (including suffix ranges).DELETE /v1/videos/{video_id}deletes stored assets, marks the metadata deleted, and best-effort deletes the upstream job where the provider supports it.
A background poller advances unfinished jobs (VIDEO_JOB_POLL_INTERVAL_MS, batch size
VIDEO_JOB_POLL_BATCH_SIZE) and fails jobs that exceed VIDEO_JOB_MAX_RUNTIME_MINUTES. Jobs are
claimed atomically, so multiple gateway instances can poll safely.
Next steps
- Creating deployments — custom video profiles.
- Providers → OpenAI-compatible — compatible APIs and transport overrides.