> ## Documentation Index
> Fetch the complete documentation index at: https://docs.base44.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Internal links on these pages omit the .md extension. Append .md to a docs page URL, or send an Accept: text/markdown header, to get that page as markdown.

# Generate video with the AI Gateway

> Generate video from your backend functions through the AI Gateway's OpenAI-compatible video endpoints.

The [AI Gateway](/developers/references/sdk/docs/interfaces/ai-gateway) serves OpenAI-compatible video endpoints, so your app can generate video from a prompt, from a still image, or from existing media, with control over the model, duration, aspect ratio, and resolution. Requests are billed to your app's credits, with no separate provider account or API key.

<Tip>
  **Before you begin:** Call the gateway from a [backend function](/developers/backend/resources/backend-functions/overview) rather than the browser, so you can check who's calling and control usage before a request is billed to your app. Get the `baseURL` and `token` from [`base44.aiGateway.connection()`](/developers/references/sdk/docs/interfaces/ai-gateway#connection).
</Tip>

Video generation is asynchronous. Creating a video returns a job straight away, and you poll it until it finishes. A single video can take minutes to render, so don't hold a request open waiting for one.

## Endpoints

| Endpoint | Description |
| - | - |
| `POST {baseURL}/videos` | Starts a video job and returns it with status `queued`. |
| `GET {baseURL}/videos/{video_id}` | Returns the job's current status, and its `url` once it's complete. |
| `GET {baseURL}/videos/{video_id}/content` | Redirects to the finished video file. |

The create endpoint accepts JSON or `multipart/form-data` and follows OpenAI's request and response format, with a few Base44 fields added. Authenticate with `Authorization: Bearer {token}`.

## Generate a video

Send a prompt and a model to `POST /videos`. Unlike the [image endpoints](/developers/references/sdk/getting-started/ai-gateway-images), there's no `automatic` option, so `model` is required.

