# AI video

> Generate footage from a prompt, animate an image, or make a photo speak, then place the clip in a template or generate it at render time.

- **URL**: https://orshot.com/docs/video-generation/ai-video

---

Orshot generates short video clips with AI and treats them like any other video layer: they live in your Brand Library, render inside your templates, and can be regenerated per render from the API, from workflows and from AI agents.

What you give decides what you get. There is no mode to pick.

| You send | You get |
|---|---|
| `prompt` | Footage from text |
| `prompt` + `imageRef` | The image animated |
| `imageRef` + `lastImageRef` | A transition between the two frames |
| `imageRef` + `script` (or `audio`) | The person in the photo speaking |

Every media value can be an `https` URL, a base64 data URI, a brand asset reference like `brand:image:123`, or a `gen://` reference to a clip Orshot generated earlier.

## Credits

AI video is billed in AI credits, the same credits used for AI images and the design agent. Clips are billed per second, on delivery only. Failed generations and cached clips cost nothing.

| Output | AI credits |
|---|---|
| HD footage or speech (up to 768p) | 3 per second |
| Full HD (1080p) | 6 per second |
| 2K | 12 per second |
| AI image | 2 |
| Voiceover or ambient audio, per started 10 s | 1 (premium voices 2) |

A 5 second HD clip is 15 credits. Ask before you spend with `POST /v1/ai/video/estimate`, which takes the same body and returns the credits without generating.

## Generate a clip

```bash
curl -X POST https://api.orshot.com/v1/ai/video \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a ceramic coffee cup on a wooden table by a window, morning light",
    "duration": 5,
    "preset": "product-hero",
    "look": "clean-studio",
    "aspect": "9:16"
  }'
```

The response is a job. Poll it until `finished` is true:

```bash
curl https://api.orshot.com/v1/studio/render-jobs/<JOB_ID> \
  -H "Authorization: Bearer <YOUR_API_KEY>"
```

```json
{
  "id": 89,
  "status": "succeeded",
  "finished": true,
  "result": {
    "data": {
      "url": "https://storage.orshot.com/generated/video/0472d0….mp4",
      "thumbnailUrl": "https://storage.orshot.com/generated/video/0472d0….jpg",
      "ref": "gen://0472d0…",
      "seconds": 5.2,
      "width": 768,
      "height": 1344,
      "creditsUsed": 15,
      "cached": false,
      "videoId": 151
    }
  }
}
```

Pass `"sync": true` to wait for the result in the same request (up to 120 seconds; a clip still generating after that comes back as `202` with the job to poll, never as a lost generation), or `webhook_url` to be called when the job settles. Every generated clip is added to your Brand Library and listed in AI Usage Logs.

### Request fields

