Gemini’s Veo 3.1 API: Generate AI Video in 13 Steps [2026]

Google’s Veo 3.1 video model has become the default pick for developers building AI video features into production apps, and the timing is not an accident. OpenAI shut down the Sora 2 models and the standalone Sora Videos API on September 24, 2026, according to OpenAI’s own developer documentation, which pulled one of the two major frontier-lab video APIs off the table overnight. That leaves Google’s Gemini API as the most accessible route to studio-grade, audio-synced AI video generation from a first-party lab, and this tutorial walks through every step needed to go from a blank terminal to a working, automated Veo 3.1 pipeline.

By the end of this guide you will have a Gemini API key, a working Python script that generates an 8-second video from a text prompt, a second script that generates video from a still image, and a batch automation pipeline with retry logic and cost tracking built in. We will also cover the pitfalls that trip up almost every developer on their first integration, plus a troubleshooting table for the errors you are most likely to hit. This is a hands-on build, not a marketing overview, so expect code in nearly every section.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What Is Google Veo 3.1, and Why It’s the API to Learn Right Now

Veo 3.1 is Google’s video generation model family, served through the Gemini API and Vertex AI. It ships in three variants: the standard model (model ID veo-3.1-generate-preview), a cheaper and faster variant (veo-3.1-fast-generate-preview), and a budget variant (veo-3.1-lite-generate-preview). All three generate short video clips with synchronized audio from either a text prompt or a reference image, which puts Veo in the same category as Runway’s Gen-4 line and ByteDance’s Seedance, but with the distribution advantage of sitting inside the same Gemini API that millions of developers already use for text and image generation.

Pricing and model access data gathered by Zerocoder’s Veo API integration guide and independently confirmed on BenchLM’s Veo 3.1 pricing breakdown both place the standard model at $0.40 per second for 720p and 1080p output, climbing to $0.60 per second at 4K. The Fast variant drops that to $0.10 per second at 720p, and the Lite variant goes as low as $0.05 per second at 720p. None of the three variants are available on a free tier, so every call you make while following this tutorial will post a small charge to your Google Cloud billing account. Budget a few dollars for testing before you touch production traffic.

Independent reviews are mixed on polish. Anchor Terminal’s 2026 review of Google Veo assigned the tool a middling grade, citing inconsistent adherence to complex prompts and occasional audio-sync drift on longer clips, even while praising the raw visual fidelity. That is a useful expectation to set before you start: Veo 3.1 is strong at short, visually simple shots and weaker at multi-subject scenes with precise dialogue timing. Keep your first prompts simple while you learn the mechanics of the API, then push complexity once the pipeline itself is solid.

Prerequisites: Accounts, Tools, and SDK Versions You’ll Need

Gather these before Step 1. Skipping ahead without them is the single most common reason this tutorial stalls halfway through.

  • A Google account with billing enabled on Google Cloud or Google AI Studio (Veo 3.1 has no free tier)
  • Python 3.10 or newer installed locally, verified with python3 --version
  • pip or a virtual environment tool (venv, poetry, or uv) to isolate dependencies
  • The official google-genai Python package, installed via pip (always pull the latest version rather than pinning to an old release, since Google revises the Veo API surface frequently)
  • A terminal or shell with environment variable support (bash, zsh, or PowerShell)
  • Roughly 500MB of free disk space for generated MP4 files during testing
  • Optional: ffmpeg installed locally if you plan to post-process or trim generated clips
  • Optional: a job queue or task runner (Celery, RQ, or a simple cron job) if you intend to follow the automation steps later in this guide

You do not need a Google Cloud Platform project with Vertex AI enabled for the Gemini API route this tutorial follows. The Gemini API path through AI Studio is the faster on-ramp, and it is what the official SDK defaults to when you call genai.Client() with an API key rather than a Cloud project ID.

Step 1 and 2: Create a Project and Enable the Gemini API

Open Google AI Studio in your browser and sign in with the Google account tied to the billing profile you want to use. If you already have an existing Google Cloud project you want to reuse, select it from the project switcher. Otherwise, create a new project specifically for this tutorial so that spend tracking stays clean and separate from any other workloads running under your account.