Use `asServiceRole` for the connection. An app that restricts Core integrations rejects user-token gateway calls unless it's shared privately or with your workspace, and most new apps have that restriction on from the start. A user-token call works while you're building and then starts failing once you share the app publicly. The mode changes which token you get and nothing else, so guard the function with [`base44.auth.me()`](/developers/references/sdk/docs/interfaces/auth#me) to control who can spend your credits.

```typescript Start a video job theme={null}
const { baseURL, token, headers } = base44.asServiceRole.aiGateway.connection();

const response = await fetch(`${baseURL}/videos`, {
  method: "POST",
  headers: { ...headers, Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "veo_3_1_fast",
    prompt: "A lighthouse beam sweeping across a calm sea at dawn",
    seconds: 8,
    resolution: "720p",
    aspect_ratio: "16:9",
  }),
});

const video = await response.json();
// { id: "video_6f1c...", object: "video", status: "queued", model: "veo_3_1_fast", ... }
```

Store the returned `id` on an entity so you can check the job later, after the function that started it has returned.

## Check the status

Poll `GET /videos/{video_id}` until `status` is `completed` or `failed`. A completed job carries the finished video's `url` and the credits it used.

```typescript Check a video job theme={null}
const response = await fetch(`${baseURL}/videos/${videoId}`, {
  headers: { ...headers, Authorization: `Bearer ${token}` },
});

const video = await response.json();

if (video.status === "completed") {
  await base44.asServiceRole.entities.Clip.update(clipId, { video_url: video.url });
} else if (video.status === "failed") {
  await base44.asServiceRole.entities.Clip.update(clipId, { error: video.error.message });
}
```

Status moves through `queued`, then `in_progress`, then either `completed` or `failed`. Poll from a [scheduled function](/developers/backend/resources/backend-functions/overview) or from the browser on an interval, rather than looping inside the function that created the job.

The entity write also uses `asServiceRole` here, because a scheduled poll runs with no signed-in user and row-level security would otherwise block it. That bypasses your entity's permissions, so write only the job's own result, and keep the record's `id` in the function's own state rather than taking it from the caller.

The `url` points to a file in your app's storage, so you can save it on an entity and play it back like any other file. `GET /videos/{video_id}/content` redirects to the same file, for clients that expect OpenAI's download endpoint. It returns `409` while the video isn't ready yet.

## Start from an image

The `frame_images` field pins the video's first or last frame to a still image. Use it to animate an image your app already has, such as a product photo or a generated image.

```typescript Animate a still image theme={null}
body: JSON.stringify({
  model: "veo_3_1_fast",
  prompt: "The camera pulls back slowly to reveal the whole harbour",
  frame_images: [
    { type: "image_url", image_url: { url: harbourPhotoUrl }, frame_type: "first_frame" },
  ],
}),
```

You can set `first_frame`, `last_frame`, or both, and each `frame_type` may appear only once. On Veo models, a `last_frame` needs a `first_frame` alongside it. Grok Imagine Video accepts a first frame only.

The `input_references` field is the other way to supply media, and it guides the whole clip instead of a single frame. Each reference is an `image_url`, `video_url`, or `audio_url`, and you can pass up to 8.

```typescript Guide a video with reference media theme={null}
body: JSON.stringify({
  model: "seedance_2",
  prompt: "Match the style and pacing of the reference clip",
  input_references: [
    { type: "video_url", video_url: { url: referenceClipUrl } },
    { type: "audio_url", audio_url: { url: voiceoverUrl } },
  ],
}),
```

<Warning>
  Use either `frame_images` or `input_references` in a request, never both. Every media URL must be a public `https://` URL, so private files that your app owns don't work here, unlike the reference images on the image endpoints.
</Warning>

## Models

Set `model` to one of these IDs. Video models are separate from the [chat models](/developers/references/sdk/docs/interfaces/ai-gateway#openai-compatible-models) and the [image models](/developers/references/sdk/getting-started/ai-gateway-images#models) the gateway serves.

| Model ID | Provider | Duration in `seconds` | Resolutions | Audio | Seed |
| - | - | - | - | - | - |
| `veo_3_1_lite` | Google | `4`, `6`, `8` | `720p`, `1080p` | Yes | Yes |
| `veo_3_1_fast` | Google | `4`, `6`, `8` | `720p`, `1080p` | Yes | Yes |
| `seedance_2` | ByteDance | `4` to `15` | `480p`, `720p`, `1080p`, `4K` | Yes | Yes |
| `seedance_2_fast` | ByteDance | `4` to `15` | `480p`, `720p` | Yes | Yes |
| `seedance_2_mini` | ByteDance | `4` to `15` | `480p`, `720p` | Yes | Yes |
| `seedance_2_5` | ByteDance | `4` to `30` | `480p`, `720p` | Yes | Yes |
| `kling_3` | Kuaishou | `3` to `15` | `720p`, `1080p` | Yes | No |
| `minimax_h3` | MiniMax | `4` to `15` | `768p`, `2K` | No | No |
| `minimax_h3_max` | MiniMax | `5` to `15` | `480p`, `768p` | No | No |
| `grok_imagine_video` | xAI | `1` to `15` | `480p`, `720p` | Yes | No |
| `grok_imagine_video_1_5` | xAI | `1` to `15` | `480p`, `720p`, `1080p` | Yes | No |

A pinned model is never swapped for another one. If the model doesn't support an option you set, the request fails with an error that names the option.

<AccordionGroup>
  <Accordion title="Supported aspect ratios">
    | Models | Aspect ratios |
    | - | - |
    | Veo models | `16:9`, `9:16` |
    | `kling_3` | `1:1`, `16:9`, `9:16` |
    | Seedance and MiniMax models | `1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9` |
    | Grok Imagine Video models | `1:1`, `3:2`, `2:3`, `4:3`, `3:4`, `16:9`, `9:16` |
  </Accordion>

  <Accordion title="Supported reference media">
    | Models | Frame images | Input references |
    | - | - | - |
    | Veo models | `first_frame`, `last_frame`, where a last frame needs a first frame | Not supported |
    | Seedance and `minimax_h3` | `first_frame`, `last_frame` | `image_url`, `video_url`, `audio_url` |
    | `kling_3`, `minimax_h3_max` | `first_frame`, `last_frame` | Not supported |
    | `grok_imagine_video` | `first_frame` | `image_url` |
    | `grok_imagine_video_1_5` | `first_frame`, `last_frame` | `image_url` |
  </Accordion>
</AccordionGroup>

### Defaults

Veo models default to 8 seconds at `720p`, `16:9`, with audio on. The other models have no gateway default, so leaving `seconds`, `resolution`, or `aspect_ratio` out lets the provider choose. Set them explicitly when the output needs a specific shape or length.

## Request parameters

| Parameter | Type | Description |
| - | - | - |
| `prompt` | `string` | Required. What to generate, up to 10,000 characters. |
| `model` | `string` | Required. Model ID from [Models](#models). There's no `automatic` option. |
| `seconds` | `integer` | Length of the video, within the range the model supports. |
| `aspect_ratio` | `string` | Base44 field. Aspect ratio of the video, such as `16:9`. Can't be combined with `size`. |
| `resolution` | `string` | Base44 field. `480p`, `720p`, `768p`, `1080p`, `2K`, or `4K`, depending on the model. Values are case-sensitive. Can't be combined with `size`. |
| `size` | `string` | Exact dimensions as `WIDTHxHEIGHT`, such as `1280x720`. Can't be combined with `aspect_ratio` or `resolution`. |
| `frame_images` | `object[]` | Base44 field. Up to 2 stills that pin the first or last frame. Can't be combined with `input_references`. |
| `input_references` | `object[]` | Base44 field. Up to 8 image, video, or audio references that guide the whole clip. Can't be combined with `frame_images`. |
| `input_reference` | `object` | A single `{ image_url }` reference, accepted for OpenAI client compatibility. |
| `generate_audio` | `boolean` | Whether to generate a soundtrack. Not supported on MiniMax models. |
| `seed` | `integer` | From 0 to 4294967295, for repeatable output. Veo and Seedance models only. |
| `dry_run` | `boolean` | Base44 field. Returns the cost without generating a video. See [Estimate the cost](#estimate-the-cost). |

Any field that isn't listed here is rejected rather than ignored.

## Response

```json theme={null}
{
  "id": "video_6f1cb2d94ae30718c2f55a01",
  "object": "video",
  "model": "veo_3_1_fast",
  "status": "completed",
  "prompt": "A lighthouse beam sweeping across a calm sea at dawn",
  "seconds": "8",
  "size": null,
  "created_at": 1790000000,
  "completed_at": 1790000240,
  "url": "https://...video.mp4",
  "usage": { "base44_credits": 160 }
}
```

The `url` and `completed_at` fields stay `null` until the job finishes, and `usage` appears once the credits are recorded. A failed job carries an `error` object with a `code` and `message` instead of a `url`.

## Estimate the cost

Set `dry_run` to `true` to see what a request would cost, without generating a video or using credits.

```typescript Estimate the cost of a request theme={null}
const response = await fetch(`${baseURL}/videos`, {
  method: "POST",
  headers: { ...headers, Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "seedance_2",
    prompt,
    seconds: 10,
    resolution: "1080p",
    dry_run: true,
  }),
});

const { model, usage } = await response.json();
// { dry_run: true, model: "seedance_2", usage: { base44_credits: ... } }
```

Video requests use your app's credits, the same shared quota as the rest of the [AI Gateway](/developers/references/sdk/docs/interfaces/ai-gateway#billing-and-limits). The cost of each video depends on the model, duration, and resolution, and video is considerably more expensive than image generation. Estimate with `dry_run` before you expose video generation to your app's users.

## Limits

| Limit | Value |
| - | - |
| Prompt | 10,000 characters |
| Frame images per request | 2 |
| Input references per request | 8 |
| Request body | 256 KB |
| Video job creation | 20 per minute per app |
| Requests | 200 per minute per app, shared with all AI Gateway requests |

Media is passed by URL, so the create endpoint doesn't accept file uploads, and the request body stays small.

## Errors

Errors use OpenAI's format, `{ "error": { "message", "type", "code", "param" } }`, so client libraries report them like any other OpenAI error.

| Status | Cause |
| - | - |
| `400` | An invalid or unsupported option, an unknown field, both `frame_images` and `input_references` in one request, a repeated `frame_type`, or a media URL that isn't publicly reachable. The message names the problem, and `param` names the field. |
| `402` | Your app is out of credits. |
| `404` | The model ID doesn't exist, or the video ID doesn't belong to your app. |
| `409` | You requested the content of a video that isn't ready yet. |
| `429` | Your app is creating videos faster than 20 per minute, or the model provider is rate limiting requests. Retry after a short wait. |
| `502` | The model provider failed to generate the video. |

A job that fails after it's accepted reports `status: "failed"` with an `error` object, rather than an HTTP error, because the create request already succeeded.

## See also

* [ai-gateway](/developers/references/sdk/docs/interfaces/ai-gateway): Get connection details and use chat models through the gateway.
* [Generate images](/developers/references/sdk/getting-started/ai-gateway-images): Generate and edit images through the gateway.
* [Backend functions](/developers/backend/resources/backend-functions/overview): Write the server-side code that calls the gateway.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.