The moment that pushed me from "interesting demo" to "I want to build something with this" was watching an 8-second Veo 3 clip. The motion was smooth enough, and the lighting realistic enough, that it felt like product-grade quality rather than a novelty.
This article is about building, not about what Veo 3 is. If you want an introduction to Veo 3 itself, there are good resources elsewhere. Here, the focus is: how do you actually implement a video generation app using the Veo 3 API through Antigravity?
The Architecture You Need to Understand First
Video generation APIs don't work like text APIs. You don't send a request and get a video back in the response. The flow is asynchronous:
User → prompt input → API request
↓
job ID returned (immediate)
↓
polling loop (5–30 seconds)
↓
video URL retrieved → display
If you try to handle this synchronously, your UI will block and users will think the app is frozen. Understanding this before you write a single line of code saves significant debugging time.
When you explain this architecture to Antigravity upfront, it generates well-structured async code that handles the polling loop correctly.
Core API Implementation
The Veo 3 API is part of the Gemini API family and accessed through Google AI Studio. Here's a practical prompt to start your Antigravity session:
"I'm building an iOS app that lets users generate short videos using the Veo 3 API.
The app should use SwiftUI with an async polling architecture.
Flow: user inputs prompt → app starts generation job → polls for completion → displays video.
Start with the API client layer."
The API client Antigravity generates will look something like this:
import Foundation
struct Veo3APIClient {
private let apiKey = "YOUR_API_KEY"
private let baseURL = "https://generativelanguage.googleapis.com/v1beta"
func startVideoGeneration(prompt: String) async throws -> String {
let url = URL(string: "\(baseURL)/models/veo-003:generateVideo?key=\(apiKey)")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"contents": [["parts": [["text": prompt]]]],
"generationConfig": [
"durationSeconds": 8,
"aspectRatio": "16:9",
"resolution": "720p"
]
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, _) = try await URLSession.shared.data(for: request)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
return json["name"] as! String // job ID for polling
}
func checkJobStatus(jobName: String) async throws -> VideoJobStatus {
let url = URL(string: "\(baseURL)/\(jobName)?key=\(apiKey)")!
let (data, _) = try await URLSession.shared.data(from: url)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let done = json["done"] as? Bool ?? false
if done {
let response = json["response"] as? [String: Any]
let candidates = response?["candidates"] as? [[String: Any]]
let videoUri = candidates?.first?["video"] as? [String: Any]
let uri = videoUri?["uri"] as? String
return .completed(videoURL: uri)
}
return .processing
}
}
enum VideoJobStatus {
case processing
case completed(videoURL: String?)
case failed
}Polling ViewModel
Tell Antigravity: "Add a ViewModel that polls the job status every 5 seconds for up to 60 seconds, updating the UI with progress." The result:
@MainActor
class VideoGenerationViewModel: ObservableObject {
@Published var status: GenerationStatus = .idle
@Published var videoURL: URL?
@Published var errorMessage: String?
private let client = Veo3APIClient()
func generateVideo(prompt: String) async {
status = .starting
do {
let jobName = try await client.startVideoGeneration(prompt: prompt)
status = .processing(progress: 0)
for attempt in 1...12 { // 12 attempts × 5 seconds = 60 second max
try await Task.sleep(nanoseconds: 5_000_000_000)
let result = try await client.checkJobStatus(jobName: jobName)
switch result {
case .processing:
status = .processing(progress: Double(attempt) / 12.0)
case .completed(let urlString):
if let urlString, let url = URL(string: urlString) {
videoURL = url
status = .completed
} else {
status = .failed
errorMessage = "Failed to retrieve video URL"
}
return
case .failed:
status = .failed
errorMessage = "Video generation failed"
return
}
}
status = .failed
errorMessage = "Generation timed out. Please try again."
} catch {
status = .failed
errorMessage = error.localizedDescription
}
}
}
enum GenerationStatus: Equatable {
case idle
case starting
case processing(progress: Double)
case completed
case failed
}UI Design — The Waiting Experience Matters
With video generation, users wait 10–30 seconds. How you handle that wait determines whether users trust the app or abandon it. Tell Antigravity specifically:
"Design the loading state to show:
- A progress bar based on polling attempts
- Animated text: 'Veo 3 is creating your video...'
- A cancel button
Reveal the video player with an animation on completion."
The progress bar gives users a sense of movement even when the API is working in the background. Without it, the experience feels broken.
Monetization Considerations
Veo 3 API is priced per second of video generated (check current Google AI Studio pricing — rates change). If you use your own API key for a user-facing app, costs scale linearly with usage.
Two practical approaches:
User-provided API key: Users enter their own Google AI Studio key. Lower friction to launch, but reduces your addressable market to users who already have API access.
Credit-based in-app purchases: Sell video generation credits through StoreKit. Ask Antigravity to wire up StoreKit 2 with a credit consumption system — it handles this pattern well.
Antigravity prompt: "Implement StoreKit 2 for selling video generation credits.
100 credits = $0.99. Each 8-second video costs 10 credits.
Track remaining credits in UserDefaults and show balance in the UI."
Before You Submit to the App Store
Verify your app handles the Veo 3 content policy correctly. Generated videos must comply with both the API terms of service and App Store guidelines — particularly around deepfakes and misleading content. Add a usage disclaimer to your app and consider adding a content reporting mechanism.
Also add the appropriate NSUsageDescription keys if your app accesses the camera or microphone alongside the video feature.
The technical implementation takes a day. Getting the monetization and policy details right takes longer, but that's where apps succeed or fail long-term. Antigravity helps with both — just ask specifically.