Video

Video & voiceover API

Generate listing videos from photos, then edit them without re-rendering, add AI voiceovers with synced subtitles, and pick background music — all from synchronous JSON calls.

POST/api/create_video

Builds an MP4 video from a list of images with motion effects, optional subtitles, background music, property characteristic overlays, and ending cards. The endpoint is synchronous: it blocks until the video is rendered, then returns HTTP 200 with the final videoUrl.

Walkthrough video examples
Watch real listing videos generated from a handful of photos — vertical and horizontal.
See examples →

Synchronous — set a generous client timeout

The endpoint blocks while the video renders, then returns the playable URL. Rendering can take several minutes, so set your HTTP client timeout generously — the default of 30–60s on most HTTP libraries will hang up before Pedra responds.

Top-level parameters

apiKeystringrequired
Your API key.
imagesarrayrequired
Ordered list of image objects. Each becomes a frame/clip in the video. See "Image object" below.
isVerticalboolean
9:16 vertical format for Instagram Reels / TikTok. Default is 16:9 horizontal.
Default: false
propertyCharacteristicsarray
Array of {label, value} pairs (Bedrooms, Bathrooms, Surface, Price, etc). Shown by default on every frame when provided; set characteristics.enabled = false on an image to hide it there.
musicobject
Background music. Object: { enabled: boolean, track: string }.
voiceobject
Voiceover. Object: { enabled: boolean, audioId: string, showSubtitles: boolean }. Generate the audioId with /api/generate_voice. See the Voice section below.
brandingobject
Custom logo, agent photo, and brand color overlay. See "Branding" section.
endingTitlestring
Headline displayed on the final card.
endingSubtitlestring
Subtitle on the final card (typically a CTA or contact info).
propertyIdstring
Optional id of the property (from list_properties or create_property) these photos belong to. When set, the finished video is saved into that property — visible in its Videos tab and editable from the app — instead of only returned as a URL.

Image object

Each entry in images describes one frame:

imageUrlstringrequired
URL of the photo (or base64 data URI).
effectstringrequired
Motion effect applied to this frame.
Values: zoom-in zoom-out static transition
secondImageUrlstring
Required when effect = "transition". The image to transition to.
subtitlestring
Caption shown over this frame.
titlestring
Large title overlay (e.g., "Living Room", "Kitchen").
watermarkobject
Watermark config: { enabled, position, opacity }. Disabled by default. Set { enabled: true } to apply your watermark (bottom-right, opacity 1.0 by default).
characteristicsobject
Show/hide the propertyCharacteristics overlay on this frame. Defaults to enabled when propertyCharacteristics is provided; set { enabled: false } to hide.

Watermark options

enabledboolean
Show the Pedra watermark.
Default: true
positionstring
Where the watermark appears.
Values: top-left top top-right left center right bottom-left bottom bottom-right
Default: bottom-right
opacitynumber
0.0 (invisible) to 1.0 (fully opaque).
Default: 1

Music tracks

enabledbooleanrequired
Turn background music on.
trackstringrequired
Music genre. One of the keys from /api/music_library. Legacy values (calm, uplifting, corporate, piano) are still accepted.
Values: acoustic chill cinematic electronic upbeat

Voiceover

enabledbooleanrequired
Turn the voiceover on.
audioIdstringrequired
Id of a voiceover rendered with /api/generate_voice. This is the handle that attaches the narration and drives the synced subtitles.
showSubtitlesboolean
Burn in word-synced subtitles from the voiceover.
Default: true

Branding

showWatermarkboolean
Show your custom logo as a watermark instead of Pedra's.
watermarkUrlstring
URL to your logo (PNG with transparency recommended).
showProfessionalPictureboolean
Display the agent's photo on the ending card.
professionalPictureUrlstring
URL to the agent's headshot.
primaryColorstring
Brand color used for overlays, as a hex string (e.g., "#007BFF"). Defaults to white (#ffffff).
Default: #ffffff

Example: minimal video

curl -X POST https://app.pedra.ai/api/create_video \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "images": [
      { "imageUrl": "https://example.com/img1.jpg", "effect": "zoom-in" },
      { "imageUrl": "https://example.com/img2.jpg", "effect": "zoom-out" }
    ],
    "isVertical": false,
    "endingTitle": "Contact us!",
    "endingSubtitle": "Visit our website"
  }'

Example: full production listing video

