# Revid API — Lyrics to music video (`lyrics-to-video`)

> Revid generates an original song from your lyrics (or writes the lyrics from a description), then a music video synced to it.

Endpoint: `POST https://www.revid.ai/api/public/v3/render` — auth header `key: YOUR_API_KEY` (get a key at https://www.revid.ai/account).
Cost: 10 credits per call + generation costs. Estimate first with `POST https://www.revid.ai/api/public/v3/calculate-credits` (same body, no auth).
Accepted workflow aliases: `text-to-music-video`, `song-to-video`.

## Required input

- source.text (the lyrics) or source.prompt (a description of the song)

## Defaults for this workflow

- media.type: `ai-video`
- AI voice-over: disabled by default
- Background music: enabled by default
- Captions: enabled by default (preset `Wrap 1`, position `bottom`)
- Aspect ratio: `9:16` (portrait) by default

## Minimal request

```bash
curl -X POST 'https://www.revid.ai/api/public/v3/render' \
  -H 'Content-Type: application/json' \
  -H 'key: YOUR_API_KEY' \
  -H 'User-Agent: my-app/1.0' \
  -d '{"workflow":"lyrics-to-video","source":{"prompt":"An upbeat pop song about shipping a side project at 3am"}}'
```

## Full example

```json
{
  "webhookUrl": "https://your-server.com/revid/webhook",
  "workflow": "lyrics-to-video",
  "source": {
    "text": "[verse]\nCoffee's cold, the cursor blinks\nOne more push before the city sleeps\n[chorus]\nShip it tonight, ship it tonight"
  },
  "media": {
    "type": "ai-video",
    "videoModel": "pro",
    "mediaPreset": "DEFAULT"
  },
  "music": {
    "generationMusicPrompt": "Upbeat synth pop, female vocal",
    "syncWith": "lyrics",
    "soundWave": false,
    "enableLyricsLipSync": true,
    "lipSyncFrequency": 0.3
  },
  "captions": {
    "enabled": true,
    "preset": "Sing-Along",
    "position": "bottom"
  },
  "aspectRatio": "9:16"
}
```

## Parameters for this workflow

Top-level fields:

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `workflow` | string | **Yes** |  | `script-to-video` / `prompt-to-video` / `audio-to-video` / `music-to-video` / `lyrics-to-video` / `song-ad` / `article-to-video` / `avatar-to-video` / `static-background-video` / `motion-transfer` / `character-swap` / `caption-video` / `video-to-clips` / `ad-generator` | The generation workflow. Decides which source input is required and every default below. |
| `webhookUrl` | string | No |  |  | Public URL called when the render finishes (success or failure). Strongly recommended over polling. Accepted aliases: webhook. |
| `projectId` | string | No |  |  | Reuse an existing project instead of creating a new one. The project must belong to the API key's workspace. Accepted aliases: pid. |
| `aspectRatio` | string | No | `"9:16"` | `9:16` / `16:9` / `1:1` / `4:5` / `auto` | Output aspect ratio. Accepted aliases: ratio. |
| `metadata` | object\|null | No |  |  | Free-form object stored with the project and echoed in webhooks. For URL sources, metadata.duration (seconds) speeds up processing. |
| `characterIds` | string[] | No |  |  | IDs of saved consistent characters to feature in the video. Get IDs from GET /api/public/v3/consistent-characters. Selecting characters can force media.imageModel to ultra when the preset requires it. Accepted aliases: selectedCharacters, options.characterIds, options.selectedCharacters. |

### `source` (required for this workflow)

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `source.text` | string | script-to-video, avatar-to-video, static-background-video |  |  | The exact script to be spoken. For script-to-video it can also be an X/Twitter or LinkedIn post URL: the post content becomes the script. For lyrics-to-video and song-ad, the lyrics, sung as written ([verse], [chorus] sections supported). Workflows: script-to-video, avatar-to-video, static-background-video, lyrics-to-video, song-ad. Accepted aliases: inputText, input. |
| `source.prompt` | string | prompt-to-video; ad-generator (unless media.provided[] is set) |  |  | What the video should be about: the AI writes the script. For music-to-video it is an optional creative brief for the visual storyline. For lyrics-to-video, a description of the song to write (10 to 500 characters). For song-ad, the ad to write: the product or its page, the audience and the outcome. For character-swap, optional instructions on top of the swap (an outfit, ...). Workflows: prompt-to-video, ad-generator, music-to-video, lyrics-to-video, song-ad, character-swap. Accepted aliases: prompt, input. |
| `source.websiteToRecord` | string | No |  |  | URL of a website to screen-record as footage for the video. Accepted aliases: websiteToRecord. |
| `source.quizzData` | object | No |  |  | Quiz definition for quiz videos: { title, questions: [{ id, question, answers: [{ id, answer, isCorrect }] }] }. Accepted aliases: options.quizzData, quizzData. |

### `media` (optional)

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `media.type` | string | No | moving-image for most workflows; ai-video for prompt-to-video and audio-to-video | `moving-image` / `ai-video` / `motion-graphics` / `stock-video` / `custom` | The visual engine for the video. Default: moving-image for most workflows; ai-video for prompt-to-video and audio-to-video. |
| `media.quality` | string | No | `"pro"` | `standard` / `pro` / `ultra` | Convenience profile that picks imageModel + videoModel for you. Explicit media.imageModel / media.videoModel override it. |
| `media.imageModel` | string | No |  | `cheap` / `good` / `ultra` | Image generation model. Auto-adjusted when needed: ai-video forces ultra, and presets/characters can upgrade it. Accepted aliases: imageGenerationModel. |
| `media.videoModel` | string | No |  | `base` / `pro` / `ultra` / `veo3` / `sora2` / `seedance-25-1080p` / `seedance-1080p` / `seedance-4k` / `minimax-h3-768p` / `minimax-h3-2k` / `wan-3-1080p` | Video generation model used when video clips are generated. The Ultra variants (seedance-1080p, seedance-4k, ...) pick the model and resolution of the ultra tier, like the create form's Ultra menu. media.type motion-graphics has three director tiers, the same as the create form, base directed by Sonnet 5.5 (2 credits/s), pro by Opus 5.5 (3 credits/s, default), ultra by Opus 5.5 at maximum effort (5 credits/s); each has a 15s minimum per generation, and veo3/sora2/the Ultra variants are automatically mapped to pro. character-swap takes base (Wan 3.0, default), pro (Seedance 2.5) or ultra (MiniMax Recast, swaps public figures too), billed per second of video. Accepted aliases: videoGenerationModel. |
| `media.density` | string | No |  | `low` / `medium` / `high` | How many distinct visuals per minute of video. More visuals = livelier video and higher generation cost. Accepted aliases: mediaMultiplier. |
| `media.animation` | string | No | `"soft"` | `none` / `soft` / `dynamic` / `depth` / `overlay` / `parallax` / `multiplane` / `cinemagraph` / `dust` / `hook` / `eco` | How still images are animated. Ignored for other media types. Only applies when media.type = moving-image. Accepted aliases: typeMovingImageAnim. |
| `media.mediaPreset` | string | No | `"DEFAULT"` |  | Visual style preset slug (e.g. DEFAULT, ANIME, PIXAR, GHIBLI, REALISM, ...). See the preset list in the docs. A preset can override media.imageModel when it requires a specific model. With media.type motion-graphics it is the motion graphics category instead, the create form's category cards: MOTION_INSPIRATION_AUTO (Auto, default: Let the agent choose the best category and reference for your brief), MOTION_INSPIRATION_CLEAN_WORDMARK (Minimal & type: Spacious layouts, expressive typography and focused graphic reveals), MOTION_INSPIRATION_PRODUCT_DEMOS (Product demos: Interface walkthroughs, cursor actions and clear feature demonstrations), MOTION_INSPIRATION_DARK_SAAS (Immersive SaaS: Layered interfaces, dramatic depth and polished feature launches), MOTION_INSPIRATION_WARM_LAUNCH (Mixed media: Photography, cutouts and textures woven into animated graphics), MOTION_INSPIRATION_FOOTAGE_LED (Footage-led: People, places and product footage with supporting motion graphics), MOTION_INSPIRATION_MASCOT (Mascot: A character mascot that acts and talks over clean product UI), MOTION_INSPIRATION_EDITORIAL (Editorial: Paper collage, archive photos, highlighted documents and maps, like a news explainer), MOTION_INSPIRATION_DIAGRAM (Diagram: Step-by-step schematics and labelled parts that build an idea), MOTION_INSPIRATION_ILLUSTRATED (Illustrated: Flat illustrated scenes and characters that turn each idea into a picture), MOTION_INSPIRATION_DATA_STORY (Data story: Charts, counters, rankings and maps that make numbers mean something), MOTION_INSPIRATION_MAPS (Maps: Animated maps: borders, routes and zooms from the world down to a city). Values match case-insensitively, with or without the MOTION_INSPIRATION_ prefix, or by label ("footage-led", "Minimal & type"); no value, DEFAULT or an image preset sent with motion-graphics means Auto, like the create form. Only applies when media.type = moving-image, ai-video or motion-graphics. Accepted aliases: media.generationPreset, options.generationPreset, generationPreset. |
| `media.maxItems` | integer | No |  | min 1 | Hard cap on the number of generated/selected media items. Accepted aliases: maxNbMedias. |
| `media.provided` | array | media.type = custom (>= 1 item); motion-transfer (target image) |  |  | Your own media items. Used as the only visuals when media.type = custom or media.useOnlyProvided = true; otherwise used as references/anchors. Items: { url (required), type, title, ... }. Accepted aliases: inputMedias. |
| `media.useOnlyProvided` | boolean | No | `false` |  | Use only media.provided[] items; disable AI media generation/search. Default: true for motion-transfer and ad-generator. Accepted aliases: options.useOnlyProvidedMedia, useOnlyProvidedMedia. |
| `media.useProvidedInOrder` | boolean | No | `false` |  | Map media.provided[] items to script sections in order (no AI picking). Forces useOnlyProvided. Fewer media than sections: adjacent sections are grouped; extra media are ignored. Requires at least one image/video item. Only applies when media.type = custom. |
| `media.mergeVideos` | boolean | No | `false` |  | Merge multiple provided videos into one sequence. |
| `media.mergeVideosFull` | boolean | No | `false` |  | Merge provided videos keeping their full length. |
| `media.addAudioToVideos` | boolean | No | true for prompt-to-video with media.type = ai-video, false otherwise |  | Generate ambient audio/sound for AI video clips. Default: true for prompt-to-video with media.type = ai-video, false otherwise. |
| `media.turnImagesIntoVideos` | boolean | No | `false` |  | Animate provided still images into video clips. |
| `media.applyStyleTransfer` | boolean | No | `false` |  | Re-style provided media to match the selected preset. |

### `captions` (optional, enabled by default)

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `captions.enabled` | boolean | No | `true` |  | Burn animated captions into the video. Accepted aliases: disableCaptions (inverted). |
| `captions.preset` | string | No | `"Wrap 1"` |  | Caption style preset. Available: Basic, Revid, Karaoke, Hormozi, Ali, Wrap 1, Wrap 2, Faceless, Elegant, Difference, Opacity, Glass, Playful, Bold Punch, Movie, Outline, Cove, Beat, Floating, Editorial Emphasis, Gram, Morph Text, Typewriter, Sing-Along, Spotlight, Cinema, Boil, Editorial, Behind Subject, Word by Word, Contextual. Behind Subject puts the captions behind each scene's subject, a person or an object, cutting it out of every full-frame scene: 20 credits per started 30 seconds of each video file, pictures free. Where the subject leaves no room above it, the captions go beside it, or low in the frame when it fills the frame. When a person under the captions can't be cut out (an auto-reframed, split-screen, blurred or looping clip, or a video over 2 minutes), the video gets Wrap 1 captions at the bottom instead, with no cut-out charge. Contextual writes the captions' look for the video and replaces them, on its figures, names, lists and dates, with motion-graphics moments timed on the words: 1 credit per started second of narration, charged only when moments are added. |
| `captions.position` | string | No | `"bottom"` | `top` / `middle` / `bottom` | Vertical position of the captions. Behind Subject defaults to top. |

### `music` (optional)

> Recommended for background music: `music: { generateMusic: true }`. Revid generates an original instrumental track from this video's script, so it fits the topic and mood (20 credits per started minute of video). Add `music.generationMusicPrompt` to steer the mood, tempo and instruments. Use `music.trackName` only when the user asks for a specific library track: an agent cannot listen to the library, so a track picked by name rarely fits the video. With music on and no track, file or generated music, the neutral `Presentation` track plays. To make the music quieter or louder, set `music.volume` to `low`, `normal` or `high`, or to an exact level from 0 to 100.

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `music.enabled` | boolean | No | off for every workflow unless the request configures music explicitly. Auto-enabled when any other music field is set. |  | Add background music. To add music, set music.generateMusic to true (recommended) rather than picking a library track. Music on with no track, file or generated music plays the neutral `Presentation` track. The whole music block also accepts the legacy alias name `audio`. Default: off for every workflow unless the request configures music explicitly. Auto-enabled when any other music field is set.. Accepted aliases: audio.enabled, disableAudio (inverted). |
| `music.generateMusic` | boolean | No | `false` |  | Recommended way to add background music. Generates an original instrumental track from this video's script, so it fits the topic and mood. Turns music on by itself. 20 credits per started minute of video. Takes precedence over music.trackName and music.audioUrl. Accepted aliases: music.generate, options.generateMusic, hasToGenerateMusic. |
| `music.generationMusicPrompt` | string | No |  |  | Optional brief for generated music: mood, tempo, instruments (e.g. "calm premium explainer, soft piano, warm acoustic guitar, no drums"). Without it, the brief is written from the video's script. For lyrics-to-video and song-ad, the style of the song itself (genre, tempo, voice). Accepted aliases: music.generationPrompt, options.generationMusicPrompt, generationMusicPrompt. |
| `music.trackName` | string | No | `"Presentation"` |  | Name of a track from the Revid library (115+ tracks, e.g. Presentation, Snaps, Bright Morning, Chill Wave, Epic Battle Orchestra). Use it only when the user asks for a specific track: an agent cannot listen to the library, so prefer music.generateMusic. Matched case-insensitively, also by slug (e.g. startup-energy). Unknown names are rejected with a 400. Ignored when music.audioUrl is set. Accepted aliases: audio.trackName, selectedAudio. |
| `music.audioUrl` | string | No |  |  | Custom background music URL. Checked for reachability (HEAD request) before the render starts. Accepted aliases: music.url, audioUrl. |
| `music.volume` | number\|string | No | normal | `low` / `normal` / `high`, or a number from 0 to 100 | How loud the background music plays. Each workflow mixes its music at its own level under the voice-over: `low` and `high` scale that level, an exact number from 0 to 100 replaces it (the editor's music volume scale). 0 turns the music off. It does not turn music on by itself. Default: normal. Accepted aliases: audio.volume. |
| `music.musicGenerationModel` | string | No | `"base"` |  | Model used for AI music generation. |
| `music.soundWave` | boolean | No | `false` |  | Overlay an animated waveform visual. Workflows: music-to-video, lyrics-to-video. Accepted aliases: hasSoundWave. |
| `music.syncWith` | string | No |  | `beats` / `lyrics` | Sync visual cuts to the music. Workflows: music-to-video, lyrics-to-video. Accepted aliases: syncMusicWith. |
| `music.enableLyricsLipSync` | boolean | No | `false` |  | Animate characters lip-syncing the lyrics. Workflows: music-to-video, lyrics-to-video, song-ad. Accepted aliases: options.enableLyricsLipSync, enableLyricsLipSync. |
| `music.lipSyncFrequency` | number | No | `0.3` | min 0, max 1 | Fraction of the video duration with lip-synced shots (0.3 = 30%). Workflows: music-to-video, lyrics-to-video, song-ad. Accepted aliases: options.lipSyncFrequency, lipSyncFrequency. |

### `options` (optional)

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `options.promptTargetDuration` | number\|string | No | `30` | min 5, max 1800 | Target video duration in seconds for prompt-driven workflows, or "auto" to let the AI choose. Workflows: prompt-to-video, ad-generator, lyrics-to-video, song-ad. Accepted aliases: promptTargetDuration, options.targetDuration (in prompt workflows). |
| `options.speakerCount` | string | No |  | `auto` / `1` / `2` | With music.enableLyricsLipSync, caps how many singers the lip sync detects in the generated vocals. It does not add singers to the song. Workflows: lyrics-to-video. |
| `options.disableAudio` | boolean | No |  |  | Hard override: strip background music from the output regardless of the music block. Accepted aliases: disableAudio. |
| `options.disableVoice` | boolean | No |  |  | Hard override: no voice-over regardless of the voice block. Accepted aliases: disableVoice. |
| `options.soundEffects` | boolean | No | `false` |  | Generate sound effects matched to the scenes. Accepted aliases: hasToGenerateSoundEffects. |
| `options.addStickers` | boolean | No | `false` |  | Add animated stickers/emojis matched to the script. Accepted aliases: addStickers. |
| `options.nsfwFilter` | boolean | No | `false` |  | Filter NSFW content in generated media. Accepted aliases: enableNsfwFilter. |
| `options.language` | string | No |  |  | Language override for the whole generation (same as voice.language). Accepted aliases: language, lang, voice.language. |
| `options.watermark` | object\|null | No |  |  | Custom watermark object forwarded as-is ({ url, position, opacity, ... }). null removes it. Accepted aliases: watermark. |
| `options.selectedPalette` | string | No |  |  | Color palette name for generated visuals. |
| `options.preventSummarization` | boolean | No | `false` |  | Never shorten the provided text (overrides summarization heuristics). |
| `options.hasToGenerateCover` | boolean | No | `false` |  | Generate a cover/thumbnail image for the video. |
| `options.coverTextType` | string | No | `"layers"` |  | Style of the text on the generated cover. |
| `options.coverStyle` | string | No |  | `depth` / `editorial` / `magazine` / `journal` / `scrapbook` / `viral` | Look of the title on the generated cover. Omit for the plain title box. Only applies when options.coverTextType = layers. |
| `options.coverPrompt` | string | No |  |  | What the cover should show, up to 1200 characters (longer is truncated). Steers the cover concept; omit to design it from the script. |
| `options.hasTextSmallAtBottom` | boolean | No | `false` |  | Show a small text line at the bottom of the video. |

### `render` (optional)

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `render.resolution` | string | No | `"1080p"` | `720p` / `1080p` / `4k` | Output resolution. 4k exports cost extra credits. Accepted aliases: resolution. |
| `render.compression` | number | No | `18` | min 0, max 50 | H.264 CRF-style compression. Lower = better quality and bigger files. Accepted aliases: compression. |
| `render.frameRate` | number | No | `30` | min 1, max 60 | Output frame rate. Accepted aliases: frameRate. |

### `advanced` (optional)

| Field | Type | Required | Default | Allowed | Description |
|---|---|---|---|---|---|
| `advanced.customCreationParams` | object | No |  |  | Expert escape hatch: extra legacy creationParams merged into the final payload. Workflow identity keys (slug, flowType) stay locked. Use only with guidance from the Revid team. Accepted aliases: customCreationParams. |

## Notes

- Runs the same tool as the Lyrics to Music Video form on /create: the song is generated with vocals, then the video is cut to it.
- source.text is sung as written (up to 5,000 characters, sections like [verse] and [chorus] are supported). source.prompt (10 to 500 characters) describes the song instead, and Revid writes the lyrics. Pass one of the two, not both.
- music.generationMusicPrompt sets the style of the song (genre, mood, voice), for example "upbeat synth pop, female vocal".
- music.syncWith lyrics (default) shows the lyrics as captions; beats cuts on the beat without lyric captions. music.soundWave adds a sound wave.
- music.enableLyricsLipSync + music.lipSyncFrequency have characters sing the lyrics on camera; options.speakerCount (auto, 1 or 2) caps the singers detected for lip sync.
- media.provided images (artist, a place, an object) are kept as they are in the scenes that show them. characterIds use saved consistent characters.
- options.promptTargetDuration ("auto" or seconds) sizes the song written from source.prompt.

## Response

Success: `{ "success": 1, "pid": "...", ... }` — the render is asynchronous. Get the result via your `webhookUrl` or poll `GET /api/public/v3/status?pid=...`.

Errors: 400 — validation error. The message names the exact field, e.g. "voice.speed must be between 0.5 and 2." 401 — missing or invalid API key (header `key`). 402 — the API key is valid but the account has no active plan. Generation endpoints require one; see https://www.revid.ai/pricing. 403 — the request was blocked in front of the API and never reached it; the body is plain text `error code: 1010`, not JSON. Usual cause: the HTTP client sent its default `User-Agent`. Set an explicit, descriptive `User-Agent` (for example `my-app/1.0`) and retry. 500 — internal error. Credits consumed by a failed render are refunded automatically.

---

All workflows: [script-to-video](https://www.revid.ai/llm/api/script-to-video.md) · [prompt-to-video](https://www.revid.ai/llm/api/prompt-to-video.md) · [audio-to-video](https://www.revid.ai/llm/api/audio-to-video.md) · [music-to-video](https://www.revid.ai/llm/api/music-to-video.md) · [lyrics-to-video](https://www.revid.ai/llm/api/lyrics-to-video.md) · [song-ad](https://www.revid.ai/llm/api/song-ad.md) · [article-to-video](https://www.revid.ai/llm/api/article-to-video.md) · [avatar-to-video](https://www.revid.ai/llm/api/avatar-to-video.md) · [static-background-video](https://www.revid.ai/llm/api/static-background-video.md) · [motion-transfer](https://www.revid.ai/llm/api/motion-transfer.md) · [character-swap](https://www.revid.ai/llm/api/character-swap.md) · [caption-video](https://www.revid.ai/llm/api/caption-video.md) · [video-to-clips](https://www.revid.ai/llm/api/video-to-clips.md) · [ad-generator](https://www.revid.ai/llm/api/ad-generator.md)

More: [endpoint reference](https://www.revid.ai/llm/api/endpoints.md) · [interactive docs](https://www.revid.ai/docs) · [OpenAPI spec](https://www.revid.ai/postman/revid-public-v3-render.openapi.json) · [MCP server](https://www.revid.ai/mcp)
