# Skill: full/stock-broll — "B-roll מסטוק במקום וידאו AI" (Complete video)

The kinetic CapCut edit of a talking head with real B-roll instead of generated clips: Gemini Flash-Lite reads the words and names the moments a picture helps, the stock is searched, scored against the frame and checked by the same cheap model as thumbnails, and three shots cut in — a portrait one full frame, a landscape one in the lower half with the face kept on top. Every word on screen, a hook, zooms, stickers, sound design and a beat under the voice, in one render. The footage costs nothing: three credits for the whole plan, where three Veo clips would cost 360. Photos and videos provided by Pexels.

**Includes:** 3 stock shots where the words name something a camera can show · full frame or split screen with the face kept · real footage from Pexels (and CC0 collections), a licence per shot · every word on screen as it is said · hook · punch zooms · emoji stickers · sound design · music under the voice · pauses cut
**Built on:** kinetic `texts[]` (effects `words`, `punch`, `blur_in`, `rise`, `glitch`) laid out by `POST /v1/studio/kinetic` + stock footage from `POST /v1/stock/broll-plan` — no caption line; the nearest single looks are hook `words` and captions `volt`
**Price:** 112–125 credits per finished minute · 30 s ≈ 60–68 credits
**Uses:** `/v1/speech/transcripts` · `/v1/studio/remove-silence` · `/v1/studio/kinetic` · `/v1/ai/text` · `/v1/ai/sound-effects` · `/v1/stock/icon` · `/v1/ai/music` · `/v1/stock/broll-plan` · `/v1/studio/edit`

Base URL: `https://apihub.co.il` · Auth: `Authorization: Bearer <API key>` · Docs: `/docs`
Send the same `X-Tag: <job name>` header on every call of one video; `GET /v1/usage?tag=<job name>` then
returns exactly what it cost. Times in request bodies are **seconds on the edited (output) timeline**.
Unknown fields are rejected with the name of the right field — read the error instead of guessing.
**Files are kept 24 hours** (renders, uploads, generated clips/images/music) — download the finished video.

**Models — the cheapest that do the job, and no generated video.** AssemblyAI Universal-2 transcribes twice, Gemini 3.1
Flash-Lite picks the moments of the words and — inside `POST /v1/stock/broll-plan` — the stock moments and a thumbnail check,
ElevenLabs makes four short sounds and a music bed; the footage is free stock (Pexels, Pixabay, CC0 collections). Everything is
priced by the video's length.

---

## 1. Transcribe the video (once)

```jsonc
POST /v1/studio/uploads { "filename": "clip.mp4", "content_type": "video/mp4", "size_bytes": <bytes> }   // a local file → output.url (skip for a public link) · the original file, never a resized copy
POST /v1/speech/transcripts { "audio_url": "<video url>", "language_code": "he", "speech_models": ["universal-2"] }
// → T0, the video's words · Universal-2 · 64 s ≈ 1 credit
// optional — the music level: a WAV of the voice for the planner to measure (`cut_audio_url`); without it the music sits at 0.3
POST /v1/studio/convert { "video_url": "<video url>", "output": { "format": "wav" } }                      // x-wait-ms: 90000 · ≈ 0.15 credits a second
```

**One transcript, on the video's own clock.** Every element the planner writes is anchored to a word — `at` a word's
id (w0001…) or `during` a stretch of them — and the render does the rest: it finds the pauses in the sound itself
(`remove_silence`, the same numbers give the same cut every time), cuts them, and puts each text, zoom, sticker and
sound on the exact output frame its word lands on. Nothing is timed on a guessed cut clock, so the cut audio is never
transcribed a second time and there is nothing to put back from a reference transcript. The render's answer reports
where every element landed (`elements`: exact / moved / lost — a word that was cut moves its element to the next
kept word of the phrase; one with nothing left to hold on to is dropped and named).

**Why a WAV (optional).** The planner measures the voice's loudness in it (`cut_audio_url`) and sets the music
≈ 10 dB under the voice — a loud car recording (−12 LUFS) needs twice the music volume of a quiet one (−18).

**Everything below is timed on the cut video's clock.**

## 2. The first plan — the numbered words and the length

```jsonc
POST /v1/studio/kinetic
{ "video_url": "<video url>", "transcript_id": "<T0>", "cut": { "from": "audio", "min_pause_ms": 200, "padding_ms": 70 },
  "cut_audio_url": "<the wav's url>" }                                   // cut_audio_url optional (the music level) · 1 credit
// → output.words (numbered, with ids, the video's clock, seconds), output.summary.source_seconds, output.warnings
//   every element of output.edit is anchored to a word (at / during); the render cuts the pauses around them
```

