Retrieve Job Status

November 17, 2025 (July 27, 2026)

Table of contents

  1. Request Headers
  2. Path Parameters
  3. Responses
  4. Model
  5. Missing media URLs
    1. How to get the link
  6. Examples
  7. Try It

Retrieve the status and details of a specific image or video generation job by its job ID.

This endpoint is particularly useful when using async mode for video generation, or when tracking job progress via webhooks using the replyUrl parameter.

To get load balancing statistics across all jobs, use GET /jobs.

https://api.useapi.net/v1/google-flow/jobs/jobId

Request Headers

Authorization: Bearer {API token}

Path Parameters

Responses

  • 200 OK

    Returns the job record with current status and details.

    Video job (completed):

    {
      "jobid": "j1731859234567v-u12345-email:jo***@gmail.com-bot:google-flow",
      "type": "video",
      "status": "completed",
      "created": "2025-11-17T12:34:56.789Z",
      "updated": "2025-11-17T12:37:23.456Z",
      "request": {
        "email": "jo***@gmail.com",
        "prompt": "A serene mountain landscape at sunset with camera slowly panning right",
        "model": "veo-3.1-fast",
        "aspectRatio": "landscape",
        "count": 2,
        "seed": 123456,
        "async": true,
        "replyUrl": "https://your-domain.com/webhook"
      },
      "response": {
        "media": [
          {
            "name": "a1d95d21-75d2-482d-a354-14ef8802ce66",
            "projectId": "9f63078c-...redacted...",
            "workflowId": "fa986834-...redacted...",
            "workflowStepId": "CAE",
            "mediaMetadata": {
              "createTime": "2026-05-20T23:14:02.931677Z",
              "mediaTitle": "A serene mountain landscape at sunset with camera slowly panning right",
              "requestData": { "videoGenerationRequestData": { "...": "..." }, "clientPlatform": "CLIENT_PLATFORM_WEB" },
              "mediaStatus": { "mediaGenerationStatus": "MEDIA_GENERATION_STATUS_SUCCESSFUL" },
              "visibility": "PRIVATE"
            },
            "video": {
              "generatedVideo": {
                "seed": 123456,
                "prompt": "A serene mountain landscape at sunset with camera slowly panning right",
                "model": "veo_3_1_t2v",
                "baseImageMediaGenerationId": "",
                "isLooped": false,
                "aspectRatio": "VIDEO_ASPECT_RATIO_LANDSCAPE"
              },
              "dimensions": { "length": "8s" },
              "operation": { "name": "a1d95d21-...redacted..." }
            },
            "mediaGenerationId": "user:12345-email:6a6f...-video:a1d95d21-...redacted...",
            "videoUrl": "https://flow-content.google/video/a1d95d21-...redacted...?Expires=...",
            "thumbnailUrl": "https://flow-content.google/image/a1d95d21-...redacted...?Expires=..."
          }
        ],
        "remainingCredits": 14845,
        "captcha": {
          "service": "CapSolver",
          "taskId": "abc123...",
          "durationMs": 3500,
          "attempts": [
            {
              "service": "CapSolver",
              "taskId": "abc123...",
              "durationMs": 3500,
              "success": true
            }
          ]
        }
      }
    }
    

    Image job (completed):

    {
      "jobid": "j1731859345678i-u12345-email:an***@gmail.com-bot:google-flow",
      "type": "image",
      "status": "completed",
      "created": "2025-11-17T12:45:12.345Z",
      "updated": "2025-11-17T12:45:34.678Z",
      "request": {
        "email": "an***@gmail.com",
        "prompt": "A serene mountain landscape at sunset with vibrant colors",
        "model": "nano-banana-2-lite",
        "aspectRatio": "landscape",
        "count": 4,
        "replyUrl": "https://your-domain.com/webhook",
        "replyRef": "custom-reference-123"
      },
      "response": {
        "media": [
          {
            "name": "…redacted…",
            "image": {
              "generatedImage": {
                "seed": 987654,
                "mediaGenerationId": "user:12345…redacted…",
                "fifeUrl": "https://flow-content.google/image/…redacted…?Expires=…&KeyName=labs-flow-prod-cdn-key&Signature=…",
                "prompt": "A serene mountain landscape at sunset with vibrant colors"
              }
            }
          }
        ],
        "captcha": {
          "service": "AntiCaptcha",
          "taskId": "abc123...",
          "durationMs": 3200,
          "attempts": [
            {
              "service": "AntiCaptcha",
              "taskId": "abc123...",
              "durationMs": 3200,
              "success": true
            }
          ]
        }
      }
    }
    

    Job failed:

    {
      "jobid": "j1731859567890i-u12345-email:an***@gmail.com-bot:google-flow",
      "type": "image",
      "status": "failed",
      "created": "2025-11-17T13:00:12.345Z",
      "updated": "2025-11-17T13:00:45.678Z",
      "request": {
        "email": "an***@gmail.com",
        "prompt": "Generate an image",
        "model": "nano-banana-2"
      },
      "error": "API error: 500",
      "code": 500,
      "response": {
        "error": {
          "code": 500,
          "message": "Internal error encountered.",
          "status": "INTERNAL"
        }
      }
    }
    
  • 401 Unauthorized

    Invalid API token.

    {
      "error": "Unauthorized"
    }
    
  • 403 Forbidden

    Job belongs to a different user.

    {
      "error": "Access denied - job belongs to different user"
    }
    
  • 404 Not Found

    Job not found in database.

    {
      "error": "Job not found"
    }
    
  • 400 Bad Request

    Invalid job ID format.

    {
      "error": "Invalid job ID format"
    }
    
  • 410 Gone

    Job has expired (jobs are retained for 7 days).

    {
      "error": "Job has expired"
    }
    

