---
title: Make performance ads for Meta, TikTok and YouTube, named and iterated from results
description: Make new ad variants each week from a few masters, named by a scheme, and pick next week's variants from your results sheet.
---
Make performance ads, also called ad creatives or variants, at a steady weekly pace for Meta, TikTok and YouTube. Your agent keeps one master timeline per concept, makes each variant as a copy with a new hook, line or call to action, sizes it for each placement, and names every file by your scheme. Each week it reads your results sheet and proposes the next round.

This is ordinary ad work for a real product. The agent makes and names the files. You upload them, set budgets, and switch ads on in Ads Manager. The agent never touches a live ad, never changes a budget, and never publishes.

Use invideo for this when you want every variant to stay an editable timeline beside its master, so next week's change is a few edits and a free export, not a new video.

## Quickest path

Set up your agent first with [invideo for AI agents](/agents). Then give it this prompt.

```text
Here are this week's results: ./results/week-44.csv. My masters are in
<project-url>. Read the results, tell me which hooks and lines won, and
propose 12 new variants. After I approve the list, make each one as a copy
of its master, in 9:16 and 4:5, named by my scheme, and export them all.
```

In a chat app, you paste the results table into the chat. In a terminal, the agent reads the CSV from the folder you name.

## What you need

The agent needs the masters, the scheme and the numbers.

- One master timeline per concept, already cut and approved.
- The variant list: hooks, lines, offers or calls to action to swap. The agent can propose them from your results.
- The placements: 9:16 for Reels, Stories, TikTok and Shorts, 4:5 or 1:1 for feeds, 16:9 for YouTube in-stream.
- Your naming scheme. One example: week, concept, hook, size, version, like `w44_c2_hookB_9x16_v1`.
- Your results sheet, exported from Ads Manager as CSV, with one row per ad name.

## Steps

The agent works in rounds. Each round is one branch, so last week's ads stay as they were.

<Steps>
<Step title="Read the results">
The agent reads your CSV with its own file tools and matches each row to a file name. It sorts by the metric you name, like cost per purchase or hook rate. It tells you which hooks, lines and sizes won, and what it proposes next. You see the ranked list; nothing in invideo changes.
</Step>
<Step title="Open this week's round on a branch">
The agent reads the master with `get_timeline` and creates a branch with `timeline_history` action `create_branch`, named for the week. It sees the branch URL.
</Step>
<Step title="Copy the master once per variant">
The agent runs `duplicate_timeline` with a `name` that matches the file name. The copies share nothing with the master. It sees one new timeline id per variant.
</Step>
<Step title="Change what the variant tests">
For a new hook line, the agent runs `set_text` on the hook's text clip. For a new opening shot, it uses `place_media` mode `replace`. It sees a new `state_token`.
</Step>
<Step title="Generate a shot the variant needs">
The agent runs `find_models` with the job in words, picks one, and reads its inputs with `get_model`. A good pick is `seedance_2_5.frames`. It calls `generate` with `dry_run: true` and tells you the price. Then it calls `generate` with `max_credits` set to that price and an `idempotency_key`. It sees a sequence URL, reads it with `read`, and places it with `place_media`.
</Step>
<Step title="Size each placement">
The agent sets the canvas with `set_timeline_settings` and fixes each clip with `fill_frame` and `set_position`. It adds safe zone lines with `create_guide`, from the platform's spec page, and moves text inside them. The page [Resize one master ad for every placement, with safe zones](/how-to/marketing/every-placement-from-one-master) has the details. It sees the new canvas in `get_timeline`.
</Step>
<Step title="Check every variant, then export">
The agent calls `capture` on each timeline at the hook, the middle and the call to action, and runs `check_timeline`. Then it runs `export` once per timeline, with `timeline_id`, `file_name` set to the scheme name and an `idempotency_key`. It sees one export card per variant, each with status `completed`, and gives you the list of names.
</Step>
</Steps>

The terminal version, for one variant:

```bash
invideo agent register <project-url> --name <name>
export INVIDEO_AS=<name>
invideo edit show <edit-url> --json
invideo branch new <edit-url> --name w44 --token <state_token>
invideo edit show <branch-url> --json
invideo edit change <branch-url> --timeline T1 --token <state_token> \
  --operations @copy-master.json
invideo gen models <project-url> --query "short product shot for an ad"
invideo gen model <project-url> --model seedance_2_5.frames
invideo gen run <page-url> --model seedance_2_5.frames --dry-run \
  -i prompt="hand pours cold brew over ice" -i aspect_ratio=9:16 -i duration=5
invideo gen run <page-url> --model seedance_2_5.frames \
  --max-credits <price> --idempotency-key w44-c2-b-shot \
  -i prompt="hand pours cold brew over ice" -i aspect_ratio=9:16 -i duration=5
invideo edit show <branch-url> --json
invideo edit change <branch-url> --timeline T7 --token <state_token> \
  --operations @hookB.json
invideo edit capture <branch-url> --timeline T7 --at 0.5,3,14 \
  --out ./frames
invideo edit export <branch-url> --format video --quality 1080 \
  --timeline T7 --file-name w44_c2_hookB_9x16_v1 --idempotency-key w44-c2-b \
  --json
invideo status <card-url> --wait
invideo notebook show <card-url> --out ./ads
mv ./ads/export.mp4 ./ads/w44_c2_hookB_9x16_v1.mp4
```