## 3. Gemini Flash-Lite picks the moments of the words

```jsonc
POST /v1/ai/text                           // Gemini 3.1 Flash-Lite ≈ 1 credit
{ "quality": "fast", "json": true, "max_tokens": 3000, "temperature": 0.4,
  "system": "<the kinetic picker brief — /skills/full-kinetic.md, step 3, verbatim>",
  "prompt": "The video is <summary.source_seconds> s (its pauses will be cut). Numbered words:\n0:אין 1:כזה 2:דבר, 3:אני …",
  "media": [ { "url": "<video url>", "type": "video", "fps": 0.5 } ] }
```

Keep the corrections that repair a real mistake, drop those that change the speaker's own style (the kinetic skill
explains). Use the emphasis, the title card and the stickers as they come.

## 4. The assets — in parallel, each made once

```jsonc
POST /v1/ai/sound-effects { "text": "fast short airy whoosh swipe transition, clean, no reverb tail", "duration": 0.6, "prompt_influence": 0.5 }   // ≈ 1 credit — also the whoosh into every stock shot
POST /v1/ai/sound-effects { "text": "short soft bubble pop click for a sticker appearing, clean", "duration": 0.5, "prompt_influence": 0.5 }
POST /v1/ai/sound-effects { "text": "short digital glitch stutter burst, electronic, clean", "duration": 0.5, "prompt_influence": 0.5 }
POST /v1/ai/sound-effects { "text": "one second tension riser swell that stops abruptly, no hit", "duration": 1.0, "prompt_influence": 0.5 }
POST /v1/stock/icon { "icon": "fluent-emoji-flat:<sticker icon>", "size": 512, "padding": 4 }   // one per sticker · free
POST /v1/ai/music { "prompt": "<picks.music>", "duration": <ceil(summary.output_seconds + 1)>, "instrumental": true }   // ≈ 0.77 credits a second
```

## 5. The final kinetic plan

```jsonc
POST /v1/studio/kinetic
{ …the body of step 2…, "corrections": […], "emphasis": […], "scenes": […], "stickers": [ { "word": 17, "url": "<icon png>" }, … ],
  "sounds": { "whoosh": "<mp3>", "pop": "<mp3>", "glitch": "<mp3>", "riser": "<mp3>" }, "music": { "url": "<music mp3>" } }
// → output.edit — the complete render body; output.words — the same numbered words
```

## 6. The stock cutaways

```jsonc
POST /v1/stock/broll-plan
{ "style": "cutaways", "count": 3, "seconds": 2.6,
  "words": <output.words of step 5>, "duration": <summary.output_seconds>,
  "edit": <output.edit of step 5>,                       // the shots are merged into it
  "sounds": { "whoosh": "<the whoosh mp3>" },
  "topic": "<one line: what the video is about>" }        // ≈ 3 credits, ≈ 10 s · an advert: add "context": "ad"
// → output.edit (ready), output.shots [ { "start": 12.67, "said": "…", "query": "smartphone posting to social media",
//     "mode": "split", "source": "pexels", "license": "Pexels", "attribution": null, "check": 8 }, … ], output.warnings
```

- The shots keep clear of the hook, the black title cards, the stickers, the first 2.5 s and the last 1.5 s, 4 s apart.
- **Full or split.** A portrait shot takes the whole frame; a landscape one fills the lower half (`split_side: "bottom"`)
  with the speaker's face kept in the upper half (`speaker_focus` 0.35 = a selfie face) — the words stay on top of it.
  `mode: "full"` / `"split"` forces one; `split_side: "top"` puts the footage above the face.
- A light scrim (30 % black) sits behind the words over the shot's half, so small white words read on busy footage
  (`scrim: false` removes it).
- The model asks for 6 moments and the first 3 that fit and find good footage are used; `exclude` (ids) keeps a shot out of a
  second plan; `moments` replaces the model with your own searches.

## 7. One render

```jsonc
POST /v1/studio/edit <output.edit of step 6>           // x-wait-ms: 90000, then poll GET /v1/executions/{id} · 720p ≈ 0.75 credits a second
```

## How a shot is chosen (`POST /v1/stock/broll-plan`)