Confirm billing is attached to that project. Veo 3.1 calls will fail with an authorization or quota error if billing is not active, and that error message rarely names the real cause directly, so this is worth double-checking now rather than debugging it later. Once billing is confirmed, make sure the Gemini API is enabled for the project. In most cases AI Studio enables this automatically the first time you generate a key, but if you are working from an existing Cloud project, check the API library explicitly and enable it by hand.

Step 3: Generate and Secure Your Gemini API Key

From the AI Studio dashboard, open the API keys section and generate a new key scoped to your project. Copy it immediately, since the dashboard will mask it after you navigate away. Never paste this key directly into source code that gets committed to version control. Instead, store it as an environment variable named GEMINI_API_KEY, which is the variable name the official SDK reads by default when you initialize the client without an explicit key argument.

export GEMINI_API_KEY="your-key-here"

# Verify it loaded correctly in this shell session
echo $GEMINI_API_KEY

If you are on Windows using PowerShell, the equivalent command is $env:GEMINI_API_KEY="your-key-here". For anything beyond local testing, drop this into a .env file loaded by a library like python-dotenv, and add that file to .gitignore before your first commit. A leaked Gemini key gives whoever finds it the ability to run paid video generation jobs on your billing account, and video generation is expensive enough per call that this is not a theoretical risk.

Step 4: Install the Official google-genai SDK

Google consolidated its generative AI tooling into a single package called google-genai, which replaced the older, now-deprecated google-generativeai package. Make sure you are installing the current one, since tutorials and Stack Overflow answers referencing the old package name will give you import errors that look confusing if you do not know the package was renamed.

python3 -m venv veo-tutorial
source veo-tutorial/bin/activate  # On Windows: veo-tutorial\Scripts\activate

pip install --upgrade google-genai

Confirm the install succeeded by importing the package in a quick interactive check:

python3 -c "from google import genai; print('SDK import OK')"

If that command errors out with ModuleNotFoundError, your virtual environment is not activated, or the package installed into a different Python interpreter than the one you are running. Check which python3 and which pip to make sure they point to the same virtual environment path.

Step 5: Initialize the Client and Authenticate

With the environment variable set and the package installed, client initialization is a single line. The SDK reads GEMINI_API_KEY automatically, so you do not need to pass it explicitly unless you are managing multiple keys in one process.

from google import genai

# Reads GEMINI_API_KEY from the environment automatically
client = genai.Client()

# Or, if you need to pass the key explicitly:
# client = genai.Client(api_key="your-key-here")

This client object is what every subsequent call in this tutorial runs through. If you are building a web service rather than a script, initialize this client once at application startup and reuse it across requests rather than creating a new client per request, since repeated initialization adds unnecessary overhead on every call.

Step 6: Send Your First Text-to-Video Request

Video generation on Veo 3.1 is asynchronous. You do not get a video back from the initial call, you get an operation object that you poll until the job finishes. This surprises developers coming from text or image APIs that return a response synchronously, so build the mental model now: submit, poll, retrieve.

from google import genai

client = genai.Client()

operation = client.models.generate_videos(
    model="veo-3.1-fast-generate-preview",
    prompt="A cinematic drone shot flying over a quiet coastal town at golden hour, gentle waves, warm light",
)

print(operation)

We used veo-3.1-fast-generate-preview rather than the standard model here deliberately. It is roughly four times cheaper per second at 720p, which matters a great deal when you are iterating on prompts and expect to throw away most of your early test generations. Swap in veo-3.1-generate-preview once you have a prompt you are confident in and need the highest visual fidelity for a final render.

Step 7: Poll the Asynchronous Operation Until It Completes

The operation object has a done property that flips to true once generation finishes. Poll it on an interval, rather than hammering the API in a tight loop, both to be a good API citizen and to avoid burning through rate limit headroom on a feature that takes real wall-clock time to complete.

import time
from google import genai

client = genai.Client()

operation = client.models.generate_videos(
    model="veo-3.1-fast-generate-preview",
    prompt="A cinematic drone shot flying over a quiet coastal town at golden hour",
)

