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

# Generate Video Ads

> Submit product URLs to generate AI-powered UGC video ads

Generate professional UGC-style video ads from product URLs. The AI analyzes product pages, creates scripts, generates video scenes with AI actors, synthesizes voiceovers, and produces fully-edited videos ready for social media.

Every parameter except `urls` is optional — the AI auto-determines actor, script style, language, and creative direction from the product page.

## Authentication

<ParamField header="Authorization" type="string" required>
  Your Nouvel API key with `generate` permission. Format: `Bearer nvl_xxxx`
</ParamField>

## Request Body

### Required

<ParamField body="urls" type="string[]" required>
  Array of 1-3 product page URLs to generate videos from. Each URL must:

  * Be a valid HTTP/HTTPS URL
  * Point to a product page (e.g., Shopify, Amazon, DTC brand sites)
  * Be publicly accessible

  <Expandable title="Example URLs">
    ```json theme={null}
    [
      "https://example.com/products/protein-powder",
      "https://shop.brand.com/skincare/serum"
    ]
    ```
  </Expandable>
</ParamField>

### Generation Settings

<ParamField body="variantCount" type="integer" default="1">
  Number of video variants to generate per URL. Must be between 1-3.

  **Total projects** = `urls.length × variantCount`

  <Note>
    Each video variant uses a different creative approach (script angle, actor, pacing). Variants help you A/B test what resonates with your audience.
  </Note>
</ParamField>

<ParamField body="aspectRatio" type="string" default="9:16">
  Video aspect ratio. Valid values:

  | Value  | Description                                 |
  | ------ | ------------------------------------------- |
  | `9:16` | Vertical / portrait (TikTok, Reels, Shorts) |
  | `16:9` | Horizontal / landscape (YouTube, web)       |
  | `1:1`  | Square (Instagram feed)                     |
</ParamField>

<ParamField body="adDuration" type="integer" default="15">
  Target video duration in seconds. Valid values: `15`, `30`, `45`, `60`.
</ParamField>

<ParamField body="contentType" type="string" default="ad">
  Content purpose. `ad` generates a direct-response ad with a CTA. `organic` generates a softer, editorial-style video.

  Valid values: `ad`, `organic`
</ParamField>

### Script & Style

<ParamField body="scriptStyle" type="string" default="problem_solution">
  Script framework for the AI copywriter. Determines the narrative structure.

  | Value              | Description                                                         |
  | ------------------ | ------------------------------------------------------------------- |
  | `problem_solution` | Pain point → product as the fix. Universal workhorse. **(default)** |
  | `three_reasons`    | Listicle format — "3 reasons this works." High retention.           |
  | `discovery`        | "I just found this..." — curiosity gap, insider authority.          |
  | `social_proof`     | Comment reply format — leverages reviews and real conversations.    |
  | `special_offers`   | Promo, discount, FOMO — "this deal won't last."                     |
  | `storytelling`     | Brand narrative arc — origin, mission, emotional connection.        |
  | `benefits`         | Direct value proposition — clean, benefit-driven sell.              |
  | `how_to`           | Tutorial / educational — step-by-step, high retention.              |
  | `emotional`        | Transformation story — before/after, aspirational.                  |
  | `gen_z`            | TikTok-native, casual, high relatability.                           |
</ParamField>

<ParamField body="overrideScript" type="string">
  Provide your own voiceover script. When set, the AI skips script generation entirely and uses this text as the voiceover. Maximum 5000 characters.

  <Info>
    The script is still processed for lip-sync timing and caption alignment, but the words are exactly what you provide.
  </Info>
</ParamField>

<ParamField body="userInstructions" type="string">
  Free-form creative directions for the AI. Maximum 2000 characters. Use this for nuanced guidance that doesn't fit into the structured parameters.

  <Expandable title="Example Instructions">
    * "Focus on the eco-friendly packaging and sustainability angle"
    * "Use an energetic, upbeat tone with fast pacing"
    * "Emphasize the science behind the product with authority"
    * "Keep it casual and relatable, like a friend's recommendation"
  </Expandable>

  <Info>
    User instructions are the highest-priority creative input — they override default AI choices for style, actor, script, and visual direction.
  </Info>