Model

  • Video generation job structure.

    {
      jobid: string                       // Unique job identifier
      type: 'video'                       // Job type
      status: 'created' | 'started' | 'completed' | 'failed'
      created: string                     // ISO 8601 creation timestamp
      updated?: string                    // ISO 8601 last update timestamp
    
      request: {
        email?: string
        prompt: string
        model?: 'veo-3.1-quality' | 'veo-3.1-fast' | 'veo-3.1-lite' | 'veo-3.1-lite-low-priority'
        aspectRatio?: 'landscape' | 'portrait' | 'square'
        count?: number                    // 1-4
        seed?: number
        startImage?: string               // mediaGenerationId for I2V mode
        endImage?: string                 // mediaGenerationId for I2V-FL mode
        referenceImage_1?: string         // mediaGenerationId for R2V mode
        referenceImage_2?: string         // mediaGenerationId for R2V mode
        referenceImage_3?: string         // mediaGenerationId for R2V mode
        async?: boolean                   // Fire-and-forget mode
        replyUrl?: string                 // Webhook URL for callbacks
        replyRef?: string                 // Custom reference for callbacks
      }
    
      response?: {
        media: Array<{
          name: string                    // Media identifier (raw UUID)
          projectId: string
          workflowId?: string
          workflowStepId?: string         // e.g. "CAE"
          mediaMetadata: {
            createTime?: string           // ISO 8601 timestamp
            mediaTitle?: string           // Original prompt
            requestData?: object          // Full Google Flow request payload (model, mode, refs, etc.)
            mediaStatus: {
              mediaGenerationStatus: string  // MEDIA_GENERATION_STATUS_SUCCESSFUL | FAILED
              error?: { code: number; message: string }
            }
            visibility?: string           // "PRIVATE"
          }
          video?: {
            generatedVideo: {
              seed: number
              prompt?: string
              model: string               // veo_3_1_t2v | veo_3_1_i2v | veo_3_1_i2v_fl | veo_3_1_r2v | omni_flash_* etc.
              baseImageMediaGenerationId?: string  // Source media ID for I2V/R2V (empty string for T2V)
              isLooped: boolean
              aspectRatio: string         // VIDEO_ASPECT_RATIO_LANDSCAPE | PORTRAIT
            }
            dimensions?: { length?: string }  // Duration string (e.g. "8s")
            operation: { name: string }
          }
          mediaGenerationId: string       // Encoded reference ID for use in subsequent API calls
          videoUrl?: string               // Signed video download URL (MP4, valid ~6h)
          thumbnailUrl?: string           // Signed thumbnail download URL (JPEG, valid ~6h)
        }>
        remainingCredits?: number
        captcha?: {                       // Captcha metadata (for debugging/research)
          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
          }>
        }
      }
    
      error?: string                      // Error message (if failed)
      errorDetails?: string               // Additional error details
      code?: number                       // HTTP status code (if failed)
    }
    
  • Image generation job structure.

    {
      jobid: string                       // Unique job identifier
      type: 'image'                       // Job type
      status: 'created' | 'started' | 'completed' | 'failed'
      created: string                     // ISO 8601 creation timestamp
      updated?: string                    // ISO 8601 last update timestamp
    
      request: {
        email?: string
        prompt: string
        model?: 'nano-banana-2-lite' | 'nano-banana-2' | 'nano-banana-pro'  // older jobs may carry 'imagen-4' | 'nano-banana'
        aspectRatio?: 'landscape' | 'portrait'
        count?: number                    // 1-4
        seed?: number
        reference_1?: string              // mediaGenerationId
        reference_2?: string              // mediaGenerationId
        reference_3?: string              // mediaGenerationId
        replyUrl?: string                 // Webhook URL for callbacks
        replyRef?: string                 // Custom reference for callbacks
      }
    
      response?: {
        media: Array<{
          name: string
          image: {
            generatedImage: {
              seed: number
              mediaGenerationId: string
              fifeUrl?: string            // Signed image URL (valid ~6h). Absent if the URL could not be produced
              encodedImage?: string       // Base64 image bytes. Present only when fifeUrl is absent
              prompt: string
            }
          }
        }>
        captcha?: {                       // Captcha metadata (for debugging/research)
          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
          }>
        }
      }
    
      error?: string                      // Error message (if failed)
      errorDetails?: string               // Additional error details
      code?: number                       // HTTP status code (if failed)
    }
    