3-frame walkthrough with mixed effects (zoom-in / zoom-out / transition / static), branded watermark + agent photo + brand color, music, voiceover, subtitles per frame, property characteristics overlay, and ending card. Matches the typical real-world payload pattern Pedra's own app uses.

curl -X POST https://app.pedra.ai/api/create_video \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "images": [
      {
        "imageUrl": "https://example.com/exterior.jpg",
        "effect": "zoom-in",
        "subtitle": "Renovated 2-bedroom in Barcelona",
        "watermark": {"enabled": true, "position": "bottom-right", "opacity": 0.75},
        "characteristics": {"enabled": true}
      },
      {
        "imageUrl": "https://example.com/living-room.jpg",
        "effect": "zoom-out",
        "subtitle": "Bright, open-plan living space",
        "watermark": {"enabled": true, "position": "bottom-right", "opacity": 0.75}
      },
      {
        "imageUrl": "https://example.com/before.jpg",
        "secondImageUrl": "https://example.com/after.jpg",
        "effect": "transition",
        "title": "Before & After"
      }
    ],
    "isVertical": false,
    "propertyCharacteristics": [
      {"label": "Price", "value": "€450,000"},
      {"label": "Bedrooms", "value": "2"},
      {"label": "Bathrooms", "value": "1"},
      {"label": "Surface", "value": "85 m²"}
    ],
    "music": {"enabled": true, "track": "cinematic"},
    "voice": {"enabled": true, "audioId": "AUDIO_ID_FROM_GENERATE_VOICE", "showSubtitles": true},
    "branding": {
      "showWatermark": true,
      "watermarkUrl": "https://example.com/agency-logo.png",
      "showProfessionalPicture": true,
      "professionalPictureUrl": "https://example.com/agent-photo.jpg",
      "primaryColor": "#000000"
    },
    "endingTitle": "Felix Ingla",
    "endingSubtitle": "felix@pedra.ai"
  }'

Response

JSON
{
  "message": "Video created successfully",
  "videoId": "<uuid>",
  "videoUrl": "https://img.pedra.ai/<uuid>"
}

Credits

Each frame costs 5 credits — except frames with effect: "static", which are free. A 5-image video with all zoom-in/zoom-out effects deducts 25 credits. See Pricing.

Validation errors

HTTP 400 is returned for:

  • Missing or empty images array.
  • Any image missing imageUrl (error names the offending index).
  • effect not in [zoom-in, zoom-out, transition, static].
  • effect: "transition" without secondImageUrl.

Available characteristic labels

When using propertyCharacteristics, the following label keys render with matching icons in the overlay: Bedrooms, Bathrooms, Surface, Price, Location, Parking, Heating, Outdoor. Custom labels are accepted but render without an icon.

Update an existing video

POST/api/update_video

Edits a video you already created — by videoIdwithout re-rendering unchanged clips. Reordering, swapping music or voiceover, changing branding, ending cards, subtitles and titles all re-stitch for free. Only brand-new or changed photos re-animate (and cost credits). Like create_video, it is synchronous and returns the new videoUrl. If the video was created with a propertyId, that link is inherited automatically — no need to pass it again.

Patch semantics

Every field except videoId is optional. Omit images to keep the existing timeline and change only audio/text/branding (a free re-stitch). Omit music, voice, branding or the ending text to leave each at its stored value — editing the music never wipes the voiceover, and vice versa. When you do send images, a clip whose photo + effect (+ second photo for transitions) matches an existing one is reused as-is.

Top-level parameters

apiKeystringrequired
Your API key.
videoIdstringrequired
Id of the video to edit (the videoId returned by create_video).
imagesarray
Full ordered image list to rebuild the timeline; clips matching an existing photo + effect are reused (no re-render, no credits). Omit to keep the current timeline and edit only audio/text/branding.
musicobject
Background music. Object: { enabled: boolean, track: string }.
voiceobject
Voiceover. Object: { enabled: boolean, audioId: string, showSubtitles: boolean }. Generate the audioId with /api/generate_voice. See the Voice section below.
brandingobject
Custom logo, agent photo, and brand color overlay. See "Branding" section.
endingTitlestring
Headline displayed on the final card.
endingSubtitlestring
Subtitle on the final card (typically a CTA or contact info).
isVerticalboolean
9:16 vertical format for Instagram Reels / TikTok. Default is 16:9 horizontal.
propertyCharacteristicsarray
Array of {label, value} pairs (Bedrooms, Bathrooms, Surface, Price, etc). Shown by default on every frame when provided; set characteristics.enabled = false on an image to hide it there.

