Skip to main content
This script uploads a product photo, starts google/veo-3.1-fast with the photo as the clip’s first frame, long-polls until the clip is done and saves it as product.mp4. The clip is 4 seconds, vertical (9:16) and has sound. It costs $0.66 and usually takes one to three minutes. You need an API key in LEAP_API_KEY and a photo named product.jpg, at most 4 MB, in the folder you run the script from. Veo crops the photo to the video’s shape, so a tall photo works best. To shrink a large photo, see Upload an image. To run the Node.js version, save it as photo-to-video.mjs and run node photo-to-video.mjs (Node 18 or later). For the Python version, run pip install requests, save it as photo_to_video.py and run python3 photo_to_video.py.

How it works

  1. POST /v1/files takes the photo itself as the request body, with no multipart encoding, and answers with a file whose id starts with file_. A PNG, JPEG or WebP image up to 4 MB works.
  2. POST /v1/generations starts the clip. first_frame takes the file’s ID; models take files by ID, never by URL. The schema of google/veo-3.1-fast allows a duration of 4, 6 or 8 seconds and an aspect_ratio of 16:9 or 9:16. The idempotency key makes a retry of this one request safe: send the same key again and you get the same generation back. See Retry safely.
  3. The script long-polls the generation: each read with prefer: wait=60 answers as soon as the status is succeeded, failed or canceled, or after 60 seconds. If it gives up, the run carries on, and you can read it again by its ID. For a loop that also rides out dropped connections and 429s, see Long-poll.
  4. output[0].url is a signed link that works for 24 hours without a key. A large file redirects to storage; fetch and requests follow the redirect on their own.
A failed run isn’t charged, and error.message says why. If the model’s safety filter turned the photo down, try another photo. See When a run fails.

Change it

Get a quote to check any input and its price before you run it.
Last modified on October 4, 2026