Documentation
Media generation
Every generation model — images, video, music, voice — sits behind one action: fal/generate with the model name as a param. This page is the routing guide: which model for which job, what each costs, and the sync/async and quality gotchas that decide whether a render lands. For assembling clips into a finished video, see CueFrame below.
One action, every model
All generation goes through POST /api/run/fal/generate with model set to a key from the tables below plus that model’s params (prompt, image_url, aspect_ratio, duration, text, …). Pricing is per model, charged only on success.
curl -X POST https://vaaya.ai/api/run/fal/generate \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana-pro",
"prompt": "candid phone-camera photo of a founder at a standing desk, warm morning light, shallow depth of field",
"aspect_ratio": "16:9",
"resolution": "4K"
}'When to use which
| Need | Model / call | Price | Pick when |
|---|---|---|---|
| Website hero, banner, people shots, marketing image | nano-banana-pro | 33¢ | The default — most photoreal model, best humans, up to 4K. |
| Readable text inside the image | gpt-image-2 | 24¢ | The only model that reliably renders readable text. |
| Photoreal still, especially one you’ll animate | seedream--v5-pro--text-to-image | 18¢ | Highest human/scene realism; the frame you feed MiniMax H3. |
| Bulk stills / iteration | seedream--v4-5--text-to-image | 4¢ | When v4.5 quality already nails the brief. |
| Cut a subject out of an image | image-background-removal | 5¢ | Transparent PNG for compositing. |
| Any scene with characters or dialogue | minimax-h3--reference-to-video | ≈34¢/s | The scene default — reference images + a shot script → one clip with dialogue and audio. |
| Animate one still / single subject | minimax-h3--image-to-video | ≈34¢/s | Seedream still → motion, with full control of the frame. |
| B-roll, product motion, abstract visuals | seedance-2-0--* | 134–677¢ | Best general (non-scene) video family. |
| Cheapest text-to-video | kling-video--v3--pro--text-to-video | 185¢ | Budget alternative. |
| Music / soundtrack bed | minimax-music--v2-6 | 15¢ | Instrumental only; the assembler loops and trims it. |
| Narration / voiceover | elevenlabs--tts--turbo-v2-5 | 5¢ per 1,000 chars | Strict TTS with preset voices — see the roster below. |
| Assemble clips into a finished video | cueframe/* | $1 render | The only assembler: upload → project → validate → composition → render. |
| A product demo, end to end | vaaya/produce_autodemo | free + sub-steps | One call from raw screen recording to rendered demo. |
Sync vs async
Images and audio are synchronous — the response carries the output file URL inline. Video, lipsync, video background-removal, and CueFrame renders are asynchronous — the call returns { job_id, async: true } immediately; fetch the output with the result tool (pass the job_id) over MCP. Polling is free, safe to repeat, and the job is charged only when it reaches succeeded — a failed job charges nothing.
- Never re-submit a generation to “check on it” — that starts a new paid render. Poll
resultinstead. - Save the sync output URL immediately. Re-running the call to “recover” a lost URL is a new paid generation; instead pass the
transaction_idthe sync call returned toresult, which replays the stored output. gpt-image-2is slow and can time out even as a single call — run it one at a time, never batch slow sync jobs in parallel.- Firing several async renders concurrently is fine — each returns its own
job_id.
Staging inputs — fal/upload
- Price
- 1¢
- Returns
{ upload_url, file_url }
Any media that feeds a generation — a reference photo, a video for lipsync, an audio track — must stay reachable while the job runs. Short-lived presigned links can expire before an async job leaves the queue, so stage inputs on the generation CDN first: call fal/upload, PUT the raw bytes to upload_url, then pass file_url as the model’s image_url / image_urls / video_url / audio_url.
curl -X POST https://vaaya.ai/api/run/fal/upload \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"file_name": "headshot.png", "content_type": "image/png"}'
# then PUT the raw bytes
curl -X PUT --upload-file headshot.png \
-H "Content-Type: image/png" "<upload_url>"- Never compress, downscale, or re-encode an input before uploading. Compression visibly degrades outputs and causes errors in edit/avatar/lipsync flows; pricing does not scale with input size, so full resolution costs nothing extra.
- Outputs of earlier generations are already on the CDN — pass those URLs straight through, no re-upload.
- For durable cross-task storage use Files; stage to the CDN from there when a generation needs the file.
Images
Nano Banana Pro — the workhorse
- Price
- 33¢ (
nano-banana-pro·nano-banana-pro--edit) - Latency
- sync
- Output
- up to 4K
The default for essentially every website and marketing image: the most realistic humans in the catalog (candid, phone-camera, UGC feel) plus rich detailed scenes and abstract brand visuals. First choice for a character-consistent series — pass a reference image_url to lock identity and style across a set. It rewards detailed prompts (lens, lighting, texture, mood) with depth.
- Size with
aspect_ratio(1:1,16:9,4:3,3:4,9:16, …) +resolution(1K,2K,4K) — it does not take exact pixel dimensions. Generate at4Kin the closest ratio, then crop/downscale to the exact target where the image is used. Never upscale. - Extreme ratios (728×90, 160×600 banners) can’t be generated directly — generate the closest ratio and crop, or build the graphic as SVG/HTML and screenshot it.
- A
content_policy_violationis a server-side check that charges nothing — reword the flagged phrase and retry.
GPT Image 2 — text in images
- Price
- 24¢ (
gpt-image-2·gpt-image-2--edit) - Latency
- sync but slow — run one at a time
The only model that reliably renders readable text inside images: flowcharts, infographics, diagrams, labels, slide graphics. Sizes are fixed — pass image_size as { "width": 1024, "height": 1024 } (landscape 1536×1024, portrait 1024×1536) or omit it; a "1024x1024" string is auto-coerced to the object, so either form is safe.
For a complex graphic that needs exact accuracy — a precise diagram, a wordmark, a real data viz — don’t fight an image model: generate an SVG and screenshot it for pixel-perfect type at no model cost.
Seedream — photoreal humans and scene stills
- Price
- 18¢ (v5 Pro) · 4¢ (v4.5)
- Latency
- sync
- Output
- up to 2K
ByteDance’s Seedream is the realism tier for photoreal humans and scenes, and its signature job is staging a scene as a still that MiniMax H3 then animates (seedream--v5-pro--text-to-image → minimax-h3--image-to-video) — precise control over composition, casting, and lighting before adding motion. The --edit variants (seedream--v5-pro--edit, seedream--v4-5--edit) composite and edit while preserving realism, and take an image_urls array. The safety checker is off by default, which suits unrestricted scene work.
Prefer v5 Pro for the hero still that gets animated or shown full-size; drop to v4.5 (4¢) for bulk and iteration where its quality already suffices. For readable text, still reach for GPT Image 2.
Background removal
image-background-removal (5¢, param image_url) cuts a product, person, or logo onto a transparent PNG for clean placement — cut out a hero subject and drop it on a brand-color background for a punchy ad.
Platform sizes
The pattern is always: generate at the closest aspect ratio at max resolution, then crop and downscale to the exact target where the image is used.
| Asset / platform | Target | Generate with |
|---|---|---|
| OpenGraph / Twitter card | 1200×630 | Nano 16:9 @ 4K, crop to 1.9:1 |
| Instagram/FB feed (square) | 1080×1080 | Nano 1:1 @ 4K; GPT 1024x1024 |
| IG/FB portrait ad | 1080×1350 | Nano 3:4 @ 4K, crop to 4:5; GPT 1024x1536 |
| Stories / Reels / Shorts / TikTok | 1080×1920 | Nano 9:16 @ 4K |
| X / YouTube thumb / landscape ad | 1920×1080 | Nano 16:9 @ 4K |
| LinkedIn shared image | 1200×627 | Nano 16:9 @ 4K, crop to 1.91:1 |
| Google Display 300×250 | 300×250 | Nano 4:3 @ 2K, crop to 6:5, downscale |
| Extreme banners (728×90, 160×600) | >2:1 ratios | Closest ratio + crop, or SVG/HTML |
Video
Route on what the video actually contains, not the words of the ask: a real *scene* — characters, dialogue, a narrative moment, a skit, a parody — is a MiniMax H3 job; b-roll, product motion, and abstract brand visuals are Seedance; reach for Kling only when Seedance’s price/variant mix doesn’t fit. Both families cap at 15 seconds per clip — for longer pieces, cut the script into segments and stitch in CueFrame.
| Model key | Price | What it is |
|---|---|---|
minimax-h3--reference-to-video | ≈34¢/s @ 2K (+11¢/ref beyond 5) | The scene default — characters, dialogue, multi-shot from reference images + a script. |
minimax-h3--image-to-video | ≈34¢/s @ 2K | Animate one still or subject — Seedream frame → motion. |
minimax-h3--text-to-video | ≈34¢/s @ 2K | Bare prompt — vague asks only (no characters, no dialogue). |
seedance-2-0--fast--reference-to-video | 134¢ | Fast from reference. |
seedance-2-0--fast--image-to-video | 135¢ | Fast from image — cheapest image-to-video. |
seedance-2-0--image-to-video | 336¢ | Standard from image. |
seedance-2-0--fast--text-to-video | 400¢ | Fast text-to-video. |
seedance-2-0--text-to-video | 500¢ | Standard text-to-video. |
seedance-2-0--reference-to-video | 677¢ | Standard from reference. |
kling-video--v3--pro--text-to-video | 185¢ | Cheapest text-to-video. |
kling-video--v3--pro--image-to-video | 185¢ | Kling v3 Pro from image. |
kling-video--v3--standard--text-to-video | 208¢ | Kling v3 Standard. |
kling-video--v3--standard--image-to-video | 208¢ | Kling v3 Standard from image. |
MiniMax H3 — real scenes, dialogue, no content gate
- Price
- ≈34¢ per second at 2K — scales with
duration - Latency
- async — poll
result - Max clip
- 15s ·
resolutionpinned to 2K
The tool for making an actual scene: multiple characters, dialogue, scene cuts, audio, a sitcom/movie/TV look — and no content gate, so real people, film/TV recreations, and edgy scenes work where Seedance and Kling refuse. Pricing is per second, so always pass an explicit duration (it defaults to a short 5s otherwise) and set max_cost_cents to cover the clip — 1521 covers the 15s max plus a large reference cast.
If you can name or describe the characters, or there is any dialogue, it is a reference-to-video job — never text-to-video, regardless of how the ask was phrased. The three modes:
- Reference-to-video — the scene default. Find a clean, front-facing, high-res image of every character the script names, stage each with
fal/upload(uncompressed), and pass them asreference_image_urlsin order. The likeness comes entirely from the references — skip one and the model invents that cast member. Sourcing real stills is a web search + scrape job. - Image-to-video — one subject, one framed shot. Compose the frame exactly with
seedream--v5-pro--text-to-image,fal/uploadthe still, then animate it:image_urlis the first frame (the output aspect ratio follows it),end_image_urloptionally pins the last frame for a first→last move, andpromptdescribes the motion. - Text-to-video — last resort, vague asks only (e.g. “abstract flowing-energy motion”). The moment there are characters or spoken lines, switch to reference-to-video — you give up all control over composition and casting here.
Write the reference-to-video prompt as a shot script: open with location + lighting + camera style, stage the characters by reference order (Image 1, Image 2, …), write the beats with DIALOGUE: lines and explicit cuts/zooms, and close with a STYLE: line.
curl -X POST https://vaaya.ai/api/run/fal/generate \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimax-h3--reference-to-video",
"reference_image_urls": ["<founder_url>", "<cofounder_url>"],
"duration": 12,
"max_cost_cents": 1521,
"prompt": "Startup office at night, single desk lamp, handheld documentary camera. Image 1 stares at a terminal; Image 2 leans in behind. DIALOGUE: IMAGE 1 (quiet): \"It works.\" Snap zoom to Image 2, eyes wide. IMAGE 2: \"Ship it.\" STYLE: single-camera mockumentary, natural performances, muted palette."
}'Seedance 2.0 and Kling — b-roll and generated motion
- The
--fastvariants support480pand720ponly (default 720p) — requesting1080pon a fast model errors. Standard variants add1080p. - When a clip is shown full-frame, use a standard variant at
1080p; reserve fast/720p for small tiles (PIP) where it won’t be seen at full size. - Seedance has no safety toggle — if a prompt is flagged, reword it. H3 has no gate at all and is the fallback when Seedance/Kling refuse.
Avatar & lipsync building blocks
Atomic pieces for a talking-head presenter — there is no turnkey avatar pipeline today, so treat these as building blocks you compose yourself (avatar frame via nano-banana-pro--edit, voiceover via TTS below, then):
| Model key | Price | Notes |
|---|---|---|
seedance-2-0--fast--image-to-video | 135¢ | Talking-head loop — set image_url = end_image_url = the avatar frame, generate_audio: true. |
sync-lipsync--v2 | 500¢ | Lip-sync a video to an audio track. Async; pass max_cost_cents: 550. |
video-background-removal | 20¢ | Alpha cutout — only for the full-frame cut-out presenter look (PIP/split layouts keep the background). Async; output_codec: "vp9". |
Music — minimax-music--v2-6
- Price
- 15¢
- Latency
- sync — faster than realtime
- Output
- instrumental only, lossless
One param: prompt — describe style, mood, genre, BPM. Output is pinned to instrumental (a background bed, never a song with vocals). There is no duration param: the track is a fixed length and the assembler auto-loops and trims it to the video, so generate it last and don’t try to match its length. For product demos and brand videos, uplifting with a driving beat works: "uplifting energetic electronic track, driving beat, modern tech-product feel, 120 BPM".
Speech & voices — elevenlabs--tts--turbo-v2-5
- Price
- 5¢ per 1,000 characters
- Latency
- sync — faster than realtime
- Default voice
Liam
Strict text-to-speech: text is the exact words to be spoken — no stage directions, no “narrator:”, no markdown. voice takes an ElevenLabs preset name from the roster below; the pace is pinned to a natural house speed, so there is no speed param to pass. The model is multilingual — pass language_code (ISO 639-1) for a non-English voiceover.
curl -X POST https://vaaya.ai/api/run/fal/generate \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "elevenlabs--tts--turbo-v2-5",
"text": "Meet the dashboard that does the busywork for you.",
"voice": "Liam"
}'Picking a voice
When the voice narrates over an on-screen presenter, match it to how the presenter looks — apparent gender, age band, and energy — so voice and face read as one person. For voiceover-only work, or when unsure, the defaults are Liam (male) / Rachel (female). The gender/age/character labels are the preset descriptions — a starting map; if a voice doesn’t fit on playback, swap within the same group.
| Voice | Gender | Age | Character |
|---|---|---|---|
Liam | male | young adult | confident, articulate narration (default) |
Rachel | female | adult | calm, clear, narration |
Aria | female | adult | expressive, warm, engaging |
Sarah | female | young adult | soft, professional, news-read |
Laura | female | young adult | upbeat, bright, friendly |
Charlotte | female | adult | smooth, confident, polished |
Alice | female | adult | warm, British, pleasant |
Matilda | female | adult | warm, trustworthy narration |
Lily | female | young adult | gentle, even, professional |
Jessica | female | young adult | lively, expressive, playful |
Brian | male | adult | deep, resonant, trustworthy |
George | male | mature | warm, British, mellow narration |
Will | male | young adult | chill, friendly, conversational |
Eric | male | adult | smooth, classy, even |
Chris | male | adult | casual, natural, everyday |
Daniel | male | adult | authoritative, news-anchor |
Bill | male | mature | warm, grandfatherly narration |
Roger | male | adult | easy-going, natural, relatable |
Assembling video — CueFrame
- Price
- 1¢ per step · $1 per render
- Latency
- steps sync · render async
CueFrame is the single video assembler — every finished video (auto-zoom demos, picture-in-picture presenter, b-roll cuts) is composed here, and it is the only combine step: never pre-combine assets with ffmpeg. Assets flow in two stages: store each one durably in Files (files/upload → PUT bytes → file_id), then cueframe/upload imports it from your stored copy — you never touch CueFrame’s own upload plumbing.
# 1) stage each asset (recording, voiceover, music) — file_id from files/upload
curl -X POST https://vaaya.ai/api/run/cueframe/upload \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"file_id": "file_..."}' # → { media_id }
# 2) create the project
curl -X POST https://vaaya.ai/api/run/cueframe/create_project \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "launch-demo", "format": {"aspectRatio": "16:9", "fps": 30, "resolution": "fhd"}}'
# 3) validate the composition (always — BEFORE writing it)
curl -X POST https://vaaya.ai/api/run/cueframe/validate \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"v": 1, "format": {...}, "tracks": [...]}'
# 4) write it, 5) render (async — returns a job_id)
curl -X POST https://vaaya.ai/api/run/cueframe/put_composition \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": "...", "v": 1, "format": {...}, "tracks": [...]}'
curl -X POST https://vaaya.ai/api/run/cueframe/render \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": "...", "intent": "preview"}'The composition
One JSON document describes the whole video: { v: 1, format: { aspectRatio, fps, resolution }, tracks: [...] }, tracks layered bottom→top. A track is { id, kind, contents: [clips] } with kind ∈ video | audio | image | overlay | effect; a clip is { id, startTime, duration, source, ... } in seconds. The workhorse source is { kind: "media", mediaId, trim: { start, end }, ... } — mediaId is what cueframe/upload returned — with knobs for volume, fades, loop, kenBurns, excludedRanges (cut dead air out of a recording without re-uploading), and reframe (the auto-zoom engine). Captions, click markers, and badges go on an overlay track as overlay primitives.
Gotchas
- Always
cueframe/validatebeforeput_composition+render. CueFrame silently drops clips that fail validation, so an invalid edit renders as"Composition has no scenes"and burns a paid render. Fix every error (each carries afixhint) untilvalid: true. - In
reframe.segments,zoomis the visible-frame fraction:1.0= full frame, smaller = tighter (~0.8 subtle push-in, ~0.5 tight, ~0.3 very tight). Never exceed 1.0 — an out-of-range zoom silently drops the whole clip. easeis an object{ in, out }(ramp seconds), not a string.focusis{ mode: "point", x, y }(normalized 0–1 — the click coordinate is the demo workhorse),{ mode: "frame-center" }to pull back between beats, or the face modes for talking heads.- Overlay clips use
{ kind: "overlay", primitiveId, params }— look up validprimitiveIds from CueFrame’s overlay registry; don’t guess. - Render
intent: "preview"first — a fast low-res draft to review the cut — then"final"for the full-resolution deliverable. Never ship a preview, and never re-runrenderto check on a job (that’s a new paid render); pollresultwith thejob_id.
Turnkey product demos — vaaya/produce_autodemo
- Price
- free to call — the internal steps bill (1¢ each + $1 render)
- Latency
- async — minutes; poll
result
One call from a raw screen recording to a finished demo video. Record your product (30–160s, silent, logging click times if you can), store the recording with files/upload, then call vaaya/produce_autodemo. Vaaya watches the recording, authors the script and edit, generates the voiceover, outro, and music, and renders — you make no fal/* or cueframe/* calls yourself. The call returns a render job_id; keep polling result until it returns the final video URL.
curl -X POST https://vaaya.ai/api/run/vaaya/produce_autodemo \
-H "Authorization: Bearer $VAAYA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recording": "file_...",
"feature": {
"whatItDoes": "One-click rollback for failed deploys",
"builderIntent": "show how fast recovery is",
"company": "Acme Deploys",
"useCases": ["incident response", "release management"]
},
"clicks": [{"t": 4.2, "x": 0.62, "y": 0.31}],
"voice": "Liam",
"intent": "final"
}'clicks([{ t, x, y }], normalized 0–1) drives the auto-zoom — pass them if your recorder captured click positions.- Optional:
recordingDurationSec,targetDurationSec,name, andintent(preview|final, defaultfinal). - The recording must be the real product — the pipeline understands what’s on screen and narrates it, so generative b-roll is no substitute for actual capture.
Quality rules
These hold across every pipeline above — the deliverable should never be traded down for bandwidth, file size, or speed:
- Upload originals, uncompressed — a downscaled input wrecks every downstream step and saves nothing.
- Generate images at the highest resolution the model offers (Nano Banana Pro at
4K); crop/downscale only where the image is placed, never at generation time — and never upscale. - Full-frame video clips: standard variant at
1080p, not a fast 720p variant. - Render
intent: "final"for anything you ship;"preview"is a review draft only. max_cost_centsis a safety ceiling against runaway spend, not an optimization target — set it high enough for the correct pipeline to complete (see Making calls).