curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
"https://example.com/property/living-room.jpg",
"https://example.com/property/kitchen.jpg",
"https://example.com/property/bedroom.jpg",
"https://example.com/property/bathroom.jpg",
"https://example.com/property/backyard.jpg",
"https://example.com/property/exterior.jpg"
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"orientation": "portrait",
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.",
"orchestration_mode": "standard",
"webhook_url": "https://yourserver.com/webhooks/vibepeak",
"background_music": true,
"subtitle": {
"subtitle_style_preset": "classic"
},
"elements_config": {
"enabled": true,
"watermark": {
"url": "https://example.com/logo.png",
"position": "bottom-right",
"size": 20,
"opacity": 100
},
"text": {
"content": "Powered by Vibepeak",
"color": "#FFFFFF",
"font": "Inter",
"size": 20,
"position": "center"
}
}
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
orientation: 'portrait',
script: 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
orchestration_mode: 'standard',
webhook_url: 'https://yourserver.com/webhooks/vibepeak',
background_music: true,
subtitle: {
subtitle_style_preset: 'classic'
},
elements_config: {
enabled: true,
watermark: {
url: 'https://example.com/logo.png',
position: 'bottom-right',
size: 20,
opacity: 100
},
text: {
content: 'Powered by Vibepeak',
color: '#FFFFFF',
font: 'Inter',
size: 20,
position: 'center'
}
}
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
json={
'images': [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'orientation': 'portrait',
'script': 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
'orchestration_mode': 'standard',
'webhook_url': 'https://yourserver.com/webhooks/vibepeak',
'background_music': True,
'subtitle': {
'subtitle_style_preset': 'classic'
},
'elements_config': {
'enabled': True,
'watermark': {
'url': 'https://example.com/logo.png',
'position': 'bottom_right',
'size': 20,
'opacity': 100
},
'text': {
'content': 'Powered by Vibepeak',
'color': '#FFFFFF',
'font': 'Inter',
'size': 20,
'position': 'center'
}
}
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
{
"task_id": "task_abc123xyz",
"status": "queued",
"queue_position": 3,
"estimated_completion": "2026-01-04T12:30:00Z",
"created_at": "2026-01-04T12:00:00Z",
"livemode": true
}
{
"task_id": "task_abc123xyz",
"status": "queued",
"message": "Video generation request accepted",
"created_at": "2026-01-04T12:00:00Z",
"livemode": false,
"request_id": "req_xyz123",
"_links": {
"self": "/v1/tasks/task_abc123xyz",
"poll_interval_seconds": 30
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "images must contain between 6 and 15 URLs",
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Provide either 'subtitle_style_preset' or 'styles', but not both.",
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "SCRIPT_TOO_SHORT",
"message": "Script is too short for 6 images. Minimum 216 characters required (36 per image), but got 100.",
"request_id": "req_xyz123",
"details": {
"script_length": 100,
"min_length": 216,
"max_length": 306,
"image_count": 6
}
}
}
{
"error": {
"code": "SCRIPT_TOO_LONG",
"message": "Script is too long for 6 images. Maximum 306 characters allowed (51 per image), but got 500.",
"request_id": "req_xyz123",
"details": {
"script_length": 500,
"min_length": 216,
"max_length": 306,
"image_count": 6
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "If elements_config is enabled, you must provide at least a 'watermark' or 'text' configuration.",
"request_id": "req_xyz123",
"details": {
"field": "elements_config.enabled",
"issues": [
{
"path": "elements_config.enabled",
"message": "If elements_config is enabled, you must provide at least a 'watermark' or 'text' configuration."
}
]
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid hex color",
"request_id": "req_xyz123",
"details": {
"field": "elements_config.text.color",
"issues": [
{
"path": "elements_config.text.color",
"message": "Invalid hex color"
}
]
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "If intro_outro is enabled, you must provide at least an 'intro' or 'outro' configuration.",
"details": {
"field": "elements_config.intro_outro.enabled",
"issues": [
{
"path": "elements_config.intro_outro.enabled",
"message": "If intro_outro is enabled, you must provide at least an 'intro' or 'outro' configuration."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Unknown intro template id. Choose one of the documented intro templates; GET /v1/intro-outro/templates lists them.",
"details": {
"field": "elements_config.intro_outro.intro.templateId",
"issues": [
{
"path": "elements_config.intro_outro.intro.templateId",
"message": "Unknown intro template id. Choose one of the documented intro templates; GET /v1/intro-outro/templates lists them."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Brand color must be a six-digit hex string such as #1A73E8, with no alpha channel",
"details": {
"field": "elements_config.intro_outro.intro.brandColorPrimary",
"issues": [
{
"path": "elements_config.intro_outro.intro.brandColorPrimary",
"message": "Brand color must be a six-digit hex string such as #1A73E8, with no alpha channel"
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "brandColorContrast requires brandColorPrimary on the same overlay.",
"details": {
"field": "elements_config.intro_outro.outro.brandColorContrast",
"issues": [
{
"path": "elements_config.intro_outro.outro.brandColorContrast",
"message": "brandColorContrast requires brandColorPrimary on the same overlay."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "title is required by template 'ruled_caps'. Send it in values.title: an omitted field renders nothing.",
"details": {
"field": "elements_config.intro_outro.intro.values.title",
"issues": [
{
"path": "elements_config.intro_outro.intro.values.title",
"message": "title is required by template 'ruled_caps'. Send it in values.title: an omitted field renders nothing."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "agent_name is required by template 'serif_business_card'. Send it in values.agent_name: an omitted field renders nothing.",
"details": {
"field": "elements_config.intro_outro.outro.values.agent_name",
"issues": [
{
"path": "elements_config.intro_outro.outro.values.agent_name",
"message": "agent_name is required by template 'serif_business_card'. Send it in values.agent_name: an omitted field renders nothing."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Template 'custom_image' requires assets.custom_image: the URL of the image it renders.",
"details": {
"field": "elements_config.intro_outro.outro.assets.custom_image",
"issues": [
{
"path": "elements_config.intro_outro.outro.assets.custom_image",
"message": "Template 'custom_image' requires assets.custom_image: the URL of the image it renders."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "SCRIPT_INVALID_CHARACTERS",
"message": "Script contains characters not supported by text-to-speech: \"🏠\". Emojis and other non-speech symbols are not allowed.",
"request_id": "req_xyz123",
"details": {
"invalid_characters": ["3", "2"]
}
}
}
{
"error": {
"code": "CONCURRENCY_LIMIT_EXCEEDED",
"message": "You have reached your concurrent task limit of 1. Please wait for existing tasks to complete.",
"request_id": "req_xyz123",
"details": {
"limit": 1,
"in_flight": 1,
"plan": "Plus"
}
}
}
Videos
Create Narrated Slideshow
Create an AI-powered real estate narrated slideshow video
POST
/
v1
/
real-estate
/
narrated-slideshow
curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
"https://example.com/property/living-room.jpg",
"https://example.com/property/kitchen.jpg",
"https://example.com/property/bedroom.jpg",
"https://example.com/property/bathroom.jpg",
"https://example.com/property/backyard.jpg",
"https://example.com/property/exterior.jpg"
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"orientation": "portrait",
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.",
"orchestration_mode": "standard",
"webhook_url": "https://yourserver.com/webhooks/vibepeak",
"background_music": true,
"subtitle": {
"subtitle_style_preset": "classic"
},
"elements_config": {
"enabled": true,
"watermark": {
"url": "https://example.com/logo.png",
"position": "bottom-right",
"size": 20,
"opacity": 100
},
"text": {
"content": "Powered by Vibepeak",
"color": "#FFFFFF",
"font": "Inter",
"size": 20,
"position": "center"
}
}
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
orientation: 'portrait',
script: 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
orchestration_mode: 'standard',
webhook_url: 'https://yourserver.com/webhooks/vibepeak',
background_music: true,
subtitle: {
subtitle_style_preset: 'classic'
},
elements_config: {
enabled: true,
watermark: {
url: 'https://example.com/logo.png',
position: 'bottom-right',
size: 20,
opacity: 100
},
text: {
content: 'Powered by Vibepeak',
color: '#FFFFFF',
font: 'Inter',
size: 20,
position: 'center'
}
}
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
json={
'images': [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'orientation': 'portrait',
'script': 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
'orchestration_mode': 'standard',
'webhook_url': 'https://yourserver.com/webhooks/vibepeak',
'background_music': True,
'subtitle': {
'subtitle_style_preset': 'classic'
},
'elements_config': {
'enabled': True,
'watermark': {
'url': 'https://example.com/logo.png',
'position': 'bottom_right',
'size': 20,
'opacity': 100
},
'text': {
'content': 'Powered by Vibepeak',
'color': '#FFFFFF',
'font': 'Inter',
'size': 20,
'position': 'center'
}
}
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
{
"task_id": "task_abc123xyz",
"status": "queued",
"queue_position": 3,
"estimated_completion": "2026-01-04T12:30:00Z",
"created_at": "2026-01-04T12:00:00Z",
"livemode": true
}
{
"task_id": "task_abc123xyz",
"status": "queued",
"message": "Video generation request accepted",
"created_at": "2026-01-04T12:00:00Z",
"livemode": false,
"request_id": "req_xyz123",
"_links": {
"self": "/v1/tasks/task_abc123xyz",
"poll_interval_seconds": 30
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "images must contain between 6 and 15 URLs",
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Provide either 'subtitle_style_preset' or 'styles', but not both.",
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "SCRIPT_TOO_SHORT",
"message": "Script is too short for 6 images. Minimum 216 characters required (36 per image), but got 100.",
"request_id": "req_xyz123",
"details": {
"script_length": 100,
"min_length": 216,
"max_length": 306,
"image_count": 6
}
}
}
{
"error": {
"code": "SCRIPT_TOO_LONG",
"message": "Script is too long for 6 images. Maximum 306 characters allowed (51 per image), but got 500.",
"request_id": "req_xyz123",
"details": {
"script_length": 500,
"min_length": 216,
"max_length": 306,
"image_count": 6
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "If elements_config is enabled, you must provide at least a 'watermark' or 'text' configuration.",
"request_id": "req_xyz123",
"details": {
"field": "elements_config.enabled",
"issues": [
{
"path": "elements_config.enabled",
"message": "If elements_config is enabled, you must provide at least a 'watermark' or 'text' configuration."
}
]
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid hex color",
"request_id": "req_xyz123",
"details": {
"field": "elements_config.text.color",
"issues": [
{
"path": "elements_config.text.color",
"message": "Invalid hex color"
}
]
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "If intro_outro is enabled, you must provide at least an 'intro' or 'outro' configuration.",
"details": {
"field": "elements_config.intro_outro.enabled",
"issues": [
{
"path": "elements_config.intro_outro.enabled",
"message": "If intro_outro is enabled, you must provide at least an 'intro' or 'outro' configuration."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Unknown intro template id. Choose one of the documented intro templates; GET /v1/intro-outro/templates lists them.",
"details": {
"field": "elements_config.intro_outro.intro.templateId",
"issues": [
{
"path": "elements_config.intro_outro.intro.templateId",
"message": "Unknown intro template id. Choose one of the documented intro templates; GET /v1/intro-outro/templates lists them."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Brand color must be a six-digit hex string such as #1A73E8, with no alpha channel",
"details": {
"field": "elements_config.intro_outro.intro.brandColorPrimary",
"issues": [
{
"path": "elements_config.intro_outro.intro.brandColorPrimary",
"message": "Brand color must be a six-digit hex string such as #1A73E8, with no alpha channel"
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "brandColorContrast requires brandColorPrimary on the same overlay.",
"details": {
"field": "elements_config.intro_outro.outro.brandColorContrast",
"issues": [
{
"path": "elements_config.intro_outro.outro.brandColorContrast",
"message": "brandColorContrast requires brandColorPrimary on the same overlay."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "title is required by template 'ruled_caps'. Send it in values.title: an omitted field renders nothing.",
"details": {
"field": "elements_config.intro_outro.intro.values.title",
"issues": [
{
"path": "elements_config.intro_outro.intro.values.title",
"message": "title is required by template 'ruled_caps'. Send it in values.title: an omitted field renders nothing."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "agent_name is required by template 'serif_business_card'. Send it in values.agent_name: an omitted field renders nothing.",
"details": {
"field": "elements_config.intro_outro.outro.values.agent_name",
"issues": [
{
"path": "elements_config.intro_outro.outro.values.agent_name",
"message": "agent_name is required by template 'serif_business_card'. Send it in values.agent_name: an omitted field renders nothing."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Template 'custom_image' requires assets.custom_image: the URL of the image it renders.",
"details": {
"field": "elements_config.intro_outro.outro.assets.custom_image",
"issues": [
{
"path": "elements_config.intro_outro.outro.assets.custom_image",
"message": "Template 'custom_image' requires assets.custom_image: the URL of the image it renders."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "SCRIPT_INVALID_CHARACTERS",
"message": "Script contains characters not supported by text-to-speech: \"🏠\". Emojis and other non-speech symbols are not allowed.",
"request_id": "req_xyz123",
"details": {
"invalid_characters": ["3", "2"]
}
}
}
{
"error": {
"code": "CONCURRENCY_LIMIT_EXCEEDED",
"message": "You have reached your concurrent task limit of 1. Please wait for existing tasks to complete.",
"request_id": "req_xyz123",
"details": {
"limit": 1,
"in_flight": 1,
"plan": "Plus"
}
}
}
Creates a new video generation task from property images. The video is generated asynchronously - you’ll receive a task ID to poll for status or configure a webhook to be notified when complete.
See Error Handling for more details.
Request Body
string[] | object[]
required
Images for the slideshow. Must contain 6 to 15 images, and all entries must be unique (no duplicates allowed).Each array entry can take one of two shapes. Pick a single shape for the whole array — you cannot mix strings and objects.1. Simple (URL strings): an array of image URLs. Every image renders as a static still. This is the default behavior.2. Rich (objects): an array of objects, where each image can opt into a subtle camera move (the “reanimate” option).Images should be:
- Publicly accessible HTTP(S) URLs (not a private/internal address)
- JPEG, PNG, or WebP format
- Minimum resolution: 720p recommended
Show Rich image object properties
Show Rich image object properties
string
required
The image URL. Same rules as the string shape: a public HTTP(S) URL that cannot point to a private/internal address.
boolean
default:"false"
When
true, VibePeak’s animation pipeline applies a subtle camera move to this image. When false or omitted, the image renders as a static still.string
Which camera move to apply. One of:
traveling_lefttraveling_righttraveling_uptraveling_downdolly_indolly_out
null (together with reanimate: true) to let VibePeak pick a move automatically.string
Optional scene label (max 64 characters) used to tune the camera move to the kind of room, e.g.
"living room", "bedroom", "kitchen", "bathroom", "garden". Omit to use a neutral default.reanimate and movement go together. When reanimate is true, movement is required — supply one of the six values above or null (which lets VibePeak pick a move automatically). When reanimate is false or omitted, movement must not be set.For the best visual result, upload images that already match your target format. When
orientation is set to portrait, the pipeline can expand landscape photos to fit a 9:16 output when needed.string
Optional output orientation for the final video.
If omitted, VibePeak auto-detects the orientation from the first image and defaults to
| Value | Description |
|---|---|
landscape | Generate a horizontal 16:9 video |
portrait | Generate a vertical 9:16 video |
landscape if detection is not possible.Choosing
portrait is useful when you want social-first vertical output from a mixed or landscape-heavy photo set.boolean
default:"true"
Whether the video has a voiceover (narration). Defaults to
true.Set to false to create a music-only slideshow: omit voice and script, and the video is produced with background music only (or silent if background_music is false). Without a voiceover, subtitle and avatar_id are not available and will be rejected.object
Voice configuration for the AI-generated narration. Required when
voiceover_enabled is true (the default); omit it together with script when voiceover_enabled is false.Show Voice properties
Show Voice properties
string
required
Voice ID to use for narration. You can find available voices using the List Voices endpoint.
string
required
ISO 639-1 language code for TTS pronunciation (e.g.,
en, es, de, fr, pt).This helps produce more accurate pronunciation for the target language.Use the same language code as the script content for best results.
string
Narration script for text-to-speech. Required when
Formula:
voiceover_enabled is true (the default); omit it together with voice when voiceover_enabled is false.The script length must be proportional to the number of images to ensure proper narration pacing (~3 seconds per image). The recommended length is per narration language (our client app targets it directly); this API enforces the outer envelope across every supported language:| Images | Min Characters | Max Characters |
|---|---|---|
| 6 | 216 | 306 |
| 10 | 360 | 510 |
| 15 | 540 | 765 |
min = images × 36, max = images × 51Numbers and symbols are allowed. Write the script naturally — numbers (e.g.
3 bedrooms, 580.000 €) and symbols are read correctly by text-to-speech. Only emojis and other non-speech characters (pictographs, control characters) are rejected.Scripts that are too short or too long for the number of images will be rejected with
SCRIPT_TOO_SHORT or SCRIPT_TOO_LONG error codes.string
default:"artistic"
Video orchestration mode that controls scene pacing and transitions.
| Value | Description |
|---|---|
standard | Standard mode with scenes of 3-7 seconds |
artistic | Cinematic mode with 2-3 second scenes and visual weight hierarchy (default) |
sequential | Images appear in order without AI reordering. Scene duration is auto-calculated. |
string
URL to receive webhook notification when the task completes.Must be a valid HTTPS URL. See Webhooks for details.
Webhook URLs must use HTTPS and cannot point to private/internal networks (SSRF protection).
string
Optional UUID of an avatar to include in the video.Must be a valid UUID v4 format (e.g.,
123e4567-e89b-12d3-a456-426614174000).Requires voiceover. A speaking avatar needs narration, so omit
avatar_id when voiceover_enabled is false (music-only or silent video) — the request is rejected otherwise.boolean
default:"false"
Enable AI-generated background music for the video.When
true, generates calm ambient music that matches the video duration. The music is automatically generated with a real estate style (soft piano, elegant, minimal) and mixed at 20% volume to complement the narration.Background music generation adds a few seconds to the processing time. If music generation fails, the video will still be created without background music.
object
Subtitle configuration for word-level animated overlays. Use
subtitle_style_preset for quick setup or styles for granular control. Providing both will result in a validation error.Requires voiceover. Subtitles are derived from the narration’s word timing, so omit
subtitle (or set it to false) when voiceover_enabled is false — the request is rejected otherwise.Show Subtitle Config properties
Show Subtitle Config properties
string
Name of a predefined style to apply.
Options:
classic, cinematic, gradient, handwritten, hustle, karaoke_highlight_no_box, karaoke_red_box_phrase, karaoke_word_box_clip, minimal, neon, one_word_red_box, phrase_yellow, retro, tiktok_default, youtube_caption.object
Granular styling properties for subtitle rendering.
Show Styles properties
Show Styles properties
integer
default:"2"
SSA alignment: 1-9 (e.g., 2 for bottom-center).
string
default:"#000000"
Background color in hex format (e.g.,
#000000).integer
default:"100"
Transparency of background box: 0-255.
string
default:"phrase_blocks"
Subtitle reveal behavior. Options:
phrase_blocks, one_word, karaoke_highlight, karaoke_box_word.integer
default:"1"
Border style: 1 (Outline) or 3 (Box).
string
default:"Arial"
System font name.
integer
default:"28"
Font size as a percentage of the safe container width, not pixels (14-100). The renderer fits the longest subtitle line to occupy that percentage of the available width, so it scales with the final video resolution. The default of 28 is a legacy value that renders far smaller than every style preset (all of which use 90); send an explicit value instead of relying on it.
integer
default:"40"
Left margin in pixels (0-500).
integer
default:"40"
Right margin in pixels (0-500).
integer
default:"200"
Vertical/Bottom margin in pixels (0-500).
number
default:"0.3"
Max silence (sec) between words before phrase break (0.01-2).
integer
default:"6"
Automatic line wrapping constraint (1-12).
integer
default:"2"
Thickness of text stroke (0-10).
string
default:"#000000"
Color of text stroke in hex format.
string
default:"#FFFFFF"
Default text color in hex format.
string
default:"#FFFFFF"
Alt color for highlights/karaoke in hex format.
integer
default:"0"
Thickness of text drop shadow (0-10).
object
Configuration for visual elements and branding overlays. If not provided, elements are disabled by default.
Show Elements Config properties
Show Elements Config properties
boolean
default:"false"
Global toggle for the elements overlay feature.
object
Watermark (logo) branding configuration.
Show Watermark properties
Show Watermark properties
string
required
URL of the uploaded watermark image. Must be a valid public URL.
string
default:"bottom-right"
Anchor point for the watermark placement. Ignored when
mode is mosaic.
Options: top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right.number
default:"20"
Relative scale percentage of the watermark (0-100). Ignored when
mode is mosaic.When the overlay is enabled (elements_config.enabled: true) and mode is single, a size below 2 is rejected with 400 INVALID_WATERMARK_GEOMETRY before any credits are charged — a smaller logo cannot render. Fractional values are accepted (2.5 is fine). Omitting the field keeps the default of 20 and is never rejected.number
default:"100"
Transparency level (0 for transparent, 100 for opaque). Applies in both modes.When the overlay is enabled (
elements_config.enabled: true), an opacity below 5 is rejected with 400 INVALID_WATERMARK_GEOMETRY before any credits are charged, in single and mosaic alike. Fractional values are accepted. Omitting the field keeps the default of 100 and is never rejected.string
default:"single"
Watermark positioning mode.
single places one logo at position/size. mosaic repeats the logo as a fixed, code-determined tiled pattern across the whole frame instead, ignoring position and size.Options: single, mosaic.integer
default:"-20"
Tile rotation in degrees (
-45 to 45), applied to every tile. Only meaningful when mode is mosaic.object
Text overlay configuration for static labels or call-to-actions.
Show Text properties
Show Text properties
string
required
Text content to be rendered on the video (max 20 characters).
string
default:"#FFFFFF"
Hex color code for the text (e.g.,
#FFFFFF).string
default:"Inter"
Font family for the text overlay.
Options:
Alegreya, DM Mono, Inter, Jost, Libre Bodoni, Quicksand, Roboto, Raleway, Noto Sans, Zilla Slab.number
default:"100"
Transparency level of the text (0-100).
number
default:"20"
Font size scale relative to video height (1-100).
string
default:"center"
Anchor point for the text placement. Same options as watermark position.
object
Branded intro and outro overlays rendered over the opening and closing of the video.Each overlay covers roughly the first or last 1.5 seconds of the video and never adds duration. The intro is fully visible from the first frame and animates out; the outro animates in and holds through the final frame. Overlays render on top of the watermark; subtitles stay above everything.There are four valid combinations: intro only (
Each design also ships with a default animation, listed per template in the gallery;
intro_outro is independent of the top-level enabled flag. The top-level enabled gates watermark and text only; intro_outro carries its own toggle. To add an intro or outro without a watermark or text overlay, send "enabled": false at the top level and "enabled": true inside intro_outro:{
"elements_config": {
"enabled": false,
"intro_outro": {
"enabled": true,
"intro": { "...": "..." }
}
}
}
outro omitted), outro only (intro omitted), both, or "enabled": false (no overlays; any intro/outro objects are ignored). When enabled is true, at least one of intro or outro must be provided, otherwise the request is rejected with VALIDATION_ERROR.Show Intro/Outro properties
Show Intro/Outro properties
boolean
default:"false"
Toggle for the intro/outro overlays. When
true, at least one of intro or outro is required.object
Overlay shown over the opening of the video. Accepts intro template ids only; an outro template id in this slot is rejected.
Show Intro/outro selection properties
Show Intro/outro selection properties
string
required
The id of the design to render, such as
ruled_caps. Every design is shown in the template gallery; GET /v1/intro-outro/templates returns the same catalogue as JSON.The intro slot accepts intro designs only and the outro slot accepts outro designs only; naming one in the wrong slot is a VALIDATION_ERROR.integer
required
Version of the template to render. The current catalogue version is
1; any other value is rejected.string
required
Animation preset id (see the table below). The intro plays the preset as an exit; the outro plays it as an entrance.
object
default:"{}"
Text values keyed by the template’s field ids. The ids are the same for every design of a kind, with one exception:
- intro:
title(required, up to 40 characters) andsubtitle(optional, up to 24) - outro:
agent_name(required, up to 32) andcontact_line(optional, up to 64) - the
custom_imageoutro: no text fields at all. Sendvaluesempty.
title on an intro, or an agent_name on any of the eleven contact-card outros, that is missing, empty or only whitespace is refused by the request schema with 400 VALIDATION_ERROR. details.field names the field as a dotted path, elements_config.intro_outro.intro.values.title or elements_config.intro_outro.outro.values.agent_name, and details.issues repeats it with every other field the schema refused in the same request. No task is created and no credits are charged.An optional field you leave out renders nothing. There is no fallback: an intro sent without subtitle draws no subtitle line, exactly as if you had sent an empty string. The default_value published by GET /v1/intro-outro/templates is sample copy for previewing a design in your own picker, and it is never drawn into a video.String values are limited to 500 characters by this API; the per-field lengths above are what each design was drawn to hold, and a longer value is fitted down to size rather than refused.A key that is not one of the ids above is accepted and then ignored. Only the ids the template declares are read, so
{ "name": "Alex Rivera" } on an outro contributes nothing at all. agent_name is then missing, so that payload is rejected with 400 VALIDATION_ERROR on elements_config.intro_outro.outro.values.agent_name rather than rendering anything. Check your keys against the field reference.object
default:"{}"
Uploaded image URLs keyed by the template’s image field ids. The catalogue declares two, both on outros:
agent_photo, the portrait the eleven contact-card designs draw, and custom_image, the full-frame image the custom_image design requires.custom_image is required on the custom_image outro: leaving it out is a 400 VALIDATION_ERROR whose details.field is elements_config.intro_outro.outro.assets.custom_image. agent_photo is optional, and a contact card sent without one draws no portrait rather than a stand-in.Each URL must be a publicly accessible HTTP(S) URL of at most 2048 characters, and cannot point to a private or internal network. A custom_image must additionally be a JPG, PNG or WEBP of at most 8 MB whose shortest edge is at least 400 px, or the request is rejected with 400 INVALID_PARAMETER. That one is not a schema rejection: the image is fetched and measured after the body validates, so it answers with a details.field and no details.issues.Intros declare no image field, so assets on an intro selection does nothing.Alongside custom_image the server records the URL you sent under a companion key, custom_image_source, so a later re-render starts from your original rather than from an already adapted frame. You never send that key and the renderer ignores it.string
Your agency’s brand color as
#RRGGBB. It paints the line the design leads with: the headline of an intro, the name on an outro card.Six hex digits only, upper or lower case (#1A73E8 and #1a73e8 are both accepted). The three-digit shorthand #1AE and the eight-digit alpha form #1A73E8FF are rejected with VALIDATION_ERROR.Optional, and set per overlay: intro and outro carry their own colors and are branded independently, so you can brand one end and leave the other exactly as delivered.All 27 templates take it. Sent on its own it also paints everything brandColorContrast would, so the design renders in one color rather than half branded. The custom_image outro is the one design with nothing to repaint: it accepts both colors and ignores them.string
Your agency’s secondary color as
#RRGGBB, same format rules. It paints the second line of the design and the furniture drawn around it: rules, underlines, frames, corner brackets, rings, sunbursts, dividers, photo borders and, on glow_caps, the halo the design is built on.Optional, and only valid alongside brandColorPrimary on the same overlay. A brandColorContrast sent on its own is rejected with VALIDATION_ERROR, because a contrast color brands nothing without a primary.When it is omitted, every slot it would have painted takes brandColorPrimary instead.There is no minimum distance between the two colors. Two shades of one color are accepted, and what they render is a design that reads in a single tone.object
Overlay shown over the closing of the video. Accepts outro template ids only. Same properties as
intro; the animation plays as an entrance instead of an exit.custom_image closes on an image of your own instead of a contact card. It is an outro like the other eleven and not an extra scene: it covers the same closing window and adds no duration. Send the image as assets.custom_image, leave values empty, and pick any of the nine animations. brandColorPrimary and brandColorContrast are accepted on it and then ignored, because the design draws no type of its own.When the image’s aspect ratio differs from the video’s, the server extends its background out to the video’s shape before rendering, so nothing in the image is ever cropped. That preparation runs while this request is being accepted, so a POST carrying a custom_image outro takes a few seconds, and up to about 25 seconds when the ratios differ. Allow for it in your client timeout.The image must be a JPG, PNG or WEBP of at most 8 MB whose shortest edge is at least 400 px, or the request is rejected with 400 INVALID_PARAMETER. If the preparation cannot run at all, the request fails with a retryable 503 INTRO_OUTRO_ADAPTATION_UNAVAILABLE before any task is created or any credit is charged. The outro itself costs no extra credits.Show Available templates
Show Available templates
15 intro designs and 12 outro designs. Every one takes
brandColorPrimary and brandColorContrast, although the custom_image outro draws no type of its own and so repaints nothing.The template gallery shows all 27 rendered, in both orientations, with each design’s ids, accepted fields, default animation and the colours it ships with. GET /v1/intro-outro/templates returns the same catalogue as JSON.See How the two colors land below for what each colour paints.Show How the two colors land
Show How the two colors land
Every template takes both colors.
brandColorPrimary goes on the one line the design is read by, and brandColorContrast on everything the design sets apart from it, so a branded frame shows the pair you actually chose rather than one color twice.Nothing is drawn behind the type. The colors replace the ones the design ships with, in the places its designer opened, and no shape is added to the frame. Two shapes are deliberately left out because both are knockouts rather than ornament: the photo gap in sunburst_serif and the ring gap in bordered_cream carry their own design’s background color so that what is under them shows through, and painting either would draw a ring the design does not have.Readability is yours to judge, and nothing is refused for it. Every outro draws its own background plate, and a branded line on one is worth checking against it: quiet_offwhite is near white, bold_ring_card and serif_business_card are white, forest_green is a dark forest green and black_gold is near black. A mid-tone brand color contrasts weakly with all of them. The design’s own type was drawn at 3 to 1 against its plate, so that is the number to aim at, but a color under it is rendered exactly as sent rather than adjusted or turned away. If a line reads thin, a lighter or darker shade of the same color, or a different design, is the fix.Intros have no plate of their own, so there is nothing to measure a color against: what sits behind an intro’s type is your video. Nothing is added to compensate either. Branding is a repaint and never changes a design’s composition: every layer keeps the position, the opacity and the visibility the design ships with, so a branded overlay is the design you picked in the colors you sent and nothing else.Show Available animations
Show Available animations
The same preset list applies to both slots. The intro only ever animates out (it is fully formed at frame zero) and the outro only ever animates in (it holds through the final frame).
| Animation ID | Motion |
|---|---|
fade | Opacity fade |
slide_up | Slides toward the top edge |
slide_down | Slides toward the bottom edge |
slide_left | Slides toward the left edge |
slide_right | Slides toward the right edge |
scale | Scales in or out |
blur | Blur dissolve |
wipe | Directional wipe |
stagger | Elements animate one after another |
animation is required on every selection, so it is a recommendation rather than a fallback.No color is refused for how it looks. Any
#RRGGBB reaches the render on any design: two shades of one color are fine and simply render the design in a single tone, and a color that reads thinly on a card’s own background is drawn as sent rather than adjusted or turned away. Only the shape is validated, so a value that is not six hex digits, or a brandColorContrast with no brandColorPrimary, is a VALIDATION_ERROR.A malformed color, or a brandColorContrast sent without a brandColorPrimary, is rejected up front with a 400 VALIDATION_ERROR and no task is created. Nothing about a color is checked later than that, so a color can no longer be the reason an overlay is dropped.The overlay job can still fail for other reasons, and on this endpoint it is submitted after the video itself has rendered, so it does not fail your task: the video is delivered without the overlay and the task result and webhook payload carry an intro_outro_warning field with the renderer’s own reason.Overlay application runs as a post-processing step after the video renders. While it runs, Get Task reports the transitional
intro_outro status (after watermarking, before subtitling). If the overlay cannot be applied, for example when the video engine does not report the scene boundaries needed to time it, the task still completes: the video is delivered without the overlay and the task result and webhook payload carry an intro_outro_warning field explaining what happened.Scene Duration Overrides
For fine-tuned control over video pacing, you can override the default scene durations.number
Minimum duration for each scene in seconds. Range: 1-30.
number
Maximum duration for each scene in seconds. Range: 1-30.
Artistic Mode Duration Overrides
When usingorchestration_mode: "artistic", you can separately control hero and secondary scene durations.
number
Minimum duration for hero (primary) scenes in seconds. Range: 1-30.
number
Maximum duration for hero (primary) scenes in seconds. Range: 1-30.
number
Minimum duration for secondary scenes in seconds. Range: 1-30.
number
Maximum duration for secondary scenes in seconds. Range: 1-30.
Test Mode
string
default:"success"
Sandbox-only scenario selector. Honored only for test-mode (
vpk_test_) keys and silently ignored for live keys, so it is safe to leave in shared request-building code.| Value | Outcome |
|---|---|
success | Synthetic task completes with a watermarked sample video |
fail | Synthetic task fails with a sanitized, public-safe error |
slow | Like success, but completes after a noticeably longer delay |
Test mode never spends credits or runs the real pipeline. See Test Mode for the full sandbox model.
Response
string
required
Unique identifier for the task. Use this to check status.
string
required
Initial task status. Always
queued for new tasks.integer
Position in the processing queue.
string
Estimated completion time (ISO 8601 format).
string
required
Task creation timestamp (ISO 8601 format).
boolean
required
true for live (vpk_live_) requests; false for test-mode (vpk_test_) requests. Use this to tell a real task from a sandbox one. See Test Mode.curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
"https://example.com/property/living-room.jpg",
"https://example.com/property/kitchen.jpg",
"https://example.com/property/bedroom.jpg",
"https://example.com/property/bathroom.jpg",
"https://example.com/property/backyard.jpg",
"https://example.com/property/exterior.jpg"
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"orientation": "portrait",
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.",
"orchestration_mode": "standard",
"webhook_url": "https://yourserver.com/webhooks/vibepeak",
"background_music": true,
"subtitle": {
"subtitle_style_preset": "classic"
},
"elements_config": {
"enabled": true,
"watermark": {
"url": "https://example.com/logo.png",
"position": "bottom-right",
"size": 20,
"opacity": 100
},
"text": {
"content": "Powered by Vibepeak",
"color": "#FFFFFF",
"font": "Inter",
"size": 20,
"position": "center"
}
}
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
orientation: 'portrait',
script: 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
orchestration_mode: 'standard',
webhook_url: 'https://yourserver.com/webhooks/vibepeak',
background_music: true,
subtitle: {
subtitle_style_preset: 'classic'
},
elements_config: {
enabled: true,
watermark: {
url: 'https://example.com/logo.png',
position: 'bottom-right',
size: 20,
opacity: 100
},
text: {
content: 'Powered by Vibepeak',
color: '#FFFFFF',
font: 'Inter',
size: 20,
position: 'center'
}
}
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
json={
'images': [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'orientation': 'portrait',
'script': 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
'orchestration_mode': 'standard',
'webhook_url': 'https://yourserver.com/webhooks/vibepeak',
'background_music': True,
'subtitle': {
'subtitle_style_preset': 'classic'
},
'elements_config': {
'enabled': True,
'watermark': {
'url': 'https://example.com/logo.png',
'position': 'bottom_right',
'size': 20,
'opacity': 100
},
'text': {
'content': 'Powered by Vibepeak',
'color': '#FFFFFF',
'font': 'Inter',
'size': 20,
'position': 'center'
}
}
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
Example: images with camera motion
To apply subtle per-image camera moves, passimages as an array of objects with reanimate: true and a movement value (or null to let VibePeak pick one). Remember you cannot mix strings and objects in the same array.
curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
{ "image_url": "https://example.com/property/living-room.jpg", "reanimate": true, "movement": "dolly_in", "room_type": "living room" },
{ "image_url": "https://example.com/property/kitchen.jpg", "reanimate": true, "movement": "traveling_left" },
{ "image_url": "https://example.com/property/bedroom.jpg", "reanimate": true, "movement": null },
{ "image_url": "https://example.com/property/bathroom.jpg", "reanimate": false },
{ "image_url": "https://example.com/property/backyard.jpg", "reanimate": true, "movement": "traveling_up" },
{ "image_url": "https://example.com/property/exterior.jpg", "reanimate": true, "movement": "dolly_out" }
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day."
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
{ image_url: 'https://example.com/property/living-room.jpg', reanimate: true, movement: 'dolly_in', room_type: 'living room' },
{ image_url: 'https://example.com/property/kitchen.jpg', reanimate: true, movement: 'traveling_left' },
{ image_url: 'https://example.com/property/bedroom.jpg', reanimate: true, movement: null },
{ image_url: 'https://example.com/property/bathroom.jpg', reanimate: false },
{ image_url: 'https://example.com/property/backyard.jpg', reanimate: true, movement: 'traveling_up' },
{ image_url: 'https://example.com/property/exterior.jpg', reanimate: true, movement: 'dolly_out' }
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
script: 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.'
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
json={
'images': [
{'image_url': 'https://example.com/property/living-room.jpg', 'reanimate': True, 'movement': 'dolly_in', 'room_type': 'living room'},
{'image_url': 'https://example.com/property/kitchen.jpg', 'reanimate': True, 'movement': 'traveling_left'},
{'image_url': 'https://example.com/property/bedroom.jpg', 'reanimate': True, 'movement': None},
{'image_url': 'https://example.com/property/bathroom.jpg', 'reanimate': False},
{'image_url': 'https://example.com/property/backyard.jpg', 'reanimate': True, 'movement': 'traveling_up'},
{'image_url': 'https://example.com/property/exterior.jpg', 'reanimate': True, 'movement': 'dolly_out'}
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'script': 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.'
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
Example: mosaic watermark
Setelements_config.watermark.mode to mosaic to tile the logo across the whole frame instead of placing it once at position. position and size are ignored in mosaic mode; rotation controls the tile angle (-45 to 45, default -20).
curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
"https://example.com/property/living-room.jpg",
"https://example.com/property/kitchen.jpg",
"https://example.com/property/bedroom.jpg",
"https://example.com/property/bathroom.jpg",
"https://example.com/property/backyard.jpg",
"https://example.com/property/exterior.jpg"
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.",
"elements_config": {
"enabled": true,
"watermark": {
"url": "https://example.com/logo.png",
"opacity": 60,
"mode": "mosaic",
"rotation": -20
}
}
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
script: 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
elements_config: {
enabled: true,
watermark: {
url: 'https://example.com/logo.png',
opacity: 60,
mode: 'mosaic',
rotation: -20
}
}
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
json={
'images': [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'script': 'Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light all day long. The kitchen pairs sleek cabinetry with generous counter space, and each bedroom offers a calm retreat at the end of the day.',
'elements_config': {
'enabled': True,
'watermark': {
'url': 'https://example.com/logo.png',
'opacity': 60,
'mode': 'mosaic',
'rotation': -20
}
}
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
Example: intro and outro overlays
To open and close the video with branded overlay cards, add anintro_outro block to elements_config. The block is independent of the top-level enabled flag, so you can request overlays without a watermark or text overlay. Omit intro or outro to apply only one of the two.
This example also paints both cards in an agency’s colors. brandColorPrimary is set on each overlay separately, so the intro and the outro could just as easily carry different colors or only one of them could be branded. All 27 designs take both colors — see what each one repaints. The one design that repaints nothing is the custom_image outro, which has no type of its own.
curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
"https://example.com/property/living-room.jpg",
"https://example.com/property/kitchen.jpg",
"https://example.com/property/bedroom.jpg",
"https://example.com/property/bathroom.jpg",
"https://example.com/property/backyard.jpg",
"https://example.com/property/exterior.jpg"
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light...",
"elements_config": {
"enabled": false,
"intro_outro": {
"enabled": true,
"intro": {
"templateId": "ruled_caps",
"templateVersion": 1,
"animation": "fade",
"values": { "title": "Sunset Villa", "subtitle": "Marbella, Spain" },
"assets": {},
"brandColorPrimary": "#1A73E8",
"brandColorContrast": "#FFFFFF"
},
"outro": {
"templateId": "serif_business_card",
"templateVersion": 1,
"animation": "slide_up",
"values": { "agent_name": "Alex Rivera", "contact_line": "[email protected] · +34 600 000 000" },
"assets": { "agent_photo": "https://example.com/agent-photo.jpg" },
"brandColorPrimary": "#1A73E8"
}
}
}
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
script: 'Welcome to this stunning 3-bedroom home...',
elements_config: {
enabled: false,
intro_outro: {
enabled: true,
intro: {
templateId: 'ruled_caps',
templateVersion: 1,
animation: 'fade',
values: { title: 'Sunset Villa', subtitle: 'Marbella, Spain' },
assets: {},
brandColorPrimary: '#1A73E8',
brandColorContrast: '#FFFFFF'
},
outro: {
templateId: 'serif_business_card',
templateVersion: 1,
animation: 'slide_up',
values: { agent_name: 'Alex Rivera', contact_line: '[email protected] · +34 600 000 000' },
assets: { agent_photo: 'https://example.com/agent-photo.jpg' },
brandColorPrimary: '#1A73E8'
}
}
}
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
json={
'images': [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'script': 'Welcome to this stunning 3-bedroom home...',
'elements_config': {
'enabled': False,
'intro_outro': {
'enabled': True,
'intro': {
'templateId': 'ruled_caps',
'templateVersion': 1,
'animation': 'fade',
'values': {'title': 'Sunset Villa', 'subtitle': 'Marbella, Spain'},
'assets': {},
'brandColorPrimary': '#1A73E8',
'brandColorContrast': '#FFFFFF'
},
'outro': {
'templateId': 'serif_business_card',
'templateVersion': 1,
'animation': 'slide_up',
'values': {'agent_name': 'Alex Rivera', 'contact_line': '[email protected] · +34 600 000 000'},
'assets': {'agent_photo': 'https://example.com/agent-photo.jpg'},
'brandColorPrimary': '#1A73E8'
}
}
}
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
Example: custom image outro
To close on a flyer, a floor plan or any image of your own instead of a contact card, pick thecustom_image outro. It takes one asset and no text, so values stays empty, and it rides the same closing window as every other outro: it adds no duration to the video.
If the image’s aspect ratio does not match the video’s, the server extends its background out to the video’s shape before rendering, so the image is never cropped. That runs while this request is being accepted, which is why the POST below can take a few seconds, and up to about 25 seconds when the ratios differ. The image must be a JPG, PNG or WEBP of at most 8 MB whose shortest edge is at least 400 px, and it costs no extra credits.
curl -X POST https://api.vibepeak.ai/v1/real-estate/narrated-slideshow \
-H "Authorization: Bearer vpk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"images": [
"https://example.com/property/living-room.jpg",
"https://example.com/property/kitchen.jpg",
"https://example.com/property/bedroom.jpg",
"https://example.com/property/bathroom.jpg",
"https://example.com/property/backyard.jpg",
"https://example.com/property/exterior.jpg"
],
"voice": {
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "en"
},
"orientation": "portrait",
"script": "Welcome to this stunning 3-bedroom home in the heart of the city. The open-concept living area floods with natural light...",
"elements_config": {
"enabled": false,
"intro_outro": {
"enabled": true,
"outro": {
"templateId": "custom_image",
"templateVersion": 1,
"animation": "fade",
"values": {},
"assets": { "custom_image": "https://example.com/listing-flyer.jpg" }
}
}
}
}'
const response = await fetch('https://api.vibepeak.ai/v1/real-estate/narrated-slideshow', {
method: 'POST',
headers: {
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
images: [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
voice: {
voice_id: 'EXAVITQu4vr4xnSDxMaL',
language: 'en'
},
orientation: 'portrait',
script: 'Welcome to this stunning 3-bedroom home...',
elements_config: {
enabled: false,
intro_outro: {
enabled: true,
outro: {
templateId: 'custom_image',
templateVersion: 1,
animation: 'fade',
values: {},
assets: { custom_image: 'https://example.com/listing-flyer.jpg' }
}
}
}
})
});
const task = await response.json();
console.log(`Task created: ${task.task_id}`);
import requests
response = requests.post(
'https://api.vibepeak.ai/v1/real-estate/narrated-slideshow',
headers={
'Authorization': 'Bearer vpk_live_xxxxx',
'Content-Type': 'application/json'
},
timeout=60,
json={
'images': [
'https://example.com/property/living-room.jpg',
'https://example.com/property/kitchen.jpg',
'https://example.com/property/bedroom.jpg',
'https://example.com/property/bathroom.jpg',
'https://example.com/property/backyard.jpg',
'https://example.com/property/exterior.jpg'
],
'voice': {
'voice_id': 'EXAVITQu4vr4xnSDxMaL',
'language': 'en'
},
'orientation': 'portrait',
'script': 'Welcome to this stunning 3-bedroom home...',
'elements_config': {
'enabled': False,
'intro_outro': {
'enabled': True,
'outro': {
'templateId': 'custom_image',
'templateVersion': 1,
'animation': 'fade',
'values': {},
'assets': {'custom_image': 'https://example.com/listing-flyer.jpg'}
}
}
}
}
)
task = response.json()
print(f"Task created: {task['task_id']}")
{
"task_id": "task_abc123xyz",
"status": "queued",
"queue_position": 3,
"estimated_completion": "2026-01-04T12:30:00Z",
"created_at": "2026-01-04T12:00:00Z",
"livemode": true
}
{
"task_id": "task_abc123xyz",
"status": "queued",
"message": "Video generation request accepted",
"created_at": "2026-01-04T12:00:00Z",
"livemode": false,
"request_id": "req_xyz123",
"_links": {
"self": "/v1/tasks/task_abc123xyz",
"poll_interval_seconds": 30
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "images must contain between 6 and 15 URLs",
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Provide either 'subtitle_style_preset' or 'styles', but not both.",
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "SCRIPT_TOO_SHORT",
"message": "Script is too short for 6 images. Minimum 216 characters required (36 per image), but got 100.",
"request_id": "req_xyz123",
"details": {
"script_length": 100,
"min_length": 216,
"max_length": 306,
"image_count": 6
}
}
}
{
"error": {
"code": "SCRIPT_TOO_LONG",
"message": "Script is too long for 6 images. Maximum 306 characters allowed (51 per image), but got 500.",
"request_id": "req_xyz123",
"details": {
"script_length": 500,
"min_length": 216,
"max_length": 306,
"image_count": 6
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "If elements_config is enabled, you must provide at least a 'watermark' or 'text' configuration.",
"request_id": "req_xyz123",
"details": {
"field": "elements_config.enabled",
"issues": [
{
"path": "elements_config.enabled",
"message": "If elements_config is enabled, you must provide at least a 'watermark' or 'text' configuration."
}
]
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid hex color",
"request_id": "req_xyz123",
"details": {
"field": "elements_config.text.color",
"issues": [
{
"path": "elements_config.text.color",
"message": "Invalid hex color"
}
]
}
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "If intro_outro is enabled, you must provide at least an 'intro' or 'outro' configuration.",
"details": {
"field": "elements_config.intro_outro.enabled",
"issues": [
{
"path": "elements_config.intro_outro.enabled",
"message": "If intro_outro is enabled, you must provide at least an 'intro' or 'outro' configuration."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Unknown intro template id. Choose one of the documented intro templates; GET /v1/intro-outro/templates lists them.",
"details": {
"field": "elements_config.intro_outro.intro.templateId",
"issues": [
{
"path": "elements_config.intro_outro.intro.templateId",
"message": "Unknown intro template id. Choose one of the documented intro templates; GET /v1/intro-outro/templates lists them."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Brand color must be a six-digit hex string such as #1A73E8, with no alpha channel",
"details": {
"field": "elements_config.intro_outro.intro.brandColorPrimary",
"issues": [
{
"path": "elements_config.intro_outro.intro.brandColorPrimary",
"message": "Brand color must be a six-digit hex string such as #1A73E8, with no alpha channel"
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "brandColorContrast requires brandColorPrimary on the same overlay.",
"details": {
"field": "elements_config.intro_outro.outro.brandColorContrast",
"issues": [
{
"path": "elements_config.intro_outro.outro.brandColorContrast",
"message": "brandColorContrast requires brandColorPrimary on the same overlay."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "title is required by template 'ruled_caps'. Send it in values.title: an omitted field renders nothing.",
"details": {
"field": "elements_config.intro_outro.intro.values.title",
"issues": [
{
"path": "elements_config.intro_outro.intro.values.title",
"message": "title is required by template 'ruled_caps'. Send it in values.title: an omitted field renders nothing."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "agent_name is required by template 'serif_business_card'. Send it in values.agent_name: an omitted field renders nothing.",
"details": {
"field": "elements_config.intro_outro.outro.values.agent_name",
"issues": [
{
"path": "elements_config.intro_outro.outro.values.agent_name",
"message": "agent_name is required by template 'serif_business_card'. Send it in values.agent_name: an omitted field renders nothing."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Template 'custom_image' requires assets.custom_image: the URL of the image it renders.",
"details": {
"field": "elements_config.intro_outro.outro.assets.custom_image",
"issues": [
{
"path": "elements_config.intro_outro.outro.assets.custom_image",
"message": "Template 'custom_image' requires assets.custom_image: the URL of the image it renders."
}
]
},
"request_id": "req_xyz123"
}
}
{
"error": {
"code": "SCRIPT_INVALID_CHARACTERS",
"message": "Script contains characters not supported by text-to-speech: \"🏠\". Emojis and other non-speech symbols are not allowed.",
"request_id": "req_xyz123",
"details": {
"invalid_characters": ["3", "2"]
}
}
}
{
"error": {
"code": "CONCURRENCY_LIMIT_EXCEEDED",
"message": "You have reached your concurrent task limit of 1. Please wait for existing tasks to complete.",
"request_id": "req_xyz123",
"details": {
"limit": 1,
"in_flight": 1,
"plan": "Plus"
}
}
}
Error Codes
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Invalid request parameters |
INVALID_IMAGE_URL | 400 | One or more image URLs are inaccessible or invalid |
INVALID_WATERMARK_URL | 400 | The elements_config watermark URL is inaccessible or does not return an image |
INVALID_WATERMARK_GEOMETRY | 400 | The elements_config watermark is enabled but too small or too faint to render (size ≥ 2 and opacity ≥ 5; size is not checked when mode is mosaic). No credits are charged |
VALIDATION_ERROR | 400 | A required intro/outro field is missing or blank: values.title on an intro, values.agent_name on a contact-card outro, assets.custom_image on the custom_image outro. details.field carries the dotted path and details.issues every refused field |
INVALID_PARAMETER | 400 | The custom_image outro image is not a JPG, PNG or WEBP of at most 8 MB with a shortest edge of at least 400 px |
SCRIPT_TOO_SHORT | 400 | Script is too short for the number of images (min 36 chars/image) |
SCRIPT_TOO_LONG | 400 | Script is too long for the number of images (max 51 chars/image) |
SCRIPT_INVALID_CHARACTERS | 400 | Script contains emojis or other non-speech characters not supported by TTS |
INVALID_API_KEY | 401 | Invalid or missing API key |
PLAN_REQUIRED | 403 | Plan doesn’t include API access |
CONCURRENCY_LIMIT_EXCEEDED | 429 | Concurrent task limit reached |
SERVICE_UNAVAILABLE | 503 | Video generation service temporarily unavailable |
INTRO_OUTRO_ADAPTATION_UNAVAILABLE | 503 | The custom_image outro could not be prepared. Retryable: no task was created and no credits were charged |
VOICE_REACTIVATION_UNAVAILABLE | 503 | A long-idle cloned voice was being restored and the restore did not complete in time. Retryable: no task was created and no credits were charged |
SERVICE_TIMEOUT | 504 | Service request timed out |
Next Steps
After creating a task:- Poll for status: Use Get Task to check progress
- Wait for webhook: If configured, receive notification when complete
- Download video: Access the video URL from the completed task result