while not operation.done:
    print("Waiting for video generation to complete...")
    time.sleep(10)
    operation = client.operations.get(operation)

print("Generation finished.")

Typical output while this runs looks like this on a standard test clip:

Waiting for video generation to complete...
Waiting for video generation to complete...
Waiting for video generation to complete...
Waiting for video generation to complete...
Generation finished.

Generation time varies with model, resolution, and current load on Google’s infrastructure, so do not hardcode an assumption about how many polling cycles a job will take. Build your code to simply wait on operation.done rather than counting iterations, and add a sane upper-bound timeout so a stuck job does not poll forever in production.

Step 8: Download and Save the Generated MP4

Once operation.done is true, the finished video lives inside operation.response.generated_videos, an array that holds one entry per clip you requested. Download the underlying file object and write it to disk.

video = operation.response.generated_videos[0]

client.files.download(file=video.video)
video.video.save("coastal_town.mp4")

print("Saved coastal_town.mp4")

Play the file back locally before you wire this into any automated pipeline. Veo 3.1 clips run up to 8 seconds, carry synchronized audio by default, and arrive as standard MP4 containers that any video player or editing tool will open without conversion. If the file is zero bytes or will not open, the download call likely ran before the operation object was fully refreshed, which is almost always a sign the polling loop exited early. Double-check that your while loop condition is checking the freshly reassigned operation variable, not a stale reference captured before the last poll.

Step 9: Generate Video From a Still Image

Veo 3.1 also accepts an image as a starting frame, which is useful for product shots, brand assets, or any workflow where you want the video to begin from an exact piece of existing art rather than whatever Veo imagines from text alone. Load the image through the SDK’s upload helper and pass it alongside your prompt.

import time
from google import genai

client = genai.Client()

uploaded_image = client.files.upload(file="product_shot.png")

operation = client.models.generate_videos(
    model="veo-3.1-fast-generate-preview",
    prompt="Slowly rotate the product 360 degrees under soft studio lighting",
    image=uploaded_image,
)

while not operation.done:
    time.sleep(10)
    operation = client.operations.get(operation)

video = operation.response.generated_videos[0]
client.files.download(file=video.video)
video.video.save("product_rotation.mp4")

Image-to-video quality depends heavily on how clean the source image is. A product shot on a plain background with good lighting gives Veo a much easier starting point than a busy, cluttered photo, and you will see that reflected directly in how coherent the motion looks in the output.

Writing Prompts That Actually Work on Veo 3.1

Prompt wording matters more on a video model than it does on a text model, because a video has to resolve camera motion, subject motion, and scene continuity all at once, inside a single 8-second window. A prompt that reads naturally to a human but stacks three separate actions in one sentence tends to produce a clip where the model picks one action and ignores the rest, which lines up with the adherence issues flagged in independent reviews of the tool.

Structure your prompts around four elements, in roughly this order: the subject, the action, the camera movement, and the lighting or mood. A prompt like “a red fox walking through snow, slow tracking shot from the side, soft overcast light” gives the model one subject, one action, one camera instruction, and one lighting cue, and it holds up far better than a prompt that tries to describe a whole short story in one breath.

Keep a running log of which prompt phrasings produced usable output and which ones did not, tagged with the model variant and resolution you tested against, since Fast and Lite can behave slightly differently from Standard on the same wording. That log becomes the regression set referenced later in this guide, and it saves you from re-learning the same prompting lessons every time you pick the project back up after a few weeks away from it.

For dialogue or sound-driven scenes, describe the audio explicitly rather than assuming the model will infer it from visual context alone. Since Veo 3.1 generates audio and video together, a prompt that says “waves crashing, seagulls calling in the distance” gives the audio track something concrete to render, instead of leaving it to guess what a silent-looking beach shot should sound like.

Step 10: Choose a Model for Your Budget: Standard, Fast, or Lite

Picking the right model variant is the single biggest cost lever available to you, and it is worth understanding the full pricing table before you write any production code. Veo 3.1 bills per second of output, and the rate changes with both the model tier and the resolution you request.