1. **Moments.** Gemini 3.1 Flash-Lite reads the numbered words and names the moments with an English search each
   (`query`), a broader second one (`alt`) and the visible subject in one or two nouns (`short`) — for the sources that
   match words exactly. Or send your own `moments` and no model is asked.
2. **Search.** Pexels and Pixabay get the full search (they rank by meaning; portrait first for a 9:16 video); Commons
   and Openverse get the nouns, widened step by step until there are enough results. Answers are cached for a day
   (Pexels allows 200 requests an hour: a cutaway plan makes ≈ 10, a faceless minute ≈ 25).
3. **Score** (0–1): the search words in the title and tags (40 %), the shape against the frame (20 % — a portrait shot
   fills 9:16, a landscape one goes in the split band), the pixels against the crop (15 % — more than 2.2× enlargement is
   refused), a clip before a photo (15 %), the source (10 %). A clip shorter than its slot, a ShareAlike or
   NonCommercial file, or one that needs a credit when none is allowed is never a candidate.
4. **Check.** The best 3–6 of every moment go to Gemini Flash-Lite as small thumbnails with the line that is said:
   0–10 each, text / watermark / logo / screenshot / collage / blur = 0–2 = vetoed. Final = 55 % check + 45 % score.
5. **Copy.** Each chosen file is copied to our storage (24 hours), the next candidate if a download fails.

The answer: `edit` (your body with the shots merged in — stock under the stickers, a whoosh 0.25 s before every
cutaway, credit lines if any), `overlays` on their own, `shots` (what was said, the search, the file, the licence, the
score and the check), `summary`, `picker` (the model's moments) and `warnings`. One call: cutaways ≈ 10 s and 2–3 credits, a faceless minute
≈ 20–30 s and 6–7 credits — the price is the Gemini calls' own cost; the searches are free.

The brief the model gets for the moments (for reference — the endpoint sends it itself):

```text
You choose B-roll for a short vertical talking-head video: stock footage that cuts in over the speaker for about 2.6 seconds while he keeps talking. You get the transcript as numbered words (number:word@seconds). Return JSON only:
{"cutaways":[{"word": n, "query": "…", "alt": "…", "short": "…", "tone": "neutral", "mode": "full"}]}
RULES
- 6 cutaways, the best first (the first 3 that fit the timing and find good footage are used), each on a CONCRETE thing the line names or implies that a stock clip can show — an object, a place, an action, a situation. Never on an opinion, a greeting, a name, a price or the call to action.
- word: the number of the word where the picture appears (the start of the phrase that names it). Not in the first 2.5 s (the hook is the speaker's face), not in the last 1.5 s, at least 4 s apart.
- prefer what a camera films — people, hands, objects, places, actions — over screens and interfaces (what a screen shows is rarely in stock and never readable).
- query: ENGLISH, 2–5 words, a generic filmable stock scene — a subject plus an action or setting ("woman scrolling phone feed", "video editing timeline on laptop", "man relaxing in hammock"). Stock libraries have no brands, apps, logos, company or people's names, screenshots or text: turn such a line into the scene it describes ("uploads to Instagram automatically" → "phone posting to social media"; "an AI agent" → "robot assistant working on laptop").
- alt: a second, broader query for the same moment, with different words.
- short: 1–3 comma-separated alternatives, each the main VISIBLE OBJECT or place in 1–2 English NOUNS — never a verb, an idea, or just "person" / "man" / "woman" ("smartphone, phone in hand", "laptop", "hammock", "robot", "office desk") — they are searched in photo titles, word for word.
- tone: "negative" when the line is about something bad — failure, a scam or fraud, loss, debt, fear, pain, illness, crime, anger, sadness, a mistake; else "neutral" or "positive". A negative line is illustrated with objects, places or hands, never a person's face (real people in stock footage must not be shown in a bad light).
- mode: "split" when the speaker's face should stay (the picture takes the lower half), else "full".
```

## Where the footage comes from, and the licences

**The licence rule (since 28.9.2026).** What the plan inserts on its own is **CC0 only** by default — photos and video on
Wikimedia Commons marked CC0 and Openverse CC0 — and a shot goes in only with its licence record (the source, the file
at the source, its page, the licence URL), which comes back in `output.shots` (`license`, `license_name`,
`license_url`, `source_url`, `page_url`). CC0 means no credit and no on-video marking, commercial use allowed.
Pexels and Pixabay (below) are used only where the server's rule allows more than CC0 (`output.summary.license_rule`
says which rule ran): the examples on this page were made on 27.9, before the rule, with Pexels.