| Field | Type | Notes |
|---|---|---|
| `prompt` | string | What to generate. Optional when you only animate an image or make it speak. |
| `imageRef` | media | Reference image: the first frame to animate, or the portrait for speech. `image` is accepted as an alias. |
| `lastImageRef` | media | Reference image for the last frame of a transition. |
| `images` | array | Long form: `[{ "url": "...", "role": "first" \| "last" \| "product" \| "style" \| "reference" }]`. |
| `references` | array | Extra style or product images. |
| `script` | string | Text the person should say. Runs through text to speech first, 1 AI credit per 10 seconds. |
| `voice` | string | Voice id from `GET /v1/audio/voices`. |
| `audio` | media | Your own audio instead of a script. |
| `consent` | boolean | Required for speech. You confirm you may use this person's likeness. |
| `duration` | number | Any whole number of seconds, 3 to 15, capped by your plan (5 on Free, 10 on Launch and Grow, 15 on Scale). Default 5. Speech follows the audio length. |
| `preset` | string | Role preset id, see below. Default `cinematic-scene`. |
| `look` | string | Look preset id, see below. |
| `fullHd` | boolean | 1080p output at 2x credits. |
| `aspect` | string | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`. |
| `fresh` | boolean | Skip the cache and generate a new take. |
| `sync` | boolean | Wait for the result instead of returning a job. |
| `webhook_url` | string | Called with the job when it finishes. |

## Generate at render time

Any video element with a parameter id accepts the same inputs as dotted keys on a render. The clip is generated, cached, and rendered into the template in one call. Renders that generate video run as async jobs.

```json
{
  "templateId": 18917,
  "modifications": {
    "hero_clip.prompt": "the heart-shaped cloud erupts upward with a bright flash",
    "hero_clip.imageRef": "https://…/love-bomb.jpeg",
    "hero_clip.preset": "cinematic-scene",
    "hero_clip.look": "archival-bw",
    "hero_clip.duration": 5,
    "page2@bg.prompt": "soft gradient waves in brand colours",
    "page2@bg.preset": "background-loop"
  },
  "response": { "format": "mp4", "mode": "async" }
}
```

Supported keys on a video element: `.prompt`, `.imageRef`, `.lastImageRef`, `.images`, `.references`, `.audio`, `.script`, `.voice`, `.consent`, `.preset`, `.look`, `.duration`, `.fullHd`.

To reuse a clip you already generated, pass its reference or URL as the element value instead. No credits are spent:

```json
{ "modifications": { "hero_clip": "gen://0472d0…" } }
```

Identical inputs return the cached clip for free, so a template rendered a hundred times with the same footage prompt generates once.

## Presets

Presets keep prompts short and results consistent. A **role** says what the clip does inside a page and locks duration, motion and where text can go. A **look** is a visual style. Both are optional and both are listed, with preview clips, at `GET /v1/ai/presets`.

Roles: `background-loop`, `product-hero`, `b-roll`, `hook`, `transition`, `ambient-texture`, `portrait-speak`, `ugc-selfie-talk`, `ugc-product-demo`, `unboxing`, `cinematic-scene`.

Looks include `archival-bw`, `cinematic`, `clean-studio`, `macro`, `aerial`, `anime`, `claymation`, `paper-cutout`, `neon`, `minimal-3d`, `watercolour`, `retro-vhs`, `documentary-archival`, `storybook`, `3d-animated`, `cartoon`, `comic-manga`, `pixel-art`, `sketch`, `pop-art`, `cyberpunk`, `noir`, `vintage-70s`, `horror`, `epic`, `ugc-selfie`, `product-commercial`, `food-macro`, `real-estate`, `travel-drone`, `sports-action`, `tech-gradient`, `news-broadcast`, `music-visualiser`, `fashion-editorial`, `toy-bricks`, `hyper-bright`.

## UGC and speech

To make a photo talk, send a clear portrait, a script or audio, and `consent: true`:

```json
{
  "imageRef": "brand:image:34523",
  "script": "Three reasons this launch matters, and why you should care today.",
  "voice": "XrExE9yKIg1WjnnlVkGX",
  "consent": true,
  "preset": "ugc-selfie-talk"
}
```

Speech clips take one to two minutes. The script runs through text to speech first, which costs 1 AI credit per 10 seconds on top of the clip. Speech is available on paid plans.

## Limits and errors

| Code | Meaning |
|---|---|
| `ai-media-no-input` | Nothing usable was sent. Give a prompt, an image, or a photo plus a script. |
| `ai-credits-insufficient` | Not enough AI credits. Top up or upgrade. |
| `ai-daily-cap` | The workspace's daily AI generation cap is reached (429). Resets at midnight UTC; do not retry until then. |
| `ai-video-not-on-plan` | AI video is not included in the plan. |
| `ai-speech-consent-required` | Speech needs `consent: true`. |
| `ai-speech-not-on-plan` | Speech needs a paid plan. |
| `ai-speech-too-long` | The script or audio runs past the plan's speech cap (`plan.ai_credits.video.max_speech_seconds`). Nothing was charged. |
| `ai-unknown-preset`, `ai-unknown-look`, `ai-invalid-aspect` | An id that is not in `GET /v1/ai/presets`, or an aspect outside `1:1, 16:9, 9:16, 4:3, 3:4`. |
| `ai-prompt-too-long`, `ai-script-too-long` | Over 2,000 characters. |
| `invalid-media-ref`, `invalid-data-uri`, `input-too-large`, `asset-not-found` | A media value that is not an https URL, an image/audio/video data URI under 25 MB, a brand asset in this workspace, or a `gen://` reference. |
| `ai-media-rejected` | The provider refused the inputs, for example a photo with no face. Nothing was charged. |
| `ai-media-provider-down` | All providers failed. Nothing was charged. Retry later. |
| `ai-video-unavailable` | No generation provider is configured on this server. |
| `ai-media-busy` | The same clip is already being generated; wait for it instead of resending. |
| `too-many-active-jobs` | The workspace has too many queued or running jobs; wait for some to finish. |
| `generation-timeout` | The job ran past 30 minutes and was closed. Retry; contact support with the job id if it repeats. |

Clip length is capped per plan (10 seconds on Launch, 15 on Scale), and speech at 30 to 90 seconds. Twenty generation requests per minute per key.