Model720p / sec1080p / sec4K / secCost for an 8-second 1080p clip
Veo 3.1 Standard (veo-3.1-generate-preview)$0.40$0.40$0.60$3.20
Veo 3.1 Fast (veo-3.1-fast-generate-preview)$0.10$0.12$0.30$0.96
Veo 3.1 Lite (veo-3.1-lite-generate-preview)$0.05$0.08N/A$0.64

Those figures come directly from pricing data published by BenchLM’s Veo 3.1 cost breakdown, cross-referenced against Zerocoder’s integration documentation. The gap between tiers is large enough to change your architecture decisions outright. A product that generates thousands of preview clips a day for user-facing drafts should default to Lite or Fast, and reserve the Standard model for a final, explicitly user-triggered render, the same pattern most teams already use for draft versus final renders in image generation products.

Step 11 and 12: Automate Generation and Add Retry Logic

A single script that generates one video on demand is useful for learning the API, but most real products need to queue many requests, survive transient failures, and avoid retrying in a way that silently doubles your bill. Wrap the generate-and-poll logic in a function with explicit retry and timeout handling before you hand this off to a job queue or scheduled worker.

import time
from google import genai
from google.genai import errors

client = genai.Client()

def generate_video(prompt, model="veo-3.1-fast-generate-preview", max_wait_seconds=300, max_retries=3):
    for attempt in range(1, max_retries + 1):
        try:
            operation = client.models.generate_videos(model=model, prompt=prompt)
            waited = 0
            while not operation.done:
                if waited >= max_wait_seconds:
                    raise TimeoutError(f"Generation exceeded {max_wait_seconds}s timeout")
                time.sleep(10)
                waited += 10
                operation = client.operations.get(operation)
            return operation.response.generated_videos[0]
        except errors.APIError as e:
            print(f"Attempt {attempt} failed: {e}")
            if attempt == max_retries:
                raise
            time.sleep(5 * attempt)  # exponential-ish backoff

Note the backoff delay growing with each attempt rather than retrying immediately. A transient failure on Google’s side, a dropped connection, or a momentary quota spike all resolve themselves faster if you give the service a few seconds of breathing room instead of hammering it again right away. Also note that this function never silently swallows a final failure. After the last retry, it re-raises the exception so your calling code, queue system, or alerting pipeline actually learns that a job failed instead of assuming success.

Step 13: The Complete Working Project: A Batch Veo 3.1 Pipeline

Putting every prior step together, here is a complete, runnable batch pipeline that reads a list of prompts, generates a video for each one, tracks estimated spend, logs failures, and saves output files with predictable names. This is the shape of script most teams actually deploy behind a queue worker or a scheduled job.

import time
import json
from pathlib import Path
from google import genai
from google.genai import errors

MODEL = "veo-3.1-fast-generate-preview"
PRICE_PER_SECOND_720P = 0.10  # Fast tier, 720p -- update if you change model/resolution
CLIP_SECONDS = 8

client = genai.Client()
output_dir = Path("veo_output")
output_dir.mkdir(exist_ok=True)

def generate_one(prompt, filename, max_retries=3):
    for attempt in range(1, max_retries + 1):
        try:
            operation = client.models.generate_videos(model=MODEL, prompt=prompt)
            waited = 0
            while not operation.done:
                if waited >= 300:
                    raise TimeoutError("generation timed out after 300s")
                time.sleep(10)
                waited += 10
                operation = client.operations.get(operation)

            video = operation.response.generated_videos[0]
            client.files.download(file=video.video)
            video.video.save(str(output_dir / filename))
            return {"prompt": prompt, "file": filename, "status": "ok", "cost_estimate": PRICE_PER_SECOND_720P * CLIP_SECONDS}
        except (errors.APIError, TimeoutError) as e:
            print(f"[{filename}] attempt {attempt} failed: {e}")
            if attempt == max_retries:
                return {"prompt": prompt, "file": filename, "status": "failed", "error": str(e)}
            time.sleep(5 * attempt)