Example: change only the music

curl -X POST https://app.pedra.ai/api/update_video \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "videoId": "VIDEO_ID_FROM_CREATE",
    "music": { "enabled": true, "track": "upbeat" }
  }'

Example: reorder + swap one photo

curl -X POST https://app.pedra.ai/api/update_video \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "videoId": "VIDEO_ID_FROM_CREATE",
    "images": [
      { "imageUrl": "https://example.com/living-room.jpg", "effect": "zoom-out" },
      { "imageUrl": "https://example.com/exterior.jpg", "effect": "zoom-in" },
      { "imageUrl": "https://example.com/new-kitchen.jpg", "effect": "zoom-in" }
    ]
  }'

Response

JSON
{
  "message": "Video updated successfully",
  "videoId": "<uuid>",
  "videoUrl": "https://img.pedra.ai/<uuid>"
}

Credits

Reused clips cost nothing. Only new or changed non-static frames re-animate at 5 credits each. A pure music/voice/text edit is free. Note: videos created before clip-reuse shipped re-animate every clip on their first image-based edit.

Voiceover & audio

A two-step flow: write a script from the photos (or supply your own), render it to audio, then attach the returned audioId to a video's voice object. Subtitles are generated automatically from the voiceover's word timings.

Generate a voiceover script

POST/api/generate_voice_script

Writes a short, natural voiceover script from the listing photos and (optionally) property facts. Uses vision so the script reflects what's actually in the images. Returns the script text — pass it to /api/generate_voice, or edit it first.

Top-level parameters

apiKeystringrequired
Your API key.
imagesarray
Photos to base the script on — an array of URL strings or { imageUrl } objects. The first few are read with vision.
propertyCharacteristicsarray
Optional {label, value} property facts to weave into the script (Bedrooms, Surface, Price, etc).
languagestring
Script language. Defaults to English. See /api/music_library for the accepted values.
Default: English
curl -X POST https://app.pedra.ai/api/generate_voice_script \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "images": [
      "https://example.com/exterior.jpg",
      "https://example.com/living-room.jpg"
    ],
    "propertyCharacteristics": [
      { "label": "Bedrooms", "value": "2" },
      { "label": "Surface", "value": "85 m²" }
    ],
    "language": "English"
  }'

Response

JSON
{
  "message": "Script generated successfully",
  "script": "Welcome to this bright, renovated two-bedroom apartment. Step into a sunlit, open-plan living space with room to breathe across 85 square metres."
}

Script generation is free.

Generate a voiceover

POST/api/generate_voice

Renders a script to a voiceover audio track via text-to-speech. Returns an audioId — pass it to a video's voice.audioId in create_video or update_video to attach the narration (and its synced subtitles).

Top-level parameters

apiKeystringrequired
Your API key.
textstringrequired
The script to narrate. Max 1000 characters.
languagestring
Voice language (selects the speaker). Defaults to English. See /api/music_library for the accepted values.
Default: English
curl -X POST https://app.pedra.ai/api/generate_voice \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "text": "Welcome to this bright, renovated two-bedroom apartment.",
    "language": "English"
  }'

Response

JSON
{
  "message": "Voice generated successfully",
  "audioId": "<uuid>",
  "audioUrl": "https://img.pedra.ai/audio/<uuid>.mp3",
  "alignmentUrl": "https://img.pedra.ai/audio/<uuid>.alignment.json",
  "duration": 7
}

Credits

Each voiceover costs 1 credit. The text limit is 1000 characters.

List the music library

POST/api/music_library

Returns the valid music.track values (genre keys) with display labels, plus the languages accepted by the voiceover endpoints. Read-only — it never deducts credits and accepts GET or POST.

curl https://app.pedra.ai/api/music_library

Response

JSON
{
  "tracks": [
    { "track": "acoustic", "label": "Acoustic & Warm" },
    { "track": "chill", "label": "Chill Beats" },
    { "track": "cinematic", "label": "Cinematic & Orchestral" },
    { "track": "electronic", "label": "Modern Electronic" },
    { "track": "upbeat", "label": "Upbeat & Energetic" }
  ],
  "variantsPerTrack": 6,
  "defaultTrack": "chill",
  "voiceLanguages": ["English", "Español", "Français", "Deutsch", "Português", "Italiano", "Nederlands", "Polski"]
}

Next

See Errors & limits for video job failure modes and timeout behavior.