Retrieve a job
October 7, 2026
Table of contents
Fetch a job record by its jobid. Every generation is a job: POST /videos, POST /videos/extend, POST /videos/upscale, POST /videos/edit and POST /images all return one. After an async request, poll this endpoint until status is completed or failed.
A video takes about 20 to 100 seconds and an image about 10, so polling every 10 seconds is enough. A job may run for up to 15 minutes, and a sync POST that answered 202 hands you a job that is still running. A job still not finished about 16 minutes after it was created is failed with 504 when it is read.
Instead of polling, pass replyUrl (and optionally replyRef) with the POST: when the job completes or fails, the API sends one POST of this same job record, as JSON, to that URL. See Model.
Job records are kept for 30 days.
https://api.useapi.net/v1/google-vids/jobs/
jobid
jobidis URL-encoded in the path (it contains:and@), for exampleuser%3A12345-user%40example.com-job%3A4aa24347-8099-4624-80ec-fb758db74419.
Request Headers
Authorization: Bearer {API token}
API tokenis required, see Setup useapi.net for details.
Responses
-
Completed (an async text-to-video job):
{ "jobid": "user:[email protected]:4aa24347-8099-4624-80ec-fb758db74419", "type": "video", "mode": "text", "email": "[email protected]", "status": "completed", "created": "2026-10-07T06:29:01.204Z", "request": { "prompt": "A hot air balloon rising over a misty valley at sunrise", "duration": 3, "async": true }, "updated": "2026-10-07T06:29:27.207Z", "completed": "2026-10-07T06:29:27.207Z", "result": { "mediaId": "user:[email protected]:eyJ1IjoiaHR0…", "width": 1280, "height": 720, "duration": 3, "resolution": "720p", "aspectRatio": "landscape", "model": "/flix/generate_videos_omni_t2v_psq/v1", "quota": { "video": { "limit": 10000, "left": 9615, "resetAt": "2026-11-01T07:00:00.000Z" } }, "elapsedMs": 24611 } }Running:
{ "jobid": "user:[email protected]:4aa24347-8099-4624-80ec-fb758db74419", "type": "video", "mode": "text", "email": "[email protected]", "status": "processing", "created": "2026-10-07T06:29:01.204Z", "request": { "prompt": "A hot air balloon rising over a misty valley at sunrise", "duration": 3, "async": true }, "updated": "2026-10-07T06:29:02.461Z" }Failed (Google refused an edit of an uploaded video):
{ "jobid": "user:[email protected]:db97658a-1240-4eee-a9dc-75f7c5123d2e", "type": "video", "mode": "edit", "email": "[email protected]", "status": "failed", "created": "2026-10-07T06:36:31.478Z", "request": { "video": "user:[email protected]:…", "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." } } -
The path value is not a Google Vids
jobid.{ "error": "Path parameter jobid (…) is not a valid job id", "code": 400 } -
Invalid API token.
{ "error": "useapi.net ⁝ Unauthorized", "code": 401 } -
{ "error": "This job does not belong to this API token", "code": 403 } -
No such job, or its record is older than 30 days.
{ "error": "Job user:[email protected]:00000000-0000-4000-8000-000000000000 not found (records are kept 30 days)", "code": 404 }
Model
The job record. POST /videos, POST /videos/extend, POST /videos/upscale, POST /videos/edit and POST /images return it too, and it is the body of the replyUrl webhook.
{ // TypeScript, all fields are optional
jobid: string // user:<id>-<email>-job:<uuid>
type: 'video' | 'image'
mode?: 'text' | 'image' | 'ingredients' | 'extend' | 'upscale' | 'edit' // video jobs only
email: string // the account the job runs on
status: 'pending' | '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
}
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
}
}
status | Meaning |
|---|---|
pending | Async only: the job is queued and has not started. |
processing | Google is generating. A sync job starts here. |
completed | Done. result.mediaId downloads the file with GET /media/mediaId. |
failed | Done without a file. error says why. |
A job sent without email and without input ids can change email while it runs: when Google refuses it because that account’s allowance is used up, it moves to another of your accounts.
error.code | Meaning |
|---|---|
400 | Google rejected the request as invalid. message has Google’s reason. |
403 | The account’s Google plan has no Vids allowance for this kind of job (a monthly limit of 0). |
404 | The account was removed with DELETE /accounts/email before the async job ran. |
422 | Google refused the prompt or the inputs (“That request looks like it goes against our terms”). Nothing was charged. Change the prompt or the inputs. In our tests every edit of an uploaded video ended here. |
429 | The account’s monthly allowance is used up, and retryAt is the reset time. Or Google is limiting the account for a short while, and retryAt is about a minute away. |
500 | An unexpected error on our side. |
502 | Google answered without a file, or with an unexpected error. |
503 | Google answered with a server error or could not be reached, or the async job could not be queued. Retry. |
504 | Google did not answer within 15 minutes, or the connection to Google was cut, or the job did not finish within its time budget. Google may still finish the file and charge it. |
596 | Google reported the account as signed out. While we re-check it, retry in about a minute. If it stays signed out, re-add it via Setup Google Vids. |
A sync POST whose job fails answers with the HTTP status in error.code and the failed job record as the body.
The replyUrl webhook is one POST with Content-Type: application/json and this record as the body, sent once when the job completes or fails (a 10-second timeout, no retries, redirects are not followed). Use replyRef to match the callback to your own records.
Examples
-
JOBID="user:[email protected]:4aa24347-8099-4624-80ec-fb758db74419" curl -H "Authorization: Bearer …" \ "https://api.useapi.net/v1/google-vids/jobs/$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "$JOBID")" -
const token = "API token"; const jobid = "user:[email protected]:4aa24347-8099-4624-80ec-fb758db74419"; const apiUrl = `https://api.useapi.net/v1/google-vids/jobs/${encodeURIComponent(jobid)}`; let job; do { await new Promise(resolve => setTimeout(resolve, 10000)); const response = await fetch(apiUrl, { headers: { "Authorization": `Bearer ${token}`, }, }); job = await response.json(); console.log(job.status); } while (job.status === "pending" || job.status === "processing"); console.log("job", job); -
import requests, time, urllib.parse token = "API token" jobid = "user:[email protected]:4aa24347-8099-4624-80ec-fb758db74419" apiUrl = "https://api.useapi.net/v1/google-vids/jobs/" + urllib.parse.quote(jobid, safe="") headers = { "Authorization" : f"Bearer {token}" } while True: time.sleep(10) job = requests.get(apiUrl, headers=headers).json() print(job.get("status")) if job.get("status") not in ("pending", "processing"): break print(job)