def run_batch(prompts):
    results = []
    total_cost = 0.0
    for i, prompt in enumerate(prompts, start=1):
        filename = f"clip_{i:03d}.mp4"
        result = generate_one(prompt, filename)
        results.append(result)
        if result["status"] == "ok":
            total_cost += result["cost_estimate"]
        print(f"[{i}/{len(prompts)}] {result['status']}: {filename}")

    with open(output_dir / "batch_report.json", "w") as f:
        json.dump({"results": results, "estimated_total_cost": round(total_cost, 2)}, f, indent=2)

    print(f"Batch complete. Estimated spend: ${total_cost:.2f}")
    return results

if __name__ == "__main__":
    prompts = [
        "A cinematic drone shot over a coastal town at golden hour",
        "Close-up of rain falling on a city window at night, neon reflections",
        "A hiker reaching a mountain summit at sunrise, wide shot",
    ]
    run_batch(prompts)

Running this against the sample prompts produces output like the following on a terminal:

[1/3] ok: clip_001.mp4
[2/3] ok: clip_002.mp4
[3/3] ok: clip_003.mp4
Batch complete. Estimated spend: $2.40

The batch_report.json file left behind gives you an auditable record of what ran, what it cost, and which jobs failed, which matters the moment someone asks why last month’s video generation line item on the Google Cloud bill is higher than expected. Swap the hardcoded prompts list for rows pulled from a database, a queue message, or a CSV upload, and you have the backbone of a real product feature.

Exposing Veo 3.1 Through a Web API Endpoint

Most teams do not call the Veo SDK directly from a frontend. Instead, they wrap it in a small backend endpoint that accepts a prompt, kicks off generation, and lets the client poll for status separately, which keeps the Gemini API key off the browser entirely and gives you a place to enforce spend limits and content checks before a single dollar gets spent. A minimal FastAPI version of that pattern looks like this.

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from google import genai
import uuid

app = FastAPI()
client = genai.Client()
jobs = {}  # swap for Redis or a database in production

class GenerateRequest(BaseModel):
    prompt: str
    model: str = "veo-3.1-fast-generate-preview"

@app.post("/generate")
def start_generation(req: GenerateRequest):
    if len(req.prompt) > 500:
        raise HTTPException(status_code=400, detail="prompt too long")
    operation = client.models.generate_videos(model=req.model, prompt=req.prompt)
    job_id = str(uuid.uuid4())
    jobs[job_id] = operation
    return {"job_id": job_id, "status": "submitted"}

@app.get("/status/{job_id}")
def check_status(job_id: str):
    operation = jobs.get(job_id)
    if operation is None:
        raise HTTPException(status_code=404, detail="unknown job_id")
    operation = client.operations.get(operation)
    jobs[job_id] = operation
    if not operation.done:
        return {"status": "processing"}
    video = operation.response.generated_videos[0]
    client.files.download(file=video.video)
    video.video.save(f"/tmp/{job_id}.mp4")
    return {"status": "complete", "file": f"/tmp/{job_id}.mp4"}

This split matters for a simple reason: generation can take well over a minute, and holding an HTTP connection open that long is fragile across load balancers, mobile networks, and browser tabs that get backgrounded. Returning a job ID immediately and letting the client poll /status on its own schedule is a far more resilient pattern than trying to make a single request-response cycle span the entire generation time. In a real deployment, replace the in-memory jobs dictionary with Redis or a database table, since an in-process dictionary disappears the moment your server restarts or you scale past a single instance.

Common Pitfalls When Building on the Veo 3.1 API

These are the mistakes that show up most often in early Veo 3.1 integrations, based on how the SDK and billing model actually behave.

  • Defaulting to the Standard model during development. Every test generation at $0.40 to $0.60 per second adds up fast when you are iterating on prompts dozens of times a day. Develop against Fast or Lite, and only switch to Standard for the final render.
  • Polling too aggressively. A tight loop with no sleep interval wastes requests against your quota and will not make generation finish any faster, since the bottleneck is server-side rendering time, not how fast you ask.
  • Hardcoding config field names from an old SDK version or a blog post. Google has revised the Veo request schema across releases. Before you ship a parameter name like a resolution or aspect ratio field to production, confirm it against the currently installed SDK rather than copying it from an older tutorial, since a renamed or removed field fails loudly in a way that is easy to misdiagnose under time pressure.
  • Committing the API key to source control. This happens constantly with hackathon and prototype code that later gets pushed to a public repository. Treat the key with the same care as a database password from day one.
  • Assuming generation time is constant. Wait times shift with model load, resolution, and whether you requested text-to-video or image-to-video. Timeout logic should be generous and configurable, not a fixed guess based on one successful test run.
  • Not budgeting for failed jobs that still have partial API usage. A job that times out mid-poll may still have consumed compute on Google’s side even if you never receive a usable video. Track failures in your billing reconciliation, not just successes.
  • Skipping content moderation on user-submitted prompts. If end users can type the prompt text, filter it the same way you would filter input to any generative model before it reaches the API, both for policy compliance and to avoid wasted spend on requests that were always going to be rejected.

