> ## 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 ad

> Make a 30 second UGC video from an audience, recipe, concept and voice, or animate a photo.

The API makes videos the way the app does. A UGC video follows the same four
steps as the create-video dialog, and you make every choice the dialog lets
you make:

1. **Audience**: who the video is for. Required: a saved audience, or `null`
   for a deliberately broad video.
2. **Recipe**: the video format. Today there is one, a 30 second UGC voiceover.
3. **Concept**: pick one of three concepts, each with its own hook, setting
   and creator.
4. **Voice**: pick the narrator.

You can also animate any finished Quick Shoot photo into a short video.

A UGC video costs 2 credits per second: 60 credits for 30 seconds. An
animation costs 2 credits per second of its length. Concepts, voices and
recipes are free.

## Before you start

You need:

* an API key with `videos:generate` and `videos:read`
* a product in LocalAds, on a plan that includes Video Ads
* 60 credits for a UGC video

## 1. Choose an audience

Every product gets audiences automatically when it is created. List them and
pick one:

```bash theme={null}
curl https://makelocalads.com/api/v1/products/a1516a75-9be5-461a-93af-c1896a0a3127/audiences \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

Use its `id` as `audience_id` in the next steps, or `null` for a broad video.
Leaving `audience_id` out is an error, so an audience is never skipped by
accident. See [Audiences](/guides/manage-audiences).

## 2. Choose a recipe

```bash theme={null}
curl https://makelocalads.com/api/v1/video-recipes \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "ugc-30s-voiceover",
      "title": "30s Authentic UGC Voiceover Ad",
      "subtitle": "UGC Voiceover Recipe",
      "duration_seconds": 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"
    }
  ]
}
```

## 3. Generate and pick a concept

Start three concepts for the audience:

```bash theme={null}
curl --request POST \
  --url https://makelocalads.com/api/v1/video-concepts \
  --header "Authorization: Bearer $LOCALADS_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
    "audience_id": "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11"
  }'
```

Concepts are kept per audience. If this audience already has concepts, you
get them back instead of new ones; send `"force": true` to replace them.

Poll until three concepts have titles:

```bash theme={null}
curl "https://makelocalads.com/api/v1/video-concepts?product_id=a1516a75-9be5-461a-93af-c1896a0a3127&audience_id=0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11" \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "5b7e0f7a-6a55-4f0b-9f6c-8f0a2c1d3e4f",
      "status": "completed",
      "title": "The 6 a.m. calm",
      "description": "A parent shows how the throw turns a chaotic morning into a quiet one.",
      "image_url": "https://cdn.example.com/concepts/calm.png",
      "creator_gender": "female"
    }
  ]
}
```

A concept can be picked as soon as it has a title; its image is a preview.

## 4. Pick a voice

Voices default to the accent of the market your brand sells in. Filter by
the concept's creator so the narrator matches:

```bash theme={null}
curl "https://makelocalads.com/api/v1/voices?product_id=a1516a75-9be5-461a-93af-c1896a0a3127&gender=female" \
  --header "Authorization: Bearer $LOCALADS_API_KEY"
```

```json theme={null}
{
  "locale": "en-US",
  "market_country": "US",
  "data": [
    {
      "id": "9c3b7a1e-2d4f-4e6a-8b0c-1d2e3f4a5b6c",
      "name": "Maya",
      "tagline": "Warm and conversational",
      "gender": "feminine",
      "accent": "American",
      "locale": "en-US",
      "preview_url": "https://makelocalads.com/api/video-ads/voice-preview/9c3b7a1e-2d4f-4e6a-8b0c-1d2e3f4a5b6c?v=2"
    }
  ]
}
```

Pass `locale` (for example `en-GB` or `hi-IN`) for another accent.
`preview_url` plays a short demo and needs no key.

## 5. Create the video

```bash theme={null}
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": "ugc",
    "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
    "recipe_id": "ugc-30s-voiceover",
    "audience_id": "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11",
    "concept_id": "5b7e0f7a-6a55-4f0b-9f6c-8f0a2c1d3e4f",
    "voice_id": "9c3b7a1e-2d4f-4e6a-8b0c-1d2e3f4a5b6c",
    "max_credits": 60
  }'
```

```javascript theme={null}
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: "ugc",
    product_id: "a1516a75-9be5-461a-93af-c1896a0a3127",
    recipe_id: "ugc-30s-voiceover",
    audience_id: "0f0c5a4e-54f7-4f0e-9d4e-1a8c3a6b5d11",
    concept_id: "5b7e0f7a-6a55-4f0b-9f6c-8f0a2c1d3e4f",
    voice_id: "9c3b7a1e-2d4f-4e6a-8b0c-1d2e3f4a5b6c",
    max_credits: 60,
  }),
});

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

`concept_id` and `voice_id` are optional. Leave one out and the render
chooses, the same as the app's defaults.

LocalAds returns `202 Accepted`:

```json theme={null}
{
  "id": "e2d4c6a8-1b3f-4d5e-9a7c-0f1e2d3c4b5a",
  "type": "ugc",
  "product_id": "a1516a75-9be5-461a-93af-c1896a0a3127",
  "status": "in_progress",
  "title": "The 6 a.m. calm",
  "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": 60
}
```

A video is usually ready in about five minutes. Poll `GET /videos/{video_id}`
with backoff until `status` is `completed` or `failed`. A render that fails is
refunded.

## Animate a photo

Turn a completed Quick Shoot photo into a video. The choices match the app's
Animate dialog: 5 to 15 seconds, Portrait (`9:16`), Landscape (`16:9`) or
Square (`1:1`), and an optional prompt.

```bash theme={null}
curl --request POST \
  --url https://makelocalads.com/api/v1/videos \
  --header "Authorization: Bearer $LOCALADS_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "type": "animation",
    "source_creative_id": "3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9",
    "duration_seconds": 10,
    "aspect_ratio": "9:16",
    "prompt": "Slow push-in as steam rises from the cup."
  }'
```

A 10 second animation costs 20 credits. Poll it the same way.

## Errors

| Status | Code | Meaning |
| - | - | - |
| `400` | `invalid_request` | `audience_id` is missing from a UGC video or concepts request (send an audience or `null`) |
| `404` | `product_not_found`, `audience_not_found`, `concept_not_found`, `voice_not_found`, `creative_not_found` | Something you referenced is not available to you |
| `422` | `plan_required` | Your plan does not include Video Ads |
| `422` | `request_rejected` | For animation, the source is not a completed Quick Shoot photo |
| `422` | `insufficient_credits` | Not enough credits. Nothing was charged |
| `422` | `credit_limit_exceeded` | The video would cost more than `max_credits` |
