OpenAI shut down the Sora 2 Videos API on September 24, 2026, and it did not leave a replacement behind. The sora-2 and sora-2-pro endpoints are gone, and OpenAI’s own model page says there is no one-to-one substitute. If your app, agency workflow, or side project generated video through that API, you have a broken integration right now, not next quarter. This tutorial shows exactly how to stand up Google’s Veo 3.1 through the Gemini API as a working replacement, with real model IDs, real pricing, and a full Python project you can run today.
We will cover both text-to-video and image-to-video generation, walk through the three Veo 3.1 pricing tiers, compare per-second costs against Kling 3.0, and go through the async polling pattern the Gemini API uses for video jobs. By the end you will have a working script that generates a video from a prompt, downloads the MP4, and handles the errors that trip up most people on their first attempt.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Why Sora 2’s shutdown forces a migration right now
OpenAI’s Sora Videos API had a short public run, one we covered in detail when the shutdown was first announced. The company opened it to developers, then pulled it less than a year later, on September 24, 2026. According to OpenAI’s model deprecation page, both sora-2 and sora-2-pro model families, along with every dated snapshot, were removed from the API entirely. There is no fallback model at OpenAI that plugs into the same request shape. Anyone who built a production feature on top of that API needs a new provider, not a code tweak.
That leaves two realistic candidates for most teams, a gap covered in more depth in our Veo 3.1 vs Sora 2 vs Kling comparison: Google’s Veo 3.1, reachable through the Gemini API and Vertex AI, and Kuaishou’s Kling 3.0, reachable through Kling’s own API and several third-party aggregators. Google has the advantage of being folded into the same SDK and billing account many teams already use for Gemini text and image calls, which is why this tutorial builds the migration around Veo 3.1 first and treats Kling as the fallback option in Step 11.
The practical stakes are simple: a text-to-video call that used to hit sora-2 now needs to hit veo-3.1-generate-001 or one of its sibling models, with a different config object, different pricing math, and a different polling loop. None of the code is exotic, but if you have never called an asynchronous generation endpoint before, the pattern is different enough from a normal chat completion request that it is worth walking through carefully.
It also matters who gets caught by this. The Sora Videos API attracted three distinct groups of builders in 2026: marketing teams generating quick social clips through a thin wrapper app, indie developers who shipped Sora-backed features inside consumer apps, and agencies that had standardized an entire client-delivery pipeline on sora-2-pro‘s longer clip lengths. Each group faces a slightly different migration problem. The marketing-tool builders mostly need a drop-in replacement with similar pricing. The agencies built around Sora Pro’s longer native clips have the hardest transition, since Veo 3.1’s documented examples center on 8-second segments rather than the 20-second clips Sora 2 Pro supported, and that gap has to be closed by chaining generations rather than a single parameter change.
Prerequisites and versions
Before starting, confirm you have the following. Version numbers matter here because Google has iterated on both the SDK and the Veo model line multiple times in 2026.
- Python 3.10 or newer (3.11 recommended), or Node.js 20 LTS or newer if you prefer the JavaScript client
- A Google account with billing enabled on either AI Studio or a Google Cloud project – Veo 3.1 is not available on the Gemini API free tier
- The
google-genaiPython package (the unified Google Gen AI SDK, not the older, deprecatedgoogle-generativeaipackage) - A Gemini API key generated from Google AI Studio, or a Vertex AI service account if you are calling through Google Cloud instead
- ffmpeg installed locally if you plan to trim, transcode, or stitch the output clips (optional, but useful for step 10)
- At least $5 to $10 in billing headroom for testing – Veo 3.1 Fast at 720p runs $0.10 per second of video, so a batch of 8-second test clips adds up faster than a text API
Step 1: Get a Gemini API key with billing enabled
Go to Google AI Studio, open the API keys section, and generate a new key tied to a Google Cloud project that has billing enabled. Veo 3.1 will reject requests from a key on the free tier with a permission error, so this step is not optional the way it might be for text generation. If you already have a Gemini API key for chat or image calls, check whether that same project has billing turned on before assuming it will work for video.
Store the key as an environment variable rather than pasting it into your script:
export GEMINI_API_KEY="your-api-key-here"
The google-genai SDK automatically picks up GEMINI_API_KEY or GOOGLE_API_KEY from the environment, so you do not need to pass the key into every client call once this is set.
Step 2: Install the google-genai SDK
Create a virtual environment and install the current SDK. This is the package that exposes generate_videos, not the older library that most 2024-era tutorials reference. You can confirm the current release on the google-genai PyPI page before installing.
python3 -m venv veo-env
source veo-env/bin/activate
pip install --upgrade google-genai
If you are working in Node.js instead, the equivalent package is Google’s Gen AI JavaScript SDK, installed with npm install @google/genai. The rest of this tutorial uses Python, since the async polling pattern is easiest to follow there, but the request shape is nearly identical in both SDKs. If this is your first time setting up a Google generative model from scratch, our Gemini 3.8 Flash API setup tutorial walks through the equivalent process for text generation and uses the same authentication approach.
Step 3: Understand the three Veo 3.1 tiers before you write any code
Veo 3.1 ships in three tiers through the Gemini API, and picking the wrong one is the single most common way people blow through a testing budget. Here is what each one actually costs per second of generated video, based on Google’s published Gemini API pricing.
| Tier | Model ID | 720p price/sec | 1080p price/sec | 4K price/sec | Audio included |
|---|---|---|---|---|---|
| Veo 3.1 Lite | veo-3.1-lite-generate-001 | $0.05 | $0.08 | Not supported | Yes |
| Veo 3.1 Fast | veo-3.1-fast-generate-001 | $0.10 | $0.12 | $0.30 | Yes |
| Veo 3.1 Standard | veo-3.1-generate-001 | $0.40 | $0.40 | $0.60 | Yes |
Note the naming split: AI Studio’s Gemini API frequently exposes the preview builds as veo-3.1-generate-preview and veo-3.1-fast-generate-preview, while Vertex AI lists the general-availability IDs as veo-3.1-generate-001, veo-3.1-fast-generate-001, and veo-3.1-lite-generate-001. The Standard GA model, veo-3.1-generate-001, actually reached general availability on November 17, 2025, and Google ships it on the same one-year GA lifecycle it uses for other Vertex AI models, so check which surface you are calling before you hardcode a model string, since a preview ID on the wrong endpoint returns a 404 rather than falling back to the GA model.
All three tiers generate audio natively as part of the price, which is different from Veo 3.0’s separate audio toggle. An 8-second clip on Fast at 1080p costs roughly $0.96; the same clip on Standard costs about $3.20, and Standard’s 4K output still prices out at $0.60 per second under the Gemini API pricing data Google had current as of October 2026. For iteration and prompt testing, start on Lite or Fast. Reserve Standard for final renders.
Step 4: Write your first text-to-video request
This is the core pattern. Veo generation is asynchronous: you submit a job, get back an operation object, and poll it until the video is ready. There is no synchronous “wait and get the file back in one call” mode, so build the polling loop from the start rather than bolting it on later.
import time
from google import genai
from google.genai import types
client = genai.Client()
operation = client.models.generate_videos(
model="veo-3.1-fast-generate-001",
prompt="A drone shot rising over a foggy pine forest at sunrise, warm light breaking through the trees",
config=types.GenerateVideosConfig(
negative_prompt="text overlays, watermarks, shaky camera",
aspect_ratio="16:9",
resolution="1080p",
duration_seconds=8,
generate_audio=True,
),
)
while not operation.done:
time.sleep(20)
operation = client.operations.get(operation)
generated_video = operation.response.generated_videos[0]
client.files.download(file=generated_video.video)
generated_video.video.save("forest_sunrise.mp4")
print("Saved forest_sunrise.mp4")
A few details worth calling out. The client = genai.Client() line with no arguments works because it reads GEMINI_API_KEY from the environment you set in Step 1. The polling loop sleeps 20 seconds between checks; Google does not publish a fixed SLA for generation time, but 8-second clips at Fast tier typically finish inside one to two minutes, and Standard tier clips can run longer. Do not tighten the sleep interval to something like 2 seconds – you will burn through request quota checking a job that is not going to finish that fast.
Understanding the operation object before you build around it
The operation object returned by generate_videos is not a video, and treating it like one is the most common source of confusion for anyone new to this pattern. It is a long-running-operation handle, similar in concept to a job ID from a batch processing system. Three fields matter for almost every integration: operation.done, a boolean that flips to true once the job finishes in any state (success, failure, or safety block); operation.response, which is populated only after done is true and only if the job actually produced output; and operation.name, an internal identifier you can log for debugging if a job appears to hang.
Two mistakes recur constantly with this pattern. The first is checking operation.response before checking operation.done, which throws an attribute error because the response field doesn’t exist yet on an in-flight job. The second is assuming operation.done == True is equivalent to success – as covered in Step 8, a completed-but-blocked generation also sets done to true, just with an empty response. If you’re wiring this into a queue-based worker system rather than a single script, store the operation name alongside your job record so a crashed worker can reattach to a job that’s still processing on Google’s side instead of resubmitting and paying twice.
Step 5: Generate video from a starting image instead of a text prompt
Sora 2 users relied heavily on image-to-video generation, feeding in a still frame and letting the model animate it. Veo 3.1 supports the same workflow through the image parameter on GenerateVideosSource. This is the closest direct replacement for that Sora workflow.
from google import genai
from google.genai import types
import time
client = genai.Client()
starting_image = types.Image.from_file(location="product_shot.png")
operation = client.models.generate_videos(
model="veo-3.1-fast-generate-001",
prompt="The camera slowly orbits the product, studio lighting shifts from cool to warm",
image=starting_image,
config=types.GenerateVideosConfig(
aspect_ratio="9:16",
resolution="1080p",
duration_seconds=8,
),
)
while not operation.done:
time.sleep(20)
operation = client.operations.get(operation)
video = operation.response.generated_videos[0]
client.files.download(file=video.video)
video.save("product_orbit.mp4")
Per Google’s Veo documentation, the model also supports a last_frame parameter for frame interpolation, where you supply both a first and a last image and the model generates the motion between them. This did not exist in the same form on Sora 2 and is genuinely a step up for anyone doing product shots or transition sequences, since it removes most of the guesswork around what the model will do with an unconstrained end state.
Step 6: Set aspect ratio, resolution, and duration correctly
Veo 3.1 through the Gemini API supports 16:9 landscape and 9:16 portrait aspect ratios. There is no native square 1:1 option in the Gemini API surface, so if your feed requires square video, plan to crop after generation rather than requesting it directly. Duration is set through duration_seconds, with 8 seconds as the standard clip length used in Google’s own documentation examples; going shorter or requesting an unusually long single clip is not guaranteed to be accepted by every tier, so test with your exact combination before building it into a production pipeline.
Resolution and tier interact directly with price, which is where migration budgets get blown. A team moving from Sora 2 Pro’s 1080p output to Veo 3.1 without checking the resolution parameter can accidentally end up calling Standard tier at $0.40 to $0.60 per second when Fast tier at $0.10 to $0.30 per second would have looked visually identical for their use case. Always set resolution explicitly in the config rather than relying on a default.
Step 7: Calculate real cost before you scale up
Do this math before you wire Veo into a feature that runs unattended. An 8-second clip is the most common test unit, so here is what a batch of 100 clips costs across tiers and resolutions.
| Configuration | Cost per 8-sec clip | Cost per 100 clips |
|---|---|---|
| Veo 3.1 Lite, 720p | $0.40 | $40.00 |
| Veo 3.1 Fast, 720p | $0.80 | $80.00 |
| Veo 3.1 Fast, 1080p | $0.96 | $96.00 |
| Veo 3.1 Standard, 1080p | $3.20 | $320.00 |
| Veo 3.1 Standard, 4K | $4.80 | $480.00 |
Notice that jumping from Fast to Standard at 1080p roughly triples your cost per clip without changing resolution at all – the price difference is buying model quality, not pixels. Independent pricing trackers confirm this holds at the top end too: MagicHour reported in March 2026 that an 8-second Veo 3.1 Standard clip at 4K comes out to $4.80, matching the per-second math above. For most migration projects coming off Sora 2, Fast tier at 1080p is the closest match on both output quality and price, and it’s the tier used throughout this tutorial’s code examples.
Step 8: Handle safety filtering and blocked generations
Video generation models apply content filters before returning a result, and Veo 3.1 will decline prompts it flags rather than silently returning a lower-quality clip. When a generation is blocked, operation.response comes back without a populated generated_videos list, so check for that condition explicitly instead of assuming operation.done == True means you have a usable file:
while not operation.done:
time.sleep(20)
operation = client.operations.get(operation)
if not operation.response or not operation.response.generated_videos:
print("Generation was blocked or returned no output. Check the prompt for flagged content.")
else:
video = operation.response.generated_videos[0]
client.files.download(file=video.video)
video.save("output.mp4")
Prompts involving real public figures, copyrighted characters, or graphic violence are the most common triggers. If you are migrating a Sora 2 workflow, re-test your existing prompt library against Veo 3.1 rather than assuming a prompt that worked on one model will pass moderation on the other – the two models do not share a filter policy.
Step 9: Build a retry wrapper for rate limits and transient errors
Google does not publish a single universal quota number for Veo 3.1, since limits vary by project tier, billing history, and region. In practice, the most common failure mode during migration is submitting a burst of jobs faster than your project’s per-minute quota allows. Wrap the initial generate_videos call in a retry with backoff so a transient 429 does not kill an entire batch job:
import time
from google import genai
from google.genai import types
from google.genai import errors
client = genai.Client()
def generate_with_retry(prompt, model="veo-3.1-fast-generate-001", max_attempts=4):
for attempt in range(max_attempts):
try:
return client.models.generate_videos(
model=model,
prompt=prompt,
config=types.GenerateVideosConfig(
aspect_ratio="16:9",
resolution="1080p",
duration_seconds=8,
),
)
except errors.APIError as e:
wait = 2 ** attempt * 10
print(f"Attempt {attempt + 1} failed ({e}), retrying in {wait}s")
time.sleep(wait)
raise RuntimeError("Exceeded max retry attempts for video generation")
This is the same shape of retry logic that experienced teams already run in front of any third-party generation API, whether it is image, text, or video. The only Veo-specific detail is that a failed submission at this stage is cheap – you are only charged once generation actually completes, not for a rejected request.
Step 10: Post-process and store the output
Veo 3.1 returns standard MP4 files through the SDK’s file download helper. Once you have output.mp4 on disk, treat it like any other video asset. If you need to trim, watermark, or transcode for a specific platform, ffmpeg handles all of it locally without another API call:
# Trim to the first 5 seconds and re-encode for web delivery
ffmpeg -i output.mp4 -t 5 -c:v libx264 -crf 23 -c:a aac output_trimmed.mp4
Do not assume Google retains your generated video indefinitely on its servers. Download and store every clip in your own storage (S3, Cloud Storage, or local disk) immediately after generation completes, rather than treating the API as a hosting layer you can re-fetch from later.
Step 11: Set up Kling 3.0 as a second provider
Relying on a single video provider is exactly the mistake that made the Sora 2 shutdown so painful for teams that had no fallback. Kling 3.0 from Kuaishou is the most credible second option, and its published pricing undercuts Veo 3.1 Standard by a wide margin while landing above Veo 3.1 Fast.
| Model | 720p/sec | 1080p/sec | 4K/sec | Native audio |
|---|---|---|---|---|
| Kling 3.0 Standard (no audio) | $0.084 | $0.112 | $0.42 | No |
| Kling 3.0 Standard (with audio) | $0.126 | $0.168 | $0.42 | Yes |
| Kling 3.0 Turbo (with audio) | $0.112 | $0.14 | Not listed | Yes |
| Veo 3.1 Fast (for comparison) | $0.10 | $0.12 | $0.30 | Yes |
At 1080p with audio, Kling 3.0 Turbo’s $0.14 per second sits close to Veo 3.1 Fast’s $0.12 per second, close enough that the deciding factor should be output quality for your specific content type rather than price alone. If you already run a similar fallback pattern for still images, the setup mirrors our Grok Imagine API setup guide and its provider-swap logic almost exactly. Build your migration with a thin abstraction layer, a single function that accepts a prompt and returns a video path, with the actual provider call swapped based on a config flag. That is the difference between a five-minute failover and a weekend rewrite the next time a provider changes terms.
Step 12: Assemble the complete working project
Here is the full project structure. Save this as generate.py and it handles text-to-video, image-to-video, retries, and safety-block detection in one file:
import os
import time
import sys
from google import genai
from google.genai import types
from google.genai import errors
MODEL = "veo-3.1-fast-generate-001"
def get_client():
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
sys.exit("Set GEMINI_API_KEY before running this script.")
return genai.Client(api_key=api_key)
def submit_job(client, prompt, image_path=None, aspect_ratio="16:9", resolution="1080p"):
config = types.GenerateVideosConfig(
aspect_ratio=aspect_ratio,
resolution=resolution,
duration_seconds=8,
generate_audio=True,
)
kwargs = {"model": MODEL, "prompt": prompt, "config": config}
if image_path:
kwargs["image"] = types.Image.from_file(location=image_path)
for attempt in range(4):
try:
return client.models.generate_videos(**kwargs)
except errors.APIError as e:
wait = 2 ** attempt * 10
print(f"Submission failed ({e}), retrying in {wait}s")
time.sleep(wait)
raise RuntimeError("Could not submit generation job after 4 attempts")
def wait_for_result(client, operation, poll_interval=20):
while not operation.done:
time.sleep(poll_interval)
operation = client.operations.get(operation)
if not operation.response or not operation.response.generated_videos:
raise RuntimeError("Generation blocked or returned no output")
return operation.response.generated_videos[0]
def generate(prompt, output_path, image_path=None):
client = get_client()
operation = submit_job(client, prompt, image_path=image_path)
video = wait_for_result(client, operation)
client.files.download(file=video.video)
video.save(output_path)
print(f"Saved {output_path}")
if __name__ == "__main__":
generate(
prompt="A time-lapse of city traffic at night, neon reflections on wet asphalt",
output_path="city_timelapse.mp4",
)
Run it with python generate.py. Swap the prompt, add image_path="frame.png" for image-to-video, or point MODEL at veo-3.1-generate-001 for the higher-fidelity Standard tier once your prompts are dialed in on the cheaper Fast tier.
Common pitfalls when migrating from Sora 2 to Veo 3.1
- Using the old google-generativeai package. That library does not expose
generate_videosthe same way. Installgoogle-genaispecifically and update your imports tofrom google import genai. - Assuming a synchronous response. There is no version of this API that returns a finished video in the same call you submit the prompt. Every integration needs a polling loop or a webhook-based callback layer.
- Forgetting billing must be enabled. A key from a free-tier AI Studio project will fail with a permission error on the first video call, even if the same key works fine for text and image generation.
- Requesting 4K on Lite tier. Lite does not support 4K output at all; the request will be rejected rather than silently downgraded.
- Mixing up preview and GA model IDs. A preview-suffixed model string called against a Vertex AI GA endpoint, or vice versa, returns a not-found error rather than routing you to the right model.
- Polling too aggressively. Checking operation status every 1-2 seconds wastes API calls against your quota for no benefit, since generation jobs realistically take one to several minutes.
- Not testing the prompt library against the new filter policy. Prompts that passed Sora 2’s moderation are not guaranteed to pass Veo 3.1’s, and silent blocks with no video returned are easy to misread as a bug in your polling code.
- Building only a single-provider integration again. The whole reason this migration exists is that a single-provider dependency broke. Wire in Kling 3.0 as a fallback path from day one rather than after the next shutdown notice.
Pre-cutover checklist before you point production traffic at Veo
Do not flip a live feature straight from Sora 2 to Veo 3.1 without running through this list first. Each item here has caused a real production incident for teams migrating generation APIs before, and none of them show up until traffic actually hits the new provider.
- Run your top 20 most-used prompts from the old Sora integration against Veo 3.1 and manually review the output. Style and camera behavior differ enough between the two models that a prompt tuned for Sora’s interpretation may need rewording.
- Confirm your billing alerts are configured on the new Google Cloud project before, not after, you route real traffic. Video generation costs scale fast enough that a misconfigured loop can produce a four-figure surprise within an hour.
- Load-test your polling logic against at least 10 concurrent jobs to confirm you’re not hitting per-minute quota limits at your expected traffic volume.
- Verify your safety-block handling path actually triggers a user-facing message rather than a silent failure, since Veo 3.1’s content filter will flag some prompts that passed cleanly through Sora 2.
- Check that your storage pipeline downloads and persists every generated video immediately, since you should not rely on being able to re-fetch a completed generation from Google at a later time.
- If your product promises a specific clip length to users, confirm Veo 3.1 can actually deliver it in a single call before advertising the same duration you offered under Sora 2 Pro.
Sample output and what to expect
Running the script from Step 12 against a simple prompt produces console output like this while the job is in flight:
Submission failed (429 Too Many Requests), retrying in 10s
Saved city_timelapse.mp4
The retry line above is normal during a burst of test runs and does not indicate anything is broken, just that your project hit a short-lived quota ceiling. The resulting MP4 file plays as a standard H.264-encoded clip in whatever length you set with duration_seconds, at the resolution and aspect ratio you specified, with an embedded audio track if generate_audio was set to true.
Troubleshooting
- Permission denied on the generate_videos call. Confirm billing is enabled on the Google Cloud project tied to your API key. Video generation is never available on the free tier.
- Model not found for veo-3.1-generate-preview. You are likely calling a preview model ID against a Vertex AI endpoint that only accepts the GA IDs (
veo-3.1-generate-001,veo-3.1-fast-generate-001,veo-3.1-lite-generate-001), or the reverse. Match the model ID format to the endpoint you’re actually hitting. - operation.done stays False for a very long time. Standard tier and 4K requests take noticeably longer than Fast tier at 720p. Give it several minutes before assuming something is stuck, and add a maximum wait timeout to your polling loop so a stuck job doesn’t hang your process indefinitely.
- generated_videos list is empty after operation.done is True. The generation was almost certainly blocked by the safety filter. Rewrite the prompt to remove references to real people, copyrighted characters, or graphic content.
- ImportError on from google import genai. You have the old
google-generativeaipackage installed instead of, or alongside,google-genai. Uninstall the old package to avoid namespace conflicts:pip uninstall google-generativeai, then reinstallgoogle-genai. - Image-to-video call ignores the starting image. Confirm the image file path resolves correctly relative to where you’re running the script, and that the file is a supported format (PNG or JPEG). A silently failed
Image.from_filecall can fall through without an obvious error in some SDK versions. - Costs higher than expected on the billing dashboard. Check whether your config is defaulting to Standard tier resolution settings. Explicitly setting
resolutionin every request prevents silent tier upgrades. - Video downloads as a 0-byte file. This usually means
client.files.downloadwas called before the operation fully resolved. Confirmoperation.response.generated_videosis populated and non-empty before attempting the download step.
Advanced tips for production use
Once the basic pipeline works, a few refinements matter for anything running at scale. First, separate your prompt-testing traffic from your production traffic by using different API keys or projects, so a batch of experimental prompts doesn’t eat into the same quota your live feature depends on. Second, log the exact model ID, resolution, and duration used for every generation alongside its cost, since Veo’s tiered pricing makes it easy to lose track of spend across a team without per-call attribution.
Third, if you’re generating video for a vertical, mobile-first feed, request 9:16 directly rather than generating 16:9 and cropping afterward – cropping a landscape composition to portrait often cuts off the subject the model intentionally framed for widescreen. Fourth, for any workflow that previously used Sora 2’s longer clip support, plan around Veo 3.1’s more conservative default duration by chaining multiple 8-second generations with the last_frame parameter linking each segment to the next, rather than assuming a single call can replicate a 25-second Sora Pro clip.
Finally, build your abstraction layer around a provider-agnostic interface from the start, even if you only use Veo 3.1 today. The Sora 2 shutdown is not likely to be the last time a major provider retires a generation API on short notice, and the teams that weathered this transition with the least disruption were the ones that had already isolated their provider-specific code behind a single function boundary. Google’s other generative media models, including the world-model work covered in our Genie 3 vs Marble vs NVIDIA Cosmos comparison, point toward more of this consolidation happening inside the Gemini API surface over the next year (see Google’s own Veo 3.1 announcement post for the roadmap signals), which makes standardizing on Google’s SDK now a reasonable bet even if you keep Kling as a fallback. One more reason not to hardcode a single model ID and forget about it: Google has veo-3.1-generate-001 scheduled for retirement on or after November 17, 2026, exactly one year after its GA launch, so bake a model-version check into whatever abstraction layer you build rather than repeating the same single-point-of-failure mistake that made the Sora 2 shutdown so disruptive.
Frequently asked questions
Is there a free tier for testing Veo 3.1?
No. Google’s Gemini API pricing documentation lists Veo 3.1 Standard, Fast, and Lite as unavailable on the free tier. Billing must be enabled on the project tied to your API key before any video generation call will succeed.
Can I still use Sora 2 through a workaround or older API version?
No. OpenAI’s shutdown on September 24, 2026 removed the Sora 2 Videos API entirely, including every dated snapshot of sora-2 and sora-2-pro. There is no legacy endpoint left running and no announced replacement API from OpenAI.
Which Veo 3.1 tier is closest to what Sora 2 offered?
Veo 3.1 Fast at 1080p is the closest practical match for most Sora 2 use cases on both price and output quality. Reserve Standard tier for final, client-facing renders where the higher $0.40 to $0.60 per second cost is justified by the quality gap.
Do I need a Google Cloud account, or is a Gemini API key from AI Studio enough?
A Gemini API key from AI Studio is enough for the Gemini API surface used throughout this tutorial, as long as billing is enabled. Vertex AI is a separate surface aimed at teams already using Google Cloud infrastructure, and it uses service account authentication instead of a simple API key.
What happens if my prompt gets blocked by the safety filter?
The operation still completes, but operation.response.generated_videos comes back empty. Your code needs to check for this explicitly, since a blocked generation does not raise an exception on its own.
Is Kling 3.0 cheaper than Veo 3.1 across the board?
Not consistently. Kling 3.0 Standard without audio is cheaper than every Veo 3.1 tier, but once you add native audio, Kling 3.0’s rates land close to or slightly above Veo 3.1 Fast at the same resolution. The cheapest option depends on whether you need audio and which resolution you’re targeting.
How long does a typical video generation job take to complete?
Google does not publish a fixed SLA, but Fast-tier 8-second clips at 720p or 1080p typically complete within one to two minutes in practice. Standard tier and 4K requests generally take longer. Build a generous polling interval and a maximum timeout rather than assuming a fixed completion time.
Can I generate video from an existing image instead of a text prompt only?
Yes. Pass a types.Image.from_file() object through the image parameter on generate_videos for image-to-video generation, or supply both an image and a last_frame for interpolation between two fixed frames.