Missing media URLs

Media URL fields are optional and can be absent even when generation succeeded. When videoUrl, thumbnailUrl, fifeUrl, servingBaseUri, previewUrl or audioUrl is missing, the media itself is fine — only the download link could not be produced at that moment.

This is rare. Across the seven days of request logs we keep, it happened on one day only — twice, for about four hours and about forty minutes — and not at all on the other six. Google issues those links from an endpoint that limits how often it will answer a given network address, so it is Google limiting our outbound calls rather than useapi.net limiting you, and nothing you do triggers it. While it lasts it affects every account generating at that moment, then clears on its own. Your generation still completed and was charged as usual.

On a media item that generated successfully — mediaStatus.mediaGenerationStatus is MEDIA_GENERATION_STATUS_SUCCESSFUL — a missing URL means “not yet”, not “failed”, so read the field before using it and treat its absence as a retry condition. A media item that failed generation never carries a URL, and retrying will not produce one.

const url = media.videoUrl;
if (!url) {
  // Not an error — the link is not available yet. See the options below.
}

Which route to use depends on whether you can wait.

If you are already polling the job, do nothing special. Completed video jobs re-resolve missing URLs automatically on GET /jobs/jobId, and the recovered link is saved, so a later poll returns it with no extra work on your side. This applies to video jobs created within the last 24 hours. Once the job is completed and a URL is still missing, leave at least a minute between polls — a job re-resolves at most once a minute, and the block clears only once traffic stops, so a tight retry loop lengthens the wait rather than shortening it.

If you hold a mediaGenerationId but no longer have the job, ask for that one media again with GET /assets/mediaGenerationId. That endpoint says which case you are in rather than leaving you to guess. A 503 with a Retry-After header means the link is coming and tells you how long to wait, so wait that long and ask again. A 404 means Google has no such media, and no amount of retrying will change it.