Where the values come from: `<edit-url>` from `invideo edit new`, `<project-url>` from `invideo project new`, `<name>` from your choice in `invideo agent register`, and `<page-url>` from `invideo notebook show <project-url>`. `<state_token>` comes from the first `invideo edit show`, then from `invideo edit show` on the branch, read again before each change, because the main token is stale there. `<branch-url>` is the `branch_url` that `invideo branch new` returns. `<price>` is the dry run's `estimated_credits`. `<card-url>` is `.sequences[0]` of what the export returns. `invideo status` does not download, so `invideo notebook show` saves the file, always as `export.mp4`, and the agent renames it. A voice line works the same way with `elevenlabs_text_to_speech.elevenlabs_tts_v4`, `-i prompt=` and `-i voice_id=`.

The two ops files are bare lists. The first, `copy-master.json`, runs on the master `T1` and copies it. The second, `hookB.json`, runs on the copy `T7` and changes the hook. The copy's id `T7` and the hook clip's id `c14` come from `invideo edit show` on the branch.

```json
[
  { "op": "duplicate_timeline", "timeline": "T1",
    "name": "w44_c2_hookB_9x16_v1" }
]
```

```json
[
  { "op": "set_text", "clip": "c14",
    "text": "Cold brew in 30 seconds. No machine." }
]
```

## Cost and time

Edits and exports are free. Generation and transcripts cost credits. Paid stock costs a small credit charge, or one of your workspace's iStock licenses. Your agent runs a dry run first and tells you the price before it spends anything.

- Costs credits: a new generated shot, a new voice line, paid stock.
- Free: copies, text swaps, resizing, guides, `capture` and every `export`.

A variant that only swaps text and size takes a few minutes. Run a big round in parts, a few variants at a time, and check each part before the next. See [Credits](/account/credits) for prices.

## If something goes wrong

Most problems come from a stale token or a name that does not match the sheet.

| Symptom | Fix |
| --- | --- |
| `get_model` or `generate` refuses the model as not found | Models retire. Run `find_models` with the job in words, use the best match, and tell the person which model you used. |
| Exit code 6, or `status` is `held` | A safety review, not a refusal. Wait, then run `invideo status <url> --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| The download folder is empty | `invideo status` does not download. Run `invideo notebook show <card-url> --out ./ads` with the card URL from `.sequences[0]`. |
| The variant needs a new voice line | Make it with `elevenlabs_text_to_speech.elevenlabs_tts_v4` and a `voice_id` from `search_library` in `elevenlabs_text_to_speech_voices`, priced and run the same way as a shot. |
| The saved file is `export.mp4`, not the scheme name | `file_name` does not name the saved file. Rename it to the scheme name after the download. |
| CLI exit 3 | The edit changed since your read. Run `invideo edit show` on the URL you edit, the branch URL on a branch, and rebuild the ops with the new token. |
| A result row matches no file | The ad was renamed in Ads Manager. Ask the person for the mapping. Do not guess. |
| Two exports have the same name | Add the version to `file_name`. Each name in the scheme must be unique. |
| Text sits under the TikTok buttons | Move it inside the guides with `set_position`, and capture again. |
| An export never finishes | `read` the card or run `invideo status --wait`. Do not call `export` again. |
| Avoid: editing the master for a variant | Instead: `duplicate_timeline`, and change the copy. |
| Avoid: changing an ad that is live | Instead: make a new variant with a new version number. The person swaps it in Ads Manager. |
| Avoid: claiming a variant will win | Instead: report what the numbers say and how many results each row has. |

## Prompts to try

Each prompt is a real variation of this job.

```text
Hook B beat hook A on every concept. Make hook B versions of the three
concepts that don't have one, in 9:16 and 4:5, version 1.
```

```text
Make 5 call-to-action variants of concept 2: "Shop now", "Try it free",
"See flavors", "Get 2 cans", "Order now". 9:16 only.
```

```text
List every variant made in October with its name, its master, and what
it changed. Put it in a brief note so the team can see it.
```

## When to use something else

invideo does not read Ads Manager, place ads, or set budgets. Export your results from Meta Ads Manager, TikTok Ads Manager or Google Ads as a CSV, and upload the finished files there yourself. For automatic creative testing inside a platform, use the platform's own tools, like Meta's Advantage+ creative options. For a shared tracker of names and results, use a spreadsheet.

## Related

- [Test hooks on one ad, with branches](/how-to/marketing/test-hooks-on-branches)
- [Resize one master ad for every placement, with safe zones](/how-to/marketing/every-placement-from-one-master)
- [Make static ads at volume from the same brief as your video ads](/how-to/marketing/static-ads-at-volume)
- [Make a UGC ad from product photos with an AI actor](/how-to/marketing/ugc-ad-with-ai-actor)
- [CSV to videos](/how-to/developers/csv-to-videos)
- [Label AI content so your ad runs on TikTok, Meta and Google](/how-to/marketing/ai-disclosure-for-ads)