</ParamField>

### Actor & Avatar

<ParamField body="selectedActorId" type="string">
  UUID of a specific actor from your Nouvel account. When set, the AI uses this actor for all avatar scenes instead of auto-selecting.

  <Note>
    If omitted, the AI auto-selects an actor based on the product category and target audience. For example, beauty products default to a female actor; fitness supplements default to a male actor.
  </Note>
</ParamField>

<ParamField body="avatarImageUrl" type="string">
  HTTPS URL to a custom avatar photo. Use this to generate a video with any person's likeness instead of a Nouvel stock actor.

  The image should be:

  * A clear, front-facing headshot or upper-body photo
  * At least 512×512 pixels
  * JPEG, PNG, or WebP format

  <Warning>
    You are responsible for having the rights to use any custom avatar image. Nouvel does not verify image ownership.
  </Warning>
</ParamField>

### Language & Localization

<ParamField body="language" type="string" default="auto">
  Language for the voiceover script. When set to `auto` or omitted, the language is auto-detected from the product page.

  Common values: `en` (English), `es` (Spanish), `fr` (French), `de` (German), `pt` (Portuguese), `ja` (Japanese), `ko` (Korean), `zh` (Chinese), `hi` (Hindi), `ar` (Arabic).

  <Expandable title="All supported languages">
    `en`, `es`, `fr`, `de`, `pt`, `it`, `nl`, `pl`, `ru`, `ja`, `ko`, `zh`, `hi`, `ar`, `tr`, `sv`, `no`, `th`, `vi`, `id`, `ms`, `tl`, `cs`, `ro`, `el`, `bg`, `hr`, `bn`
  </Expandable>
</ParamField>

<ParamField body="accent" type="string">
  Voice accent override. When omitted, the accent is auto-determined from the language (e.g., English defaults to American).

  For English, valid accents include: `American English accent`, `British English accent`, `Australian English accent`, `Indian English accent`, `Canadian English accent`.
</ParamField>

### Audience

<ParamField body="targetAudience" type="string">
  Description of your target audience. Maximum 500 characters. The AI tailors the script tone, vocabulary, hook strategy, and actor selection to match.

  <Expandable title="Example Audiences">
    * "Women 25-35 interested in clean beauty and natural skincare"
    * "Male fitness enthusiasts aged 18-30 who follow gym culture on TikTok"
    * "Busy parents looking for convenient, healthy meal solutions"
    * "Tech-savvy professionals aged 30-45 who value productivity tools"
  </Expandable>
</ParamField>

### Advanced

<ParamField body="formatPreset" type="string">
  Visual layout preset ID. Controls the scene structure and narrative arc. Contact support for available preset IDs.
</ParamField>

<ParamField body="webhookUrl" type="string">
  HTTPS URL to receive a notification when the job completes (or fails). The URL must:

  * Use HTTPS (HTTP is rejected)
  * Not point to localhost or private IP ranges

  Nouvel retries failed deliveries up to 3 times with exponential backoff (immediate → 10s → 60s).

  <Info>
    See the [Webhooks guide](/api-reference/webhooks) for payload format, signature verification, and retry behavior.
  </Info>
</ParamField>

## Response

<ResponseField name="jobId" type="string" required>
  UUID of the generation job. Use this to poll status via `/api/v1/jobs/{jobId}`.

  ```json theme={null}
  "550e8400-e29b-41d4-a716-446655440000"
  ```
</ResponseField>

<ResponseField name="totalProjects" type="integer" required>
  Total number of videos that will be generated (`urls.length × variantCount`).
</ResponseField>

<ResponseField name="status" type="string" required>
  Job status. Always `"running"` on successful submission.
</ResponseField>

## Example Requests

