Documentation

vaaya / docs / reference

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

NeedModel / callPricePick when
Website hero, banner, people shots, marketing imagenano-banana-pro33¢The default — most photoreal model, best humans, up to 4K.
Readable text inside the imagegpt-image-224¢The only model that reliably renders readable text.
Photoreal still, especially one you’ll animateseedream--v5-pro--text-to-image18¢Highest human/scene realism; the frame you feed MiniMax H3.
Bulk stills / iterationseedream--v4-5--text-to-imageWhen v4.5 quality already nails the brief.
Cut a subject out of an imageimage-background-removalTransparent PNG for compositing.
Any scene with characters or dialogueminimax-h3--reference-to-video≈34¢/sThe scene default — reference images + a shot script → one clip with dialogue and audio.
Animate one still / single subjectminimax-h3--image-to-video≈34¢/sSeedream still → motion, with full control of the frame.
B-roll, product motion, abstract visualsseedance-2-0--*134–677¢Best general (non-scene) video family.
Cheapest text-to-videokling-video--v3--pro--text-to-video185¢Budget alternative.
Music / soundtrack bedminimax-music--v2-615¢Instrumental only; the assembler loops and trims it.
Narration / voiceoverelevenlabs--tts--turbo-v2-55¢ per 1,000 charsStrict TTS with preset voices — see the roster below.
Assemble clips into a finished videocueframe/*$1 renderThe only assembler: upload → project → validate → composition → render.
A product demo, end to endvaaya/produce_autodemofree + sub-stepsOne 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 result instead.
  • Save the sync output URL immediately. Re-running the call to “recover” a lost URL is a new paid generation; instead pass the transaction_id the sync call returned to result, which replays the stored output.
  • gpt-image-2 is 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
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 at 4K in 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_violation is 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-imageminimax-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 / platformTargetGenerate with
OpenGraph / Twitter card1200×630Nano 16:9 @ 4K, crop to 1.9:1
Instagram/FB feed (square)1080×1080Nano 1:1 @ 4K; GPT 1024x1024
IG/FB portrait ad1080×1350Nano 3:4 @ 4K, crop to 4:5; GPT 1024x1536
Stories / Reels / Shorts / TikTok1080×1920Nano 9:16 @ 4K
X / YouTube thumb / landscape ad1920×1080Nano 16:9 @ 4K
LinkedIn shared image1200×627Nano 16:9 @ 4K, crop to 1.91:1
Google Display 300×250300×250Nano 4:3 @ 2K, crop to 6:5, downscale
Extreme banners (728×90, 160×600)>2:1 ratiosClosest 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 keyPriceWhat 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 @ 2KAnimate one still or subject — Seedream frame → motion.
minimax-h3--text-to-video≈34¢/s @ 2KBare prompt — vague asks only (no characters, no dialogue).
seedance-2-0--fast--reference-to-video134¢Fast from reference.
seedance-2-0--fast--image-to-video135¢Fast from image — cheapest image-to-video.
seedance-2-0--image-to-video336¢Standard from image.
seedance-2-0--fast--text-to-video400¢Fast text-to-video.
seedance-2-0--text-to-video500¢Standard text-to-video.
seedance-2-0--reference-to-video677¢Standard from reference.
kling-video--v3--pro--text-to-video185¢Cheapest text-to-video.
kling-video--v3--pro--image-to-video185¢Kling v3 Pro from image.
kling-video--v3--standard--text-to-video208¢Kling v3 Standard.
kling-video--v3--standard--image-to-video208¢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 · resolution pinned 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 as reference_image_urls in 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/upload the still, then animate it: image_url is the first frame (the output aspect ratio follows it), end_image_url optionally pins the last frame for a first→last move, and prompt describes 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 --fast variants support 480p and 720p only (default 720p) — requesting 1080p on a fast model errors. Standard variants add 1080p.
  • 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 keyPriceNotes
seedance-2-0--fast--image-to-video135¢Talking-head loop — set image_url = end_image_url = the avatar frame, generate_audio: true.
sync-lipsync--v2500¢Lip-sync a video to an audio track. Async; pass max_cost_cents: 550.
video-background-removal20¢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.

VoiceGenderAgeCharacter
Liammaleyoung adultconfident, articulate narration (default)
Rachelfemaleadultcalm, clear, narration
Ariafemaleadultexpressive, warm, engaging
Sarahfemaleyoung adultsoft, professional, news-read
Laurafemaleyoung adultupbeat, bright, friendly
Charlottefemaleadultsmooth, confident, polished
Alicefemaleadultwarm, British, pleasant
Matildafemaleadultwarm, trustworthy narration
Lilyfemaleyoung adultgentle, even, professional
Jessicafemaleyoung adultlively, expressive, playful
Brianmaleadultdeep, resonant, trustworthy
Georgemalematurewarm, British, mellow narration
Willmaleyoung adultchill, friendly, conversational
Ericmaleadultsmooth, classy, even
Chrismaleadultcasual, natural, everyday
Danielmaleadultauthoritative, news-anchor
Billmalematurewarm, grandfatherly narration
Rogermaleadulteasy-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 kindvideo | 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/validate before put_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 a fix hint) until valid: true.
  • In reframe.segments, zoom is 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.
  • ease is an object { in, out } (ramp seconds), not a string. focus is { 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 valid primitiveIds 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-run render to check on a job (that’s a new paid render); poll result with the job_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, and intent (preview | final, default final).
  • 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_cents is a safety ceiling against runaway spend, not an optimization target — set it high enough for the correct pipeline to complete (see Making calls).