**Photos and videos provided by [Pexels](https://www.pexels.com)** (when the server's rule allows them).

| Source | What | Licence | Needs |
|---|---|---|---|
| **Pexels** | stock video (portrait 1080p and more) and photos | Pexels licence — commercial use, edits allowed, no credit required | the server's `PEXELS_API_KEY` (set on dev) |
| **Pixabay** | stock video and photos | Pixabay content licence — commercial use, no credit | the server's `PIXABAY_API_KEY` (not set yet) |
| **Wikimedia Commons, stock** | ~37,000 photographs Commons hosts from Unsplash and Pixabay (their CC0 years) | CC0 — no credit | nothing |
| **Wikimedia Commons, CC0** | photos and ~10,500 video clips whose licence statement and file page say CC0 | CC0 — no credit | nothing (the default rule) |
| **Openverse** | openly licensed photos (StockSnap, rawpixel, Flickr…) | CC0 (the default rule) / public domain (CC BY with `attribution: "any"`) | nothing |
| **Wikimedia Commons, video** | CC0 / public-domain / CC BY clips (documentary more than B-roll) | as marked per file | only with `kind: "video"` and no Pexels / Pixabay key |

**Pexels and Pixabay are used only inside the edit.** Under their API terms the plan copies the chosen files for this
render (they expire with it after 24 hours) and never lists them as a library: `GET /v1/stock/search` does not return
them; `output.shots` keeps each shot's licence record (its page, the photographer, the file the copy was made from) —
render the copy (`url`). Where you publish the video or list its
credits, show `output.provided_by` — "Photos and videos provided by Pexels" with the link https://www.pexels.com — and,
where you can, the photographers in `output.photographers` ("Video by Ann on Pexels").

**People in stock footage.** A line about something bad — a scam, failure, loss, debt, fear, illness — is never
illustrated with a recognisable person (hands, objects and places instead): real people must not be shown in a bad
light. In an advert or a sales video send `context: "ad"`: then no stock person and no brand appear at all, so nobody in
the footage seems to endorse the product. The model marks each moment's tone, the plan refuses pictures whose own words
name people for those moments, and the thumbnail check scores any recognisable person there 0.

Never used: NonCommercial, NoDerivatives or ShareAlike material (a ShareAlike clip would make the customer's whole
video ShareAlike), meme GIFs (GIPHY's terms forbid commercial use; Tenor's API is closed) and Coverr (its API is free
only for non-commercial use). Every shot comes back in `output.shots` with its `license`, `attribution`, `credit`,
`author` and `page_url` — keep them with the video. With `attribution: "any"` a CC BY shot is allowed and the planner
puts its credit line on screen while it shows.

**Without a Pexels or Pixabay key** the stock is photographs only (clips need one of them): still real, still free, but a
photo holds still for its 2–3 seconds. `output.summary.stock_video_sources` says what the server has.

## What it costs — and the same video with Veo

The example: the owner's car video (Hebrew, 62 s → 57.3 s), made with the defaults above on 27.9.2026 — three full-frame
cutaways from Pexels (two video clips and a photo; photos and videos provided by Pexels), −15.8 LUFS. Real provider cost
next to the credits (`GET /v1/usage?tag=…`):

| Step | Credits | Time |
|---|---|---|
| Upload | 1 | 2 s |
| Transcribe the original (Universal-2, 62 s) | 1 | 15 s |
| Pauses cut, sound only (WAV) | 9 | 8 s |
| Transcribe the cut (Universal-2) | 1 | 7 s |
| Kinetic plan ×2 | 2 | 1–2 s |
| Gemini 3.1 Flash-Lite picks (watching the video) | 1 | 4 s |
| 4 sound effects + 6 emoji stickers | 4 | 2 s (parallel) |
| Music, 59 s | 45 | 12 s |
| **Stock plan** — 2 Pexels clips + 1 Pexels photo: Flash-Lite moments + thumbnail check, files copied | 3 | 11 s |
| Render (720p, 57.3 s) | 44 | 2.7 min |
| **Total** | **111** | |

The same edit with CC0 photographs only (before the server had a Pexels key) cost 110 credits: clips cost no more than
photos — the plan is a credit or two either way. Three generated 4-second clips instead (Veo 3.1 Fast, 720p, 120 credits a clip; Veo 3.1 Lite is half that and
weaker) would add **360 credits** to the same edit — ≈ 469 credits
for the video; the stock plan costs 3. A generated clip wins when the video needs something that does not
exist on camera (a product that is not made yet, a fantasy scene); for things that exist — a phone, a desk, a city,
hands on a keyboard — stock is the same picture at ~1 % of the price.

## Tune it

`context: "ad"` (an advert: no stock people, no brands) · `count` 1–8 · `seconds` 1.2–6 · `kind: "video"` (clips only) / `"image"` · `mode` · `split_side` · `speaker_focus` ·
`attribution: "any"` (CC BY allowed, credited on screen) · `verify: false` (no thumbnail check) · `topic` · `avoid` spans ·
everything the kinetic plan takes (`seed`, `face`, `speed`, `accent`, `resolution`).

The kinetic picker brief (step 3), for completeness:

```text
You pick the moments for a fast CapCut-style kinetic-typography edit of a vertical talking-head video. Every spoken word will appear on screen as it is said; you choose which words get the big type, where the emoji stickers go and which line becomes a full black title card. You WATCH and HEAR the attached video and get its transcript as numbered words (number:word). Return JSON only:
{
 "corrections": [ { "word": n, "text": "the word actually said" } ],
 "emphasis": [ n, … ],
 "scenes": [ { "word": n } ],
 "stickers": [ { "word": n, "icon": "…" } ],
 "music": "…"
}
RULES
- corrections: only a word that is clearly wrong — you hear something else, or it cannot be right in its sentence (e.g. the verb's person does not match its subject: "אני … יתמיד" → "אתמיד"). Never "fix" the speaker's own style, slang or prefixes (colloquial forms like "בלהתמיד", "בלמכור" are what he says). The text is what is said there, in the video's language; "" removes a word that was not said (one word heard as two: "וידאו עם" → "וידאוים", ""). When unsure, leave it.
- emphasis: the words that carry the meaning — a strong noun or verb, a number, a contrast, an emotion — about one in every four words, never two neighbours, never small function words.
- scenes: 1 for a video under 40 s, else 2: the thesis or the punchline the video builds to (a short line, 2–6 words) — never in the first 5 words, never the last 3 words.
- stickers: 5–8 concrete words a small emoji can illustrate (money, pain, a goal, laziness, time…), spread over the video — at least 10 words apart, not in the first 6 words, not inside a scene line. icon: one of ["fire","rocket","money-bag","dollar-banknote","chart-increasing","chart-decreasing","brain","flexed-biceps","bullseye","light-bulb","hourglass-not-done","alarm-clock","stopwatch","warning","cross-mark","check-mark-button","red-heart","broken-heart","eyes","thinking-face","face-screaming-in-fear","face-with-steam-from-nose","face-with-rolling-eyes","pensive-face","crying-face","sleeping-face","party-popper","trophy","1st-place-medal","gem-stone","key","locked","puzzle-piece","magnifying-glass-tilted-left","megaphone","mobile-phone","laptop","person-running","person-lifting-weights","handshake","hundred-points","sparkles","high-voltage","collision","skull","roasted-sweet-potato","couch-and-lamp","bed","hamburger","shopping-cart","gear","mountain","snail","turtle"].
- music: one sentence in English for an instrumental bed under the voice that fits the mood: genre, tempo in BPM, 3–4 instruments, energy; a clear beat (drums, 808 or claps) and a simple hook, never pads-only; "no vocals, stays under the voice".
```

Send `x-wait-ms: 110000` to wait for the render in the same call; if the response is still
`processing`, poll `GET /v1/executions/{id}` until `status` is `completed` and read `output.url`.

## Check the bill

`GET /v1/usage?tag=<job name>` — per call, per operation and the total, in credits. Quote that to the
customer, not the estimate above.

## Rules that keep captions readable on every platform

- **Safe zone.** Keep text inside the middle 80% of the frame: `side_margin` ≥ 6 handles the sides; `margin`
  ≥ 12 keeps the bottom clear of the platform UI (username, buttons, progress bar).
- **A line never leaves the frame** — a line too wide for the side margins is set smaller automatically. Do
  not shrink the size yourself, wrap instead (`max_chars_per_line`).
- **Hebrew has no capitals** — keep `uppercase: false`; it only breaks Latin words mixed into a sentence.
- **Fonts.** `GET /v1/studio/fonts` lists the bundled families and which scripts they draw. A font that
  cannot draw the caption's language is swapped and reported in `warnings`.
- The customer's transcript wins: to fix a word, `GET /v1/speech/transcripts/{id}/captions`, edit the text,
  and send the list back as `captions.captions` — timing is kept when the word count is unchanged.