Troubleshooting Guide: 8 Issues and Fixes

Here are the specific errors and symptoms developers run into most often, and the fix for each.

SymptomLikely causeFix
ModuleNotFoundError: No module named 'google.genai'Virtual environment not activated, or package installed under the wrong Python interpreterRun which python3 and which pip to confirm they match; reinstall inside the active venv
Authorization or permission error on every callBilling not enabled on the project, or the Gemini API not enabledCheck billing status in Google Cloud console and confirm the API is enabled for the exact project tied to your key
ImportError referencing google.generativeaiCode or a tutorial is using the deprecated older package nameUninstall the old package and install google-genai instead; update imports to from google import genai
Operation never flips to donePolling loop is checking a stale local variable instead of the reassigned operation objectConfirm you reassign operation = client.operations.get(operation) inside the loop, not just on the first call
Downloaded file is 0 bytes or will not playDownload called before the operation fully completed, or the save path is not writableConfirm operation.done is true before calling download, and check filesystem permissions on the output directory
Unexpectedly high bill for a small number of test clipsTesting against the Standard model instead of Fast or Lite, or generating 4K when 720p would doSwitch to veo-3.1-fast-generate-preview or veo-3.1-lite-generate-preview for development and iterate at 720p
Prompt produces video that ignores part of the instructionPrompt is too complex for the model’s current adherence, a known limitation flagged in independent reviewsSimplify the prompt to a single clear subject and action per clip rather than multiple simultaneous instructions
Job fails intermittently under load with no clear error textTransient service-side issue or momentary quota pressureWrap calls in retry logic with exponential backoff, as shown in the automation steps above, rather than treating the first failure as final

Advanced Tips for Scaling a Production Video Pipeline

Once the basic pipeline works, a handful of operational practices separate a demo script from something you can run unattended at scale. First, separate your cost-tracking logic from the generation logic entirely, and log every request with its model tier, resolution, and duration at the moment you submit it, not after it completes. That way a crashed worker process still leaves an accurate billing trail behind, rather than losing the record along with the in-flight job.

Second, put a hard spend cap in front of the pipeline at the application layer, independent of whatever limits Google exposes on the account itself. A counter that halts new submissions once a daily or hourly dollar threshold is hit is a cheap insurance policy against a bug that submits the same batch job in a retry loop a hundred times in a row.

Third, treat prompt engineering as a versioned asset, not a string buried in application code. Store your working prompt templates in a file or table you can diff and roll back, since small wording changes can shift output quality meaningfully on a model that is still, by independent accounts, inconsistent on complex instructions. Keep a small regression set of prompts you re-run whenever Google updates the model, so you catch quality or behavior drift before your users do.

Finally, build your queue worker to be idempotent. If a job is retried after a crash partway through, it should not re-bill the same generation twice. Tag each request with a unique job ID before submission, and check whether that ID already produced a completed file on disk or in object storage before calling the API again.

How Veo 3.1 Fits Into the AI Video Landscape After Sora 2’s Shutdown

The timing context matters for anyone deciding where to invest integration effort. OpenAI’s decision to retire the Sora 2 models and the Sora Videos API on September 24, 2026, documented directly in OpenAI’s model reference page, removed a direct frontier-lab competitor from the field. That leaves Google’s Veo 3.1 as the clearest first-party option for teams that want a major-lab video model without routing through a third-party aggregator, alongside independent players like Runway and ByteDance’s Seedance that serve the same space from outside the big-three model labs.

