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

# Create a video

> Make a video from any recipe in one call: we write the story and script, cast it and render it.

[← Videos](https://docs.makelocalads.com/ugc-videos/overview.md)

**Endpoint:** `POST https://makelocalads.com/api/v1/videos`
**Permission:** `videos:generate`

> [card] [Open in LocalAds](https://makelocalads.com/app/a1516a75-9be5-461a-93af-c1896a0a3127?tab=studio\&type=video-ads\&section=create)
>
> The create-video dialog in the product's Video Ads studio.

Make a video from any recipe the app offers: a 30 second UGC voiceover, a
Pixar style story, a podcast clip, a song and more. List them, with each
one's length range and top price, from `GET /video-recipes`.

You make the same choices as the app's create-video dialog: the recipe, the
audience and the language. We write a story for that audience (or follow
`story_idea`, your own idea in your words), write the script, draw the cast,
pick their voices and render it.

## Usage notes

* `audience_id` is required: a saved audience of the product, or `null` for a
  deliberately broad video. Omitting it is an error.
* The product's plan must include Video Ads.
* The recipe's top price (`max_credits` in `GET /video-recipes`) is charged
  when the request is accepted. Once the script is written, the video costs
  what it runs to and the rest is refunded, so `credits_charged` on the video
  drops to its real price. It never costs more than the top price.
* `max_credits` refuses the request with `422` before anything is charged if
  the recipe's top price is more. A video that fails is refunded in full.
* A recipe made in a style or footage look (stylized animation, found
  footage) is made in its first one.
* Send an `Idempotency-Key` so a retry after a network failure never starts
  and charges for a second video.

## List the recipes

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --url https://makelocalads.com/api/v1/video-recipes \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": [
    {
      "id": "ugc-30s-voiceover",
      "title": "30s Authentic UGC Voiceover Ad",
      "subtitle": "Her voice over her own phone video",
      "min_duration_seconds": 15,
      "max_duration_seconds": 30,
      "max_credits": 30,
      "preview_video_url": "https://makelocalads.com/video-ads/blueprints/ugc-30s-voiceover-preview.mp4",
      "poster_url": "https://makelocalads.com/video-ads/blueprints/ugc-30s-voiceover-poster.jpg"
    },
    {
      "id": "pixar-style",
      "title": "Pixar Style Ad",
      "subtitle": "3D animated story",
      "min_duration_seconds": 30,
      "max_duration_seconds": 45,
      "max_credits": 45,
      "preview_video_url": "https://makelocalads.com/video-ads/blueprints/pixar-style-preview.mp4",
      "poster_url": "https://makelocalads.com/video-ads/blueprints/pixar-style-poster.jpg"
    }
  ]
}
```

## Quick start

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  --url https://makelocalads.com/api/v1/videos \
  --header "Authorization: Bearer $LOCALADS_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: video-order-2201" \
  --data '{
    "type": "recipe",
    "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
    "recipe_id": "ugc-30s-voiceover",
    "audience_id": "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11",
    "language": "English",
    "story_idea": "A nurse finishes a double shift and still has energy for her kids.",
    "max_credits": 30
  }'
```

```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const response = await fetch("https://makelocalads.com/api/v1/videos", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LOCALADS_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "video-order-2201",
  },
  body: JSON.stringify({
    type: "recipe",
    product_id: "a1516a75-9be5-461a-93af-c1896a0a3127",
    recipe_id: "ugc-30s-voiceover",
    audience_id: "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11",
    language: "English",
    story_idea: "A nurse finishes a double shift and still has energy for her kids.",
    max_credits: 30,
  }),
});

const video = await response.json();
```

```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import os
import requests

response = requests.post(
    "https://makelocalads.com/api/v1/videos",
    headers={
        "Authorization": f"Bearer {os.environ['LOCALADS_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "video-order-2201",
    },
    json={
        "type": "recipe",
        "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
        "recipe_id": "ugc-30s-voiceover",
        "audience_id": "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11",
        "language": "English",
        "story_idea": "A nurse finishes a double shift and still has energy for her kids.",
        "max_credits": 30,
    },
)

video = response.json()
```

## Input fields

**type** `string` *required*

`recipe` for a recipe video. Use `animation` to
[animate a photo](https://docs.makelocalads.com/ugc-videos/animate-a-photo.md) instead.

**product\_id** `string` *required*

The product the video is for.

**recipe\_id** `string`

The recipe, from `GET /video-recipes`. Defaults to the 30 second UGC
voiceover, `ugc-30s-voiceover`.

**audience\_id** `string` *required*

A saved audience of the product, from
`GET /products/{product_id}/audiences`, or `null` for a deliberately broad
video.

**language** `string` (default: English)

The language the video is spoken in, such as `English`, `Hindi` or
`Hinglish`.

**story\_idea** `string`

Your own idea for the story, in your words, up to 2,000 characters. The
story and script keep to it. Leave it out and we write the story.

**max\_credits** `integer`

Refuse the request with `422 credit_limit_exceeded`, before anything is
charged, if the recipe's top price is more than this many credits.

> [accordion] Complete JSON schema
>
> ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
> {
>   "type": "object",
>   "additionalProperties": false,
>   "required": ["type", "product_id", "audience_id"],
>   "properties": {
>     "type": { "type": "string", "const": "recipe" },
>     "product_id": { "type": "string", "format": "uuid" },
>     "recipe_id": { "type": "string", "default": "ugc-30s-voiceover" },
>     "audience_id": { "type": ["string", "null"], "format": "uuid" },
>     "language": { "type": "string", "default": "English" },
>     "story_idea": { "type": "string", "minLength": 1, "maxLength": 2000 },
>     "max_credits": { "type": "integer", "minimum": 1 }
>   }
> }
> ```

## Response

`202 Accepted`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "e2d4c6a8-1b3f-4d5e-9a7c-0f1e2d3c4b5a",
  "type": "recipe",
  "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
  "status": "in_progress",
  "title": "30s Authentic UGC Voiceover Ad",
  "recipe_id": "ugc-30s-voiceover",
  "audience_id": "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11",
  "source_creative_id": null,
  "video_url": null,
  "thumbnail_url": null,
  "duration_seconds": 30,
  "aspect_ratio": "9:16",
  "created_at": "2026-09-25T10:00:00.000Z",
  "credits_charged": 30
}
```

The `title` is the recipe's until the story is written, then the story's.
`duration_seconds` and `credits_charged` start at the recipe's top length and
price, and settle to the real ones once the script is written.

A video is usually ready in about 10 to 15 minutes. Poll
`GET /videos/{video_id}` with backoff until `status` is `completed` or
`failed`; see [Polling](https://docs.makelocalads.com/concepts/polling.md). A completed video carries
`video_url` and `thumbnail_url`.

## Errors

| Status | Code | Meaning |
| - | - | - |
| `400` | `invalid_request` | `audience_id` is missing (send an audience or `null`) |
| `400` | `invalid_request` | `recipe_id` or `language` is not one we offer |
| `404` | `product_not_found` | The product is not available to you |
| `422` | `plan_required` | Your plan does not include Video Ads |
| `422` | `insufficient_credits` | Not enough credits. Nothing was charged |
| `422` | `credit_limit_exceeded` | The recipe's top price is more than `max_credits` |

See [Errors and retries](https://docs.makelocalads.com/concepts/errors.md) for the error format.


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