<CodeGroup>
  ```bash cURL - Minimal (auto everything) theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/generate \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "urls": ["https://example.com/products/protein-powder"]
    }'
  ```

  ```bash cURL - Full control theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/generate \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "urls": ["https://example.com/products/protein-powder"],
      "variantCount": 2,
      "scriptStyle": "discovery",
      "aspectRatio": "9:16",
      "adDuration": 15,
      "language": "en",
      "targetAudience": "Male fitness enthusiasts aged 18-30",
      "webhookUrl": "https://your-server.com/webhooks/nouvel"
    }'
  ```

  ```typescript TypeScript - With custom avatar theme={null}
  const response = await fetch('https://app.nouvel.ai/api/v1/generate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.NOUVEL_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      urls: ['https://example.com/products/skincare-serum'],
      variantCount: 2,
      scriptStyle: 'social_proof',
      avatarImageUrl: 'https://your-cdn.com/influencer-photo.jpg',
      targetAudience: 'Women 25-35 interested in clean beauty',
      language: 'en',
    }),
  });

  const { jobId, totalProjects } = await response.json();
  console.log(`Job ${jobId} started. Generating ${totalProjects} videos.`);
  ```

  ```python Python - With script override theme={null}
  import requests

  response = requests.post(
      'https://app.nouvel.ai/api/v1/generate',
      headers={
          'Authorization': f'Bearer {api_key}',
          'Content-Type': 'application/json',
      },
      json={
          'urls': ['https://example.com/products/skincare-serum'],
          'overrideScript': (
              'I have been using this serum for three weeks now and '
              'honestly my skin has never looked this good. The texture '
              'is lightweight, absorbs in seconds, and that glow is real. '
              'Check the link below.'
          ),
          'aspectRatio': '9:16',
      }
  )

  data = response.json()
  print(f"Job {data['jobId']} started.")
  ```
</CodeGroup>

## Response Examples

<CodeGroup>
  ```json 200 - Success theme={null}
  {
    "jobId": "550e8400-e29b-41d4-a716-446655440000",
    "totalProjects": 2,
    "status": "running"
  }
  ```

  ```json 400 - Invalid Parameter theme={null}
  {
    "error": "Invalid scriptStyle \"foobar\". Valid: problem_solution, three_reasons, discovery, social_proof, special_offers, storytelling, benefits, how_to, emotional, gen_z"
  }
  ```

  ```json 400 - Invalid Request theme={null}
  {
    "error": "urls must be an array of 1-3 URLs"
  }
  ```

  ```json 401 - Unauthorized theme={null}
  {
    "error": "Invalid API key"
  }
  ```

  ```json 402 - Quota Exceeded theme={null}
  {
    "error": "quota_exceeded",
    "reason": "Monthly plan quota exhausted. Insufficient credit balance for overage billing.",
    "usage": {
      "used": 50,
      "limit": 50
    }
  }
  ```

  ```json 403 - Forbidden theme={null}
  {
    "error": "Forbidden",
    "details": "API key does not have 'generate' permission"
  }
  ```

  ```json 422 - Not a Product Page theme={null}
  {
    "error": "not_a_product",
    "reason": "example.com is not a product page. Paste a link to a product you want to advertise."
  }
  ```
</CodeGroup>

## Error Codes

| Code | Description                                                                                               |
| ---- | --------------------------------------------------------------------------------------------------------- |
| 400  | Invalid JSON body, invalid URL format, or invalid parameter value. Check the `error` message for details. |
| 401  | Invalid or missing API key                                                                                |
| 402  | Quota exceeded. Check `usage` object for current limits.                                                  |
| 403  | API key missing `generate` permission                                                                     |
| 422  | URL recognized as non-product page (blog, homepage, etc.)                                                 |
| 500  | Internal server error                                                                                     |

## How It Works

