Upscale Videos
January 20, 2026 (May 25, 2026)
Table of contents
Upscale a previously generated video to 1080p or 4K resolution.
- Currently, video upscaling requires a paid Google AI subscription or remaining credits.
- Currently,
4Kresolution requires a Google AI Ultra subscription. Other paid accounts can only use1080presolution. 1080pupscaling is free.4Kupscaling currently costs 50 credits.- Video upscaling typically completes within 30-60 seconds, with 4K taking a few minutes.
- Upscaling the same video twice returns a cached result (no additional credits consumed). Use the video URL from the first upscale response.
https://api.useapi.net/v1/google-flow/videos/upscale
Request Headers
Authorization: Bearer {API token}
Content-Type: application/json
# Alternatively you can use multipart/form-data
# Content-Type: multipart/form-data
API tokenis required, see Setup useapi.net for details.
Request Body
{
"mediaGenerationId": "user:12345-email:6a6f...-video:CAMaJDMx...",
"resolution": "1080p"
}
mediaGenerationIdis required. ThemediaGenerationIdfrom a previously generated video via POST /videos.resolutionis optional. Target resolution for upscaling (default:1080p).
Supported values:1080p,4K.asyncis optional, enables fire-and-forget mode (default:false).
Whentrue, returns immediately with201 Createdand job metadata. Poll GET /jobs/jobIdfor completion status.replyUrlis optional, webhook URL for job status callbacks.
Receives POST requests with job status updates (created,started,completed,failed). The JSON payload shape matches GET /jobs/jobIdresponse.replyRefis optional, custom reference string passed back in webhook callbacks.captchaToken,captchaRetry,captchaOrderare optional, mutually exclusive captcha parameters. See Captcha Parameters for details.
Responses
-
Video upscaled successfully. Both
operations[](legacy, withfifeUrl) andmedia[](current, withvideoUrl) are populated.{ "jobId": "j1737312345678v-u12345-email:jo***@gmail.com-bot:google-flow", "operations": [ { "operation": { "name": "2fefd089-...redacted..._upsampled", "metadata": { "@type": "type.googleapis.com/google.internal.labs.aisandbox.v1.Media", "name": "2fefd089-...redacted..._upsampled", "video": { "seed": 0, "model": "veo_3_1_upsampler_1080p", "aspectRatio": "VIDEO_ASPECT_RATIO_LANDSCAPE", "isLooped": false, "upsampleResolution": "VIDEO_UPSAMPLE_RESOLUTION_1080P", "fifeUrl": "https://flow-content.google/video/2fefd089-..._upsampled?Expires=...", "servingBaseUri": "https://flow-content.google/image/2fefd089-..._upsampled?Expires=...", "mediaGenerationId": "user:12345-email:6a6f...-video:2fefd089-..._upsampled", "mediaVisibility": "PRIVATE" } } }, "sceneId": "", "status": "MEDIA_GENERATION_STATUS_SUCCESSFUL", "mediaGenerationId": "user:12345-email:6a6f...-video:2fefd089-..._upsampled" } ], "media": [ { "name": "2fefd089-...redacted..._upsampled", "projectId": "9f63078c-...redacted...", "workflowId": "66612805-...redacted...", "workflowStepId": "CAI", "mediaMetadata": { "createTime": "2026-05-20T23:31:41.145235Z", "requestData": { "videoGenerationRequestData": { "...": "..." }, "clientPlatform": "CLIENT_PLATFORM_WEB" }, "mediaStatus": { "mediaGenerationStatus": "MEDIA_GENERATION_STATUS_SUCCESSFUL" }, "visibility": "PRIVATE" }, "video": { "generatedVideo": { "seed": 0, "model": "veo_3_1_upsampler_1080p", "baseImageMediaGenerationId": "", "isLooped": false, "aspectRatio": "VIDEO_ASPECT_RATIO_LANDSCAPE", "upsampleMetadata": { "videoUpsampleResolution": "VIDEO_UPSAMPLE_RESOLUTION_1080P" } }, "dimensions": { "length": "0s" }, "operation": { "name": "2fefd089-..._upsampled" } }, "mediaGenerationId": "user:12345-email:6a6f...-video:2fefd089-..._upsampled", "videoUrl": "https://flow-content.google/video/2fefd089-..._upsampled?Expires=...", "thumbnailUrl": "https://flow-content.google/image/2fefd089-..._upsampled?Expires=..." } ], "remainingCredits": 18760, "captcha": { "service": "AntiCaptcha", "taskId": "abc123...", "durationMs": 3500, "attempts": [ { "service": "AntiCaptcha", "taskId": "abc123...", "durationMs": 3500, "success": true } ] } } -
Job created in async mode (
async: true). Video upscaling is processing in the background.Use GET /jobs/
jobIdto poll for completion status.{ "jobid": "j1737312345678v-u12345-email:jo***@gmail.com-bot:google-flow", "type": "video", "status": "created", "created": "2026-01-19T12:34:56.789Z", "request": { "async": true, "mediaGenerationId": "user:12345-email:6a6f...-video:CAMaJDMx...", "resolution": "1080p" }, "response": { "captcha": { "service": "AntiCaptcha", "taskId": "14af1dbb-885c-4e25-8121-7a79489dfd0e", "durationMs": 5357 } } } -
Invalid request.
{ "error": "Error message" } -
Invalid API token.
{ "error": "Unauthorized" } -
Request rejected by Google — the reCAPTCHA token was outright rejected (not just scored low). After internal retries with fresh tokens, the worker returns this. The top-level
errorfield carries acaptcha_quality:prefix so tooling can detect this failure class. IncreasecaptchaRetry(default 3) in the request body to 5 or higher, and configure additional providers via POST /accounts/captcha-providers socaptchaOrdercan cycle across them. If issues persist, contact your reCAPTCHA provider(s) support. -
Account not found or video not found.
{ "error": "Google Flow account [email protected] not found" } -
Video upscaling polling timeout (after 10 minutes).
{ "error": "Polling timeout" } -
Google returns several
RESOURCE_EXHAUSTED429 variants. Check thereasonfield and respond accordingly:Reason Meaning Scope What to do Cooldown PUBLIC_ERROR_USER_REQUESTS_THROTTLEDPer-user concurrency limit (the token was fine) All models on the account Run fewer parallel requests, retry later ~30 min PUBLIC_ERROR_USER_QUOTA_REACHEDAccount’s overall Flow quota cap All models on the account Add accounts, or omit emailto load-balance~30 min PUBLIC_ERROR_PER_MODEL_DAILY_QUOTA_REACHEDPer-model daily quota Only that model, on that account Switch model, or wait Next UTC midnight PUBLIC_ERROR_UNUSUAL_ACTIVITY_TOO_MUCH_TRAFFICCaptcha-provider issue — low reCAPTCHA score (worker already auto-retried) Per request Add more captcha providers + captchaOrder, or raisecaptchaRetry60s Every
429returns the cooldown two ways — an HTTPRetry-After: <seconds>header and a bodyretryAfter: <ISO timestamp>field. Honor it before retrying.When
emailis omitted, the first three reasons quarantine the account and the load balancer routes around it. If every account is quarantined for the model you get429witherror: "no_eligible_account". Routing details: GET /jobs › Load Balancing Algorithm.Captcha-quality failures prefix the top-level
errorwithcaptcha_quality:(e.g."captcha_quality: PUBLIC_ERROR_UNUSUAL_ACTIVITY_TOO_MUCH_TRAFFIC after 3 attempts") so you can detect them without parsing the nested body. -
503has two distinct causes — check the response body to tell them apart.A transient Google-side outage. Wait 5-10 seconds and retry.
{ "error": { "code": 503, "message": "Service temporarily unavailable.", "status": "UNAVAILABLE" } }A captcha-provider failure — the top-level
erroris prefixedCaptcha service failed:. The problem is your captcha provider (check its API key and balance), not Google. Don’t retry until the provider is back.{ "error": "Captcha service failed: ERROR_ZERO_BALANCE", "code": 503 }
Model
-
Video upscaling completed. Returns upscaled video data with signed URLs. Re-upscaling the same video returns
rawBytes(base64 video data) instead of URLs.{ jobId: string // Job identifier operations: Array<{ // Legacy shape, still populated for upscale (with fifeUrl) operation: { name: string metadata: { '@type': string name: string video: { seed: number model: string // veo_3_1_upsampler_1080p | veo_3_1_upsampler_4k aspectRatio: string // VIDEO_ASPECT_RATIO_LANDSCAPE | PORTRAIT isLooped: boolean upsampleResolution?: string // 'VIDEO_UPSAMPLE_RESOLUTION_1080P' | 'VIDEO_UPSAMPLE_RESOLUTION_4K' fifeUrl: string // Signed video URL (MP4) servingBaseUri: string // Signed thumbnail URL (JPEG) mediaGenerationId: string // Encoded reference ID for the upscaled video mediaVisibility: string } } } sceneId: string status: string // MEDIA_GENERATION_STATUS_SUCCESSFUL | FAILED mediaGenerationId: string }> media: Array<{ // Current shape — same fields as POST /videos name: string // Media identifier (raw UUID) projectId: string workflowId?: string workflowStepId?: string // e.g. "CAI" for upscale mediaMetadata: { createTime?: string // ISO 8601 timestamp requestData?: object // Full Google Flow request payload mediaStatus: { mediaGenerationStatus: string // MEDIA_GENERATION_STATUS_SUCCESSFUL | FAILED error?: { code: number; message: string } } visibility?: string } video?: { generatedVideo: { seed: number model: string // veo_3_1_upsampler_1080p | veo_3_1_upsampler_4k baseImageMediaGenerationId?: string // Source video media ID isLooped: boolean aspectRatio: string // VIDEO_ASPECT_RATIO_LANDSCAPE | PORTRAIT upsampleMetadata?: { videoUpsampleResolution: string } // VIDEO_UPSAMPLE_RESOLUTION_1080P | _4K } dimensions?: { length?: string } // Often "0s" for upscale (not meaningful) operation: { name: string } } mediaGenerationId: string // Encoded reference ID for use in subsequent API calls videoUrl?: string // Signed video download URL (MP4) thumbnailUrl?: string // Signed thumbnail download URL (JPEG) }> remainingCredits?: number captcha?: { // Captcha metadata service: string // "CapSolver" | "AntiCaptcha" | "YesCaptcha" | "CapMonster" | "SolveCaptcha" | "2Captcha" | "EzCaptcha" | "UserProvided" taskId?: string durationMs: number attempts: Array<{ service: string taskId?: string durationMs: number success: boolean error?: string }> } } -
Job created and processing in background. Structure matches GET /jobs/
jobIdresponse.{ jobid: string // Job identifier type: 'video' // Job type status: 'created' // Job status created: string // ISO 8601 timestamp request: { async: true mediaGenerationId: string // Original video reference resolution?: string // "1080p" | "4K" replyUrl?: string replyRef?: string } response: { captcha?: { // Present when captcha was used service: string // Provider or "UserProvided" taskId?: string // Absent for UserProvided durationMs: number } } } -
Error response structure.
{ jobId?: string // Present for job-related errors error: string // Error summary message code?: number // HTTP status code response?: { // API response with error details error?: { code: number message: string status: string details?: Array<{ '@type': string reason: string }> } } }
Examples
-
# First generate a video curl -X POST \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"prompt": "A serene mountain landscape at sunset"}' \ "https://api.useapi.net/v1/google-flow/videos" > generate.json # Extract mediaGenerationId from the response MEDIA_ID=$(jq -r '.media[0].mediaGenerationId' generate.json) # Upscale the video to 1080p curl -X POST \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"mediaGenerationId\": \"$MEDIA_ID\", \"resolution\": \"1080p\"}" \ "https://api.useapi.net/v1/google-flow/videos/upscale" > upscale.json # Extract upscaled video URL and download VIDEO_URL=$(jq -r '.media[0].videoUrl' upscale.json) curl "$VIDEO_URL" --output upscaled_video.mp4 -
const token = 'YOUR_API_TOKEN'; // First generate a video const generateResponse = await fetch('https://api.useapi.net/v1/google-flow/videos', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'A serene mountain landscape at sunset' }) }); const generated = await generateResponse.json(); // Prefer media[] over deprecated operations[] const mediaGenerationId = generated.media[0].mediaGenerationId; // Upscale the video const upscaleResponse = await fetch('https://api.useapi.net/v1/google-flow/videos/upscale', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ mediaGenerationId, resolution: '1080p' }) }); const upscaled = await upscaleResponse.json(); // Prefer media[] over deprecated operations[] const videoUrl = upscaled.media[0].videoUrl; // Download upscaled video (Node.js) const videoResponse = await fetch(videoUrl); const videoBuffer = await videoResponse.arrayBuffer(); require('fs').writeFileSync('upscaled_video.mp4', Buffer.from(videoBuffer)); console.log('Upscaled video saved!'); -
import requests token = 'YOUR_API_TOKEN' headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } # First generate a video generate_response = requests.post( 'https://api.useapi.net/v1/google-flow/videos', headers=headers, json={'prompt': 'A serene mountain landscape at sunset'} ) generated = generate_response.json() # Prefer media[] over deprecated operations[] media_generation_id = generated['media'][0]['mediaGenerationId'] # Upscale the video upscale_response = requests.post( 'https://api.useapi.net/v1/google-flow/videos/upscale', headers=headers, json={ 'mediaGenerationId': media_generation_id, 'resolution': '1080p' } ) upscaled = upscale_response.json() # Prefer media[] over deprecated operations[] video_url = upscaled['media'][0]['videoUrl'] # Download upscaled video video_response = requests.get(video_url) with open('upscaled_video.mp4', 'wb') as f: f.write(video_response.content) print('Upscaled video saved!')