Edit a video
October 7, 2026 (October 22, 2026)
Table of contents
Change a clip with a prompt, for example “Make the paper boat bright yellow”. The result is a new clip with a new mediaId, and the job runs on the account that holds the source.
Edit works on clips Google made: pass a video mediaId from POST /videos, POST /videos/extend or POST /videos/upscale. In our tests 7 of 7 edits of generated clips worked, including a colour change, an added object, a restyle, an extended 8-second clip and a 1080p portrait clip.
An edit works on 10 seconds of a clip and returns those 10 seconds. On a longer clip it edits the first 10 seconds, so edit clips of 10 seconds or less.
An edit of a 1080p clip probably comes back softer, the same as an extend: see 720p or 1080p.
The result carries Google’s visible Gemini ✦ (see Watermark). Our watermark remover does not work on edited clips: the model repaints the source’s star into the picture, so a faint diamond stays even after the new stamp is removed.
The API also accepts an uploaded video, an assetId from POST /assets, but Google refuses those at once with 422. That happened to every upload we tried, even a clip this API had generated and we uploaded again. A refusal uses nothing.
720p or 1080p
In our tests a 1080p clip behaves like a 720p clip that was upscaled, and it takes about twice as long to make. When you extend or edit a 1080p clip, Google seems to work on a 720p copy and then upscale the result again. Each round makes faces and small details softer. So:
- Make, extend and edit your clips at
720p. - When the clip is finished, upscale it once with POST /videos/upscale. Upscale takes clips of up to 20 seconds.
- Pick
1080pfrom the start for a single clip that you won’t extend or edit. A clip longer than 20 seconds can’t be upscaled, so for a long1080pclip, extend at720pand setresolution: "1080p"on the last extend only, or extend at1080pthroughout and accept softer faces.
Cost
An edit uses the length of the clip it returns, at most 10 seconds, from the account’s monthly Vids video allowance. A request Google refuses uses nothing.
Sync, async and webhooks
- Every generation runs as a job on our side, with up to 5 minutes for Google’s answer, so a slow answer is never lost.
- Sync (the default): the POST waits for the job and answers
200with the finished job record, or the job’s error code. A video usually takes 20 to 100 seconds, an image about 10. If the job is still running after about 100 seconds, the POST answers202with the job as it stands: fetch the result with GET /jobs/jobid. Always checkstatus: onlycompletedhas aresult. - Async: pass
async: true. The POST answers202at once with the job instatus: "processing". Poll GET /jobs/jobidor wait for the webhook. replyUrlreceives onePOSTof the final job record, as JSON, when the job completes or fails, for sync and async requests alike. It does not make a request async.- Each account runs at most
maxJobsjobs at a time (default3, range1to10, set with POST /accounts). GET /jobs shows what is running. Job records are kept for 7 days.
https://api.useapi.net/v1/google-vids/videos/edit
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
{
"video": "user:[email protected]:eyJ1IjoiaHR0…",
"prompt": "Make the paper boat bright yellow",
"async": true,
"replyUrl": "https://your-domain.com/webhook",
"replyRef": "boat-yellow"
}
videois required, the videomediaIdof the clip to edit, from a finished job’sresult. AnassetIdof an uploaded MP4 is accepted, but Google refuses it.promptis required, the change to make.
Maximum length:5000characters.durationis optional for amediaId, where it defaults to the clip’s length up to 10 seconds and may not be longer than the clip, and required for anassetId, where it is the uploaded video’s length in seconds.
Range:3to10.aspectRatiois optional.
Supported values:landscape,portrait. Default: the source clip’s (landscapefor anassetId).resolutionis optional.
Supported values:720p,1080p. Default: the source clip’s (720pfor anassetId).asyncis optional,trueto answer202at once and run the job in the background. Default:false.replyUrlis optional, a publichttp(s)URL that receives onePOSTof the job record when the job completes or fails. It works with sync and async requests.
Callback body has the same JSON shape as GET /jobs/jobidresponse.
Maximum length:1024characters.replyRefis optional, your own reference echoed back in the job record.
Maximum length:1024characters.
Responses
-
200 OK — sync mode, a 3-second clip edited, charged 3 seconds.
{ "jobid": "j1007134950991174444v-u12345-email:[email protected]:google-vids", "type": "video", "mode": "edit", "email": "[email protected]", "status": "completed", "created": "2026-10-07T06:28:21.680Z", "request": { "video": "user:[email protected]:eyJ1IjoiaHR0…", "prompt": "Make the paper boat bright yellow" }, "updated": "2026-10-07T06:28:59.896Z", "completed": "2026-10-07T06:28:59.896Z", "result": { "mediaId": "user:[email protected]:eyJ1IjoiaHR0…", "width": 1280, "height": 720, "duration": 3, "resolution": "720p", "aspectRatio": "landscape", "model": "/flix/generate_videos_omni_edit_psq/v1", "quota": { "video": { "limit": 10000, "left": 9621, "resetAt": "2026-11-01T07:00:00.000Z" } }, "elapsedMs": 38167 } } -
202 Accepted — the job is running: with
async: trueat once, in sync mode after about 100 seconds of waiting. Fetch the result with GET /jobs/jobid(or wait for the webhook). -
400 Bad Request — a parameter is missing or invalid, or
durationis longer than the clip.{ "error": "duration (the uploaded video's length in seconds, 3–10) is required when video is an asset id", "code": 400 }{ "error": "Parameter video is not a valid video / asset id", "code": 400 } -
Invalid API token.
{ "error": "useapi.net ⁝ Unauthorized", "code": 401 } -
403 Forbidden — the
videoid was issued to a different API token, or the account has Google Workspace smart features turned off.{ "error": "video id does not belong to this API token", "code": 403 } -
404 Not Found — the account that holds the source is no longer connected.
{ "error": "Account [email protected] (from the video id) is not configured", "code": 404 } -
422 Unprocessable Content — Google refused the edit. Every uploaded video gets this answer. Nothing was charged.
{ "jobid": "j1007123831345945676v-u12345-email:[email protected]:google-vids", "type": "video", "mode": "edit", "email": "[email protected]", "status": "failed", "created": "2026-10-07T06:36:31.478Z", "request": { "video": "user:[email protected]:AVL_0qh2dWHI…", "duration": 4, "prompt": "Make the sky stormy and dark" }, "updated": "2026-10-07T06:36:41.176Z", "completed": "2026-10-07T06:36:41.176Z", "error": { "code": 422, "message": "Google refused this request (\"That request looks like it goes against our terms. Try asking something else.\"). Change the prompt or the inputs and try again." } } -
429 Too Many Requests — the account is running
maxJobsjobs, the month’s video allowance is used up, or Google is limiting the account for a minute.{ "error": "Account [email protected] is running 3 of 3 jobs (maxJobs). Wait for one to finish, or raise maxJobs with POST /accounts", "code": 429 }Once Google refuses a job because the account’s monthly video allowance is used up, the API keeps Google’s reset time on the account (
exhaustedUntilin GET /accounts) and gives it no video jobs until then. A request that must run on that account is answered at once with429, with no job created and no call to Google.retryAtis when to try again. If the account’s plan changed, GET /accounts/emailreads its allowance again and lets it take jobs right away.{ "error": "Account [email protected] has used up this month's Google Vids video allowance. It resets at 2026-11-01T07:00:00.000Z. If its plan changed, call GET /accounts/[email protected] to refresh it", "code": 429, "retryAt": "2026-11-01T07:00:00.000Z" }A job that Google itself refuses for its allowance (the first refusal, or a job that needs more than the account has left) fails with
error.code429,error.retryAtset to the reset time, anderror.quotawith what was left (limit,left,resetAt). An account with some allowance left still takes smaller jobs. -
504 Gateway Timeout — Google did not answer within 5 minutes: run it again.
-
596 Account Error
Google reported the account as signed out. While we re-check it, the answer is the first message below. Retry in about a minute. If it stays signed out, the account is paused, you receive an email, and the answer becomes the second message until you re-add it via Setup Google Vids.
{ "error": "Google reported this account as signed out. We are re-checking it — please retry in about a minute. If it stays signed out, you will receive a re-add email.", "code": 596 }{ "error": "Account [email protected]: Google signed this account out. Re-add it at https://useapi.net/docs/start-here/setup-google-vids", "code": 596 }
Model
The job record, mode: "edit". GET /jobs/jobid and the replyUrl webhook return the same shape.
{ // TypeScript, all fields are optional
jobid: string // j<time><random><v|i>-u<id>-email:<email>-bot:google-vids
type: 'video' | 'image'
mode?: 'text' | 'image' | 'ingredients' | 'extend' | 'upscale' | 'edit' // video jobs only
email: string // the account the job runs on
status: 'processing' | 'completed' | 'failed'
created: string // ISO 8601, when the job was accepted
updated?: string // ISO 8601, the last status change
completed?: string // ISO 8601, when the job completed or failed
request: Record<string, unknown> // your request body as sent (ids as strings)
replyUrl?: string
replyRef?: string
error?: { // status 'failed'
code: number // 400 | 403 | 404 | 422 | 429 | 500 | 502 | 503 | 504 | 596, see GET /jobs/{jobid}
message: string
retryAt?: string // code 429: ISO 8601, when to try again
quota?: { // code 429 for allowance: Google's counter for this kind of job when it refused it
limit: number // monthly limit (seconds of video, or images)
left: number // what was left, 0 when used up
resetAt: string // ISO 8601, when the allowance resets
}
}
result?: { // status 'completed'
mediaId: string // download it with GET /media/{mediaId}. A video: extend / upscale / edit it. An image: use it as startImage, referenceImage_N or avatar image
width: number
height: number
duration?: number // video: length of the whole clip in seconds
resolution?: '720p' | '1080p' // video
aspectRatio?: 'landscape' | 'portrait' // video
model: string | null // Google's backend path, e.g. /flix/generate_videos_omni_t2v_psq/v1
quota: { // what the account has left after this job
video?: { limit: number, left: number, resetAt: string } // seconds, video jobs
image?: { limit: number, left: number, resetAt: string } // images, image jobs
}
elapsedMs: number // how long Google took
}
}
Examples
-
curl -X POST "https://api.useapi.net/v1/google-vids/videos/edit" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer …" \ -d '{ "video": "user:[email protected]:eyJ1IjoiaHR0…", "prompt": "Make the paper boat bright yellow" }' -
const token = "API token"; const video = "mediaId of a clip made by this API"; const apiUrl = "https://api.useapi.net/v1/google-vids/videos/edit"; const response = await fetch(apiUrl, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}`, }, body: JSON.stringify({ video, prompt: "Make the paper boat bright yellow" }) }); const job = await response.json(); console.log("response", response.status, job.result ?? job.error ?? job); -
import requests token = "API token" video = "mediaId of a clip made by this API" apiUrl = "https://api.useapi.net/v1/google-vids/videos/edit" headers = { "Content-Type": "application/json", "Authorization" : f"Bearer {token}" } data = { "video": video, "prompt": "Make the paper boat bright yellow" } response = requests.post(apiUrl, headers=headers, json=data) print(response, response.json())