That consolidation is also tracked more broadly on release-timeline trackers such as PromptZone’s AI model release timeline, which logs how frequently the major labs ship and retire model variants. The pace is fast enough that any tutorial, including this one, should be read with an expectation that model IDs, pricing, and config schemas will shift again within months. Build your integration with that assumption baked in: isolate the model ID and pricing constants in one place in your codebase, so updating them later is a one-line change rather than a hunt through scattered hardcoded strings.

Independent, somewhat critical coverage like Anchor Terminal’s review is also worth reading in full before you commit a product roadmap to Veo exclusively. A grade that lands in the middle of the pack, rather than a glowing report, is a reasonable signal that today’s generation of AI video models, Veo included, still needs human review in the loop for anything customer-facing, rather than being treated as a fully automated, ship-it-unsupervised feature.

Testing Your Veo 3.1 Integration Before You Ship

Before any of this goes in front of real users, run it through a short checklist rather than trusting that one successful manual test means the whole pipeline is solid. Start by confirming your error paths actually fire: temporarily point the model string at a typo’d ID like veo-3.1-generate-wrong and confirm your retry and logging code catches that failure cleanly instead of crashing the whole process.

Next, test what happens when a user submits an empty prompt, an extremely long prompt, or a prompt in a language other than English, since Veo’s behavior and your own input validation both need to handle those cases gracefully rather than surfacing a raw stack trace to an end user. Confirm your spend-cap logic actually blocks a submission once the threshold is hit, not just that it logs a warning and lets the request through anyway.

Finally, verify your downloaded files play correctly across the actual devices your users will use, not just your development machine. A clip that renders fine in a desktop video player can still fail silently in a mobile web view if your frontend is not handling the MP4 container or codec the way you expect. Catching that in a test pass costs you a few minutes. Catching it after launch costs you a support queue full of confused users.

Frequently Asked Questions

Is there a free tier for the Veo 3.1 API?
No. Published pricing data confirms Veo 3.1, across the Standard, Fast, and Lite variants, bills per second of generated video with no free allotment. Budget for testing costs from your very first call.

What is the maximum video length Veo 3.1 can generate in one call?
Official documentation describes Veo 3.1 clips as running up to 8 seconds per generation. For longer sequences, generate multiple clips and stitch them together in a post-processing step, for example with ffmpeg.

Which model variant should I use for a cost-sensitive prototype?
veo-3.1-lite-generate-preview is the cheapest, starting at $0.05 per second at 720p, making it the right default while you are still iterating on prompts and have not committed to a final creative direction.

Does Veo 3.1 generate audio along with the video?
Yes. Veo 3.1 produces video with synchronized audio by default, which is part of why pricing is quoted per second of combined audio-visual output rather than for a silent video track alone.

Can I generate video from an image instead of a text prompt?
Yes. Upload the image through the SDK’s file upload helper and pass it as the image argument alongside your prompt to generate_videos, as shown in Step 9 above.

Why does my script hang instead of returning a video immediately?
Video generation is asynchronous by design. The initial call returns an operation object, not a finished video, and you must poll that operation until its done property is true before the video is available to download.

Is the older google-generativeai package still usable for Veo?
Treat it as deprecated. Google consolidated its generative AI SDK tooling into the google-genai package, and new tutorials, including this one, should be written and followed against that current package rather than the older name.

What happened to Sora’s video API, and does that affect Veo pricing?
OpenAI shut down the Sora 2 models and the Sora Videos API on September 24, 2026. There is no public evidence that this directly changed Veo 3.1 pricing, but it does reduce the number of major-lab alternatives available to developers choosing a first-party video generation API.

Nadia Dubois

Nadia Dubois

AI & Innovation Editor

Nadia Dubois is the AI & Innovation Editor at Tech Insider, where she tracks the rapid evolution of artificial intelligence, from foundation models to real-world enterprise deployment. She previously covered AI and startups for La Tribune and contributed to MIT Technology Review's European coverage. Nadia specializes in generative AI, AI regulation, and the intersection of technology and European industrial policy. She holds a dual degree in Computational Linguistics and Journalism from Sciences Po Paris.

View all articles