If you cannot wait, download the video through us with GET /assets/mediaGenerationId?raw=true. It reads the media over a different Google route that the block does not touch, so it works immediately and keeps working while the other two return nothing. It is video only.

Prefer the two link-based routes when you can. They hand you a link and you download straight from Google. The raw form moves the whole file through useapi.net on every call, which makes it a way out of a stuck download rather than your normal download path.

Characters and voices resolve their URLs as they are read, so simply requesting GET /characters/ref or GET /voices/ref again is enough. Wait a minute before retrying — a response that came back without its URL is held briefly, so an immediate repeat returns the same one.

None of this applies to images. Whenever an image URL is missing, POST /images puts the image itself in the response instead, base64-encoded in encodedImage — so you already have the picture and there is nothing to retry.

Examples

  • # Get job status
    curl "https://api.useapi.net/v1/google-flow/jobs/j1731859234567v-u12345-email:jo***@gmail.com-bot:google-flow" \
      -H "Authorization: Bearer {API token}"
    
    # Poll for completion (check every 5 seconds)
    while true; do
      STATUS=$(curl -s "https://api.useapi.net/v1/google-flow/jobs/j1731859234567v-u12345-email:jo***@gmail.com-bot:google-flow" \
        -H "Authorization: Bearer {API token}" | jq -r '.status')
    
      echo "Status: $STATUS"
    
      if [[ "$STATUS" == "completed" || "$STATUS" == "failed" ]]; then
        break
      fi
    
      sleep 5
    done
    
  • const apiToken = 'your-api-token'
    const jobId = 'j1731859234567v-u12345-email:jo***@gmail.com-bot:google-flow'
    
    // Get job status
    async function getJobStatus(jobId) {
      const response = await fetch(`https://api.useapi.net/v1/google-flow/jobs/${jobId}`, {
        headers: {
          'Authorization': `Bearer ${apiToken}`
        }
      })
    
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${await response.text()}`)
      }
    
      return await response.json()
    }
    
    // Poll until completion
    async function waitForCompletion(jobId, intervalMs = 5000) {
      while (true) {
        const job = await getJobStatus(jobId)
        console.log(`Status: ${job.status}`)
    
        if (job.status === 'completed') {
          console.log('Job completed!', job.response)
          return job
        }
    
        if (job.status === 'failed') {
          console.error('Job failed:', job.error)
          throw new Error(job.error)
        }
    
        await new Promise(resolve => setTimeout(resolve, intervalMs))
      }
    }
    
    // Usage
    const job = await waitForCompletion(jobId)
    
  • import requests
    import time
    
    api_token = 'your-api-token'
    job_id = 'j1731859234567v-u12345-email:jo***@gmail.com-bot:google-flow'
    
    # Get job status
    def get_job_status(job_id: str) -> dict:
        response = requests.get(
            f'https://api.useapi.net/v1/google-flow/jobs/{job_id}',
            headers={'Authorization': f'Bearer {api_token}'}
        )
        response.raise_for_status()
        return response.json()
    
    # Poll until completion
    def wait_for_completion(job_id: str, interval_sec: int = 5) -> dict:
        while True:
            job = get_job_status(job_id)
            print(f"Status: {job['status']}")
    
            if job['status'] == 'completed':
                print("Job completed!", job['response'])
                return job
    
            if job['status'] == 'failed':
                raise Exception(f"Job failed: {job.get('error')}")
    
            time.sleep(interval_sec)
    
    # Usage
    job = wait_for_completion(job_id)
    
    # Access video URLs (for video jobs)
    if job['type'] == 'video':
        for item in job['response']['media']:
            if 'videoUrl' in item:
                print(f"Video URL: {item['videoUrl']}")
    
    # Access image URLs (for image jobs)
    if job['type'] == 'image':
        for media in job['response']['media']:
            if 'image' in media and 'generatedImage' in media['image']:
                print(f"Image URL: {media['image']['generatedImage']['fifeUrl']}")
    

Try It