<Steps>
  <Step title="Product Analysis">
    The AI scrapes the product page, extracting:

    * Product name, description, and benefits
    * Images and branding
    * Language and pricing
  </Step>

  <Step title="Script Generation">
    A proprietary model trained on hundreds of high-performing ad scripts generates a voiceover script using the selected `scriptStyle`. Supports 30+ languages.

    * Scripts are optimized for natural speech patterns
    * Word count is enforced server-side to match the target duration
  </Step>

  <Step title="Visual Concept">
    AI selects the best product photos, generates feature badges, headline text, and CTA copy based on the script and scraped product data.
  </Step>

  <Step title="Video Production">
    * Synthesizes natural voiceover from the script
    * Generates a lip-synced avatar speaking the hook
    * Composes product slides with animated transitions
    * Renders the final video with captions
  </Step>

  <Step title="Final Delivery">
    Exports MP4 files in your selected aspect ratio, optimized for social platforms.
  </Step>
</Steps>

<Warning>
  **Generation is asynchronous.** This endpoint returns immediately with a `jobId`. Videos take 2-5 minutes to generate. Poll `/api/v1/jobs/{jobId}` for status updates, or use a `webhookUrl` to be notified on completion.
</Warning>

## Auto-Detection Defaults

When you submit just a URL, the AI auto-determines everything:

| Parameter        | Auto-Detection Logic                                                                |
| ---------------- | ----------------------------------------------------------------------------------- |
| **Actor**        | AI selects based on product category gender rules (beauty → female, fitness → male) |
| **Language**     | Detected from product page metadata, defaults to English                            |
| **Accent**       | Matched to language (English → American, Spanish → Spanish accent)                  |
| **Script style** | Defaults to `problem_solution`                                                      |
| **Duration**     | 15 seconds                                                                          |
| **Aspect ratio** | 9:16 (vertical)                                                                     |

## Billing & Quotas

* Each video generated counts as **1 credit** toward your monthly quota
* If your plan quota is exhausted but you have credit balance, **overage billing** applies at **\$8/video**
* Generation fails with `402` if both quota and credit balance are exhausted

<Info>
  **Example:** With a 50-video/month plan:

  * Request 1: `urls: [A, B], variantCount: 2` → 4 credits used, 46 remaining
  * Request 2: `urls: [C], variantCount: 3` → 3 credits used, 43 remaining
</Info>

## Best Practices

<AccordionGroup>
  <Accordion title="Start simple, then customize">
    Send just `urls` first to see what the AI produces. Then add `scriptStyle`, `targetAudience`, or `userInstructions` to fine-tune results. Over-specifying can constrain the AI unnecessarily.
  </Accordion>

  <Accordion title="Use targetAudience for tone matching">
    Instead of verbose `userInstructions`, use `targetAudience` to let the AI adapt vocabulary, hook strategy, and energy level automatically. A "Gen Z TikTok users" audience gets a very different script than "C-suite executives."
  </Accordion>

  <Accordion title="Custom avatars for brand consistency">
    Use `avatarImageUrl` with your brand ambassador or influencer's photo for consistent branding across all videos. Pair with `overrideScript` for full creative control.
  </Accordion>

  <Accordion title="Generate variants for A/B testing">
    Use `variantCount: 2-3` to test different creative approaches. Variants use different scripts, hook strategies, and creative angles automatically.
  </Accordion>

  <Accordion title="Monitor generation progress">
    Poll `/api/v1/jobs/{jobId}` every 10-15 seconds, or register a `webhookUrl` to be notified automatically. Check the `stage` field to see pipeline progress.
  </Accordion>
</AccordionGroup>

## Next Steps

After submitting a generation job:

1. **Poll for status**: Use [Get Job Status](/api-reference/jobs) to monitor progress — or register a [webhook](/api-reference/webhooks) to be notified on completion
2. **Download videos**: Once `status: "completed"`, find video URLs in `completedDetails[].finalOutputUrl`
3. **Publish to social**: Use the [Publishing API](/api-reference/publish) to schedule posts across platforms
4. **Track performance**: Use the [Analytics API](/api-reference/analytics) to monitor engagement metrics
