> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pictory.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Video Generation Job

> Retrieve the status and output of an AI video generation job

## Overview

This endpoint retrieves the current status and output of an AI video generation job using its unique job ID. While the generation is in progress, it returns the job status. Once the video generation completes, it returns the output including the video URL, thumbnail images, dimensions, duration, and AI credits consumed.

<Note>
  A valid API key is required to use this endpoint. Obtain your API key from the [API Access page](https://app.pictory.ai/api-access) in your Pictory dashboard.
</Note>

***

## API Endpoint

```http theme={null}
GET https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}
```

***

## Request Parameters

### Path Parameters

<ParamField path="jobid" type="uuid" required>
  The unique identifier (UUID) of the video generation job. This is the `jobId` returned by the [Generate Video](/api-reference/ai-studio/generate-video) endpoint.

  **Example:** `"4a09edf9-d071-4847-bda5-8dea913d80fe"`
</ParamField>

### Headers

<ParamField header="Authorization" type="string" required>
  API key for authentication (starts with `pictai_`)

  ```
  Authorization: YOUR_API_KEY
  ```
</ParamField>

***

## Response

### In-Progress Response

Returned while the video is being generated:

<ResponseField name="job_id" type="string">
  The unique identifier of the video generation job
</ResponseField>

<ResponseField name="success" type="boolean">
  `true` while the job is processing
</ResponseField>

<ResponseField name="data" type="object">
  <ResponseField name="status" type="string">
    `"in-progress"` while the video is still being generated
  </ResponseField>
</ResponseField>

### Completed Response

Returned when the video has been generated successfully:

<ResponseField name="job_id" type="string">
  The unique identifier of the video generation job
</ResponseField>

<ResponseField name="success" type="boolean">
  `true` when the job has completed successfully
</ResponseField>

<ResponseField name="data" type="object">
  Contains the video generation output.

  <ResponseField name="status" type="string">
    `"completed"` when the video has been generated successfully
  </ResponseField>

  <ResponseField name="url" type="string">
    Direct download URL for the generated video file (MP4)
  </ResponseField>

  <ResponseField name="duration" type="string">
    Duration of the generated video (e.g., `"8s"`)
  </ResponseField>

  <ResponseField name="width" type="number">
    Width of the generated video in pixels
  </ResponseField>

  <ResponseField name="height" type="number">
    Height of the generated video in pixels
  </ResponseField>

  <ResponseField name="thumbnailImageUrl" type="string">
    URL for the video thumbnail image (JPG)
  </ResponseField>

  <ResponseField name="lastFrameImageUrl" type="string">
    URL for the last frame of the video (PNG). This can be used as a `firstFrameImageUrl` in a subsequent generate video request to create a seamless continuation.
  </ResponseField>

  <ResponseField name="previewImageUrl" type="string">
    URL for the video preview image (JPG)
  </ResponseField>

  <ResponseField name="aiCreditsUsed" type="number">
    Number of AI credits consumed for this video generation
  </ResponseField>
</ResponseField>

### Failed Response

Returned when the video generation job has failed:

<ResponseField name="job_id" type="string">
  The unique identifier of the video generation job
</ResponseField>

<ResponseField name="success" type="boolean">
  `false` when the job has failed
</ResponseField>

<ResponseField name="data" type="object">
  <ResponseField name="status" type="string">
    `"failed"` when the video generation has failed
  </ResponseField>

  <ResponseField name="error_code" type="string">
    Error code identifying the failure type (e.g., `"5001"`)
  </ResponseField>

  <ResponseField name="error_message" type="string">
    Descriptive message explaining the cause of the failure
  </ResponseField>
</ResponseField>

### Response Examples

<ResponseExample>
  ```json 200 - In Progress theme={null}
  {
      "job_id": "4a09edf9-d071-4847-bda5-8dea913d80fe",
      "success": true,
      "data": {
          "status": "in-progress"
      }
  }
  ```

  ```json 200 - Completed theme={null}
  {
      "job_id": "4a09edf9-d071-4847-bda5-8dea913d80fe",
      "success": true,
      "data": {
          "duration": "8s",
          "thumbnailImageUrl": "https://example.cloudfront.net/videos/user/a1b2c3d4-e5f6-7890-abcd-ef1234567890/d7c6b5a4-e3f2-1098-dcba-fedcba987654_thumbnail.jpg",
          "lastFrameImageUrl": "https://example.cloudfront.net/videos/user/a1b2c3d4-e5f6-7890-abcd-ef1234567890/d7c6b5a4-e3f2-1098-dcba-fedcba987654_last_frame.png",
          "previewImageUrl": "https://example.cloudfront.net/videos/user/a1b2c3d4-e5f6-7890-abcd-ef1234567890/d7c6b5a4-e3f2-1098-dcba-fedcba987654_preview.jpg",
          "width": 720,
          "aiCreditsUsed": 12.8,
          "height": 1280,
          "url": "https://example.cloudfront.net/videos/user/a1b2c3d4-e5f6-7890-abcd-ef1234567890/d7c6b5a4-e3f2-1098-dcba-fedcba987654.mp4",
          "status": "completed"
      }
  }
  ```

  ```json 200 - Failed theme={null}
  {
      "job_id": "4a09edf9-d071-4847-bda5-8dea913d80fe",
      "success": false,
      "data": {
          "status": "failed",
          "error_code": "5001",
          "error_message": "Video generation failed due to an internal error."
      }
  }
  ```

  ```json 200 - Invalid Job ID theme={null}
  {
      "id": "4a09edf9-d071-4847-bda5-8dea913d80fe",
      "success": false,
      "data": {
          "error_code": "5000",
          "error_message": "JOB_NOT_FOUND"
      }
  }
  ```

  ```json 401 - Unauthorized theme={null}
  {
      "message": "Unauthorized"
  }
  ```
</ResponseExample>

***

## Status Codes

| Status Code | Description                                                                                                                              |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **200**     | Request processed successfully. Check the `status` field in `data` to determine the job state (`in-progress`, `completed`, or `failed`). |
| **401**     | Unauthorized. The API key in the `Authorization` header is missing or invalid.                                                           |
| **404**     | Job not found. The provided `jobid` does not match any existing job.                                                                     |
| **500**     | Internal server error. Retry the request after a brief delay.                                                                            |

***

## Completed Response Fields

| Field               | Description                                                            |
| ------------------- | ---------------------------------------------------------------------- |
| `url`               | Direct download link for the generated MP4 video file                  |
| `duration`          | Duration of the generated video (e.g., `"8s"`)                         |
| `width`             | Width of the generated video in pixels                                 |
| `height`            | Height of the generated video in pixels                                |
| `thumbnailImageUrl` | Auto-generated thumbnail image for the video (JPG)                     |
| `lastFrameImageUrl` | Last frame of the video (PNG), useful for creating video continuations |
| `previewImageUrl`   | Preview image for the video (JPG)                                      |
| `aiCreditsUsed`     | Number of AI credits consumed for this generation                      |

***

## Code Examples

<Tip>
  Replace `YOUR_API_KEY` with your actual API key and use the `jobId` returned from the [Generate Video](/api-reference/ai-studio/generate-video) endpoint.
</Tip>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.pictory.ai/pictoryapis/v1/jobs/4a09edf9-d071-4847-bda5-8dea913d80fe' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'accept: application/json' | python -m json.tool
  ```

  ```python Python theme={null}
  import requests
  import time

  def poll_video_job(api_key, job_id, max_wait=600, poll_interval=15):
      """
      Poll for video generation job completion.

      Args:
          api_key: Your Pictory API key
          job_id: The video generation job ID
          max_wait: Maximum wait time in seconds (default 10 minutes)
          poll_interval: Polling interval in seconds (default 15 seconds)
      """
      url = f"https://api.pictory.ai/pictoryapis/v1/jobs/{job_id}"
      headers = {
          "Authorization": api_key,
          "accept": "application/json"
      }

      start_time = time.time()

      while time.time() - start_time < max_wait:
          response = requests.get(url, headers=headers)
          data = response.json()

          if not data.get("success"):
              error_data = data.get("data", {})
              print(f"Job failed: {error_data.get('error_message', 'Unknown error')}")
              return data

          status = data.get("data", {}).get("status")

          if status == "in-progress":
              print("Video generation in progress...")
              time.sleep(poll_interval)
              continue

          if status == "completed":
              result = data["data"]
              print(f"Video generated successfully!")
              print(f"Video URL: {result.get('url')}")
              print(f"Duration: {result.get('duration')}")
              print(f"Dimensions: {result.get('width')}x{result.get('height')}")
              print(f"AI Credits Used: {result.get('aiCreditsUsed')}")
              return data

          if status == "failed":
              error_msg = data["data"].get("error_message", "Unknown error")
              print(f"Video generation failed: {error_msg}")
              return data

          print(f"Unexpected status: {status}")
          return data

      print("Timeout waiting for video generation")
      return None

  # Usage
  result = poll_video_job("YOUR_API_KEY", "4a09edf9-d071-4847-bda5-8dea913d80fe")
  ```

  ```javascript JavaScript theme={null}
  async function pollVideoJob(apiKey, jobId, maxWait = 600000, pollInterval = 15000) {
    const url = `https://api.pictory.ai/pictoryapis/v1/jobs/${jobId}`;
    const startTime = Date.now();

    while (Date.now() - startTime < maxWait) {
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Authorization': apiKey,
          'accept': 'application/json'
        }
      });

      const data = await response.json();

      if (!data.success) {
        console.log(`Job failed: ${data.data?.error_message || 'Unknown error'}`);
        return data;
      }

      const status = data.data?.status;

      if (status === 'in-progress') {
        console.log('Video generation in progress...');
        await new Promise(resolve => setTimeout(resolve, pollInterval));
        continue;
      }

      if (status === 'completed') {
        console.log('Video generated successfully!');
        console.log(`Video URL: ${data.data.url}`);
        console.log(`Duration: ${data.data.duration}`);
        console.log(`Dimensions: ${data.data.width}x${data.data.height}`);
        console.log(`AI Credits Used: ${data.data.aiCreditsUsed}`);
        return data;
      }

      if (status === 'failed') {
        console.log(`Video generation failed: ${data.data?.error_message}`);
        return data;
      }

      console.log(`Unexpected status: ${status}`);
      return data;
    }

    console.log('Timeout waiting for video generation');
    return null;
  }

  // Usage
  const result = await pollVideoJob('YOUR_API_KEY', '4a09edf9-d071-4847-bda5-8dea913d80fe');
  ```

  ```php PHP theme={null}
  <?php
  function pollVideoJob($apiKey, $jobId, $maxWait = 600, $pollInterval = 15) {
      $url = "https://api.pictory.ai/pictoryapis/v1/jobs/{$jobId}";
      $startTime = time();

      while (time() - $startTime < $maxWait) {
          $ch = curl_init($url);
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_HTTPHEADER, [
              'Authorization: ' . $apiKey,
              'accept: application/json'
          ]);

          $response = curl_exec($ch);
          curl_close($ch);

          $data = json_decode($response, true);

          if (!$data['success']) {
              echo "Job failed: " . ($data['data']['error_message'] ?? 'Unknown error') . "\n";
              return $data;
          }

          $status = $data['data']['status'] ?? null;

          if ($status === 'in-progress') {
              echo "Video generation in progress...\n";
              sleep($pollInterval);
              continue;
          }

          if ($status === 'completed') {
              echo "Video generated successfully!\n";
              echo "Video URL: " . ($data['data']['url'] ?? '') . "\n";
              echo "Duration: " . ($data['data']['duration'] ?? '') . "\n";
              echo "Dimensions: " . ($data['data']['width'] ?? 0) . "x" . ($data['data']['height'] ?? 0) . "\n";
              echo "AI Credits Used: " . ($data['data']['aiCreditsUsed'] ?? 0) . "\n";
              return $data;
          }

          if ($status === 'failed') {
              echo "Video generation failed: " . ($data['data']['error_message'] ?? 'Unknown error') . "\n";
              return $data;
          }

          echo "Unexpected status: {$status}\n";
          return $data;
      }

      echo "Timeout waiting for video generation\n";
      return null;
  }

  // Usage
  $result = pollVideoJob('YOUR_API_KEY', '4a09edf9-d071-4847-bda5-8dea913d80fe');
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "encoding/json"
      "fmt"
      "io"
      "net/http"
      "time"
  )

  func pollVideoJob(apiKey, jobID string, maxWait, pollInterval time.Duration) (map[string]interface{}, error) {
      url := fmt.Sprintf("https://api.pictory.ai/pictoryapis/v1/jobs/%s", jobID)
      startTime := time.Now()

      for time.Since(startTime) < maxWait {
          req, _ := http.NewRequest("GET", url, nil)
          req.Header.Set("Authorization", apiKey)
          req.Header.Set("accept", "application/json")

          client := &http.Client{}
          resp, err := client.Do(req)
          if err != nil {
              return nil, err
          }

          body, _ := io.ReadAll(resp.Body)
          resp.Body.Close()

          var data map[string]interface{}
          json.Unmarshal(body, &data)

          success, _ := data["success"].(bool)
          if !success {
              fmt.Println("Job failed")
              return data, nil
          }

          jobData, _ := data["data"].(map[string]interface{})
          status, _ := jobData["status"].(string)

          if status == "in-progress" {
              fmt.Println("Video generation in progress...")
              time.Sleep(pollInterval)
              continue
          }

          if status == "completed" {
              fmt.Println("Video generated successfully!")
              if videoURL, ok := jobData["url"].(string); ok {
                  fmt.Printf("Video URL: %s\n", videoURL)
              }
              if duration, ok := jobData["duration"].(string); ok {
                  fmt.Printf("Duration: %s\n", duration)
              }
              return data, nil
          }

          if status == "failed" {
              if msg, ok := jobData["error_message"].(string); ok {
                  fmt.Printf("Video generation failed: %s\n", msg)
              }
              return data, nil
          }

          fmt.Printf("Unexpected status: %s\n", status)
          return data, nil
      }

      return nil, fmt.Errorf("timeout waiting for video generation")
  }

  func main() {
      result, err := pollVideoJob(
          "YOUR_API_KEY",
          "4a09edf9-d071-4847-bda5-8dea913d80fe",
          10*time.Minute,
          15*time.Second,
      )
      if err != nil {
          fmt.Println("Error:", err)
          return
      }

      if success, ok := result["success"].(bool); ok && success {
          data := result["data"].(map[string]interface{})
          if videoURL, ok := data["url"].(string); ok {
              fmt.Printf("\nVideo: %s\n", videoURL)
          }
      }
  }
  ```
</CodeGroup>

***

## Polling Best Practices

<Warning>
  Use a polling interval of **10–30 seconds** when checking job status. Polling too frequently may result in rate limiting.
</Warning>

1. **Use webhooks when possible.** Configure a `webhook` URL in the original generate video request to receive automatic notification when the job completes, rather than polling.

2. **Implement timeouts.** Set a reasonable maximum wait time. Video generation can take several minutes depending on the model and duration selected.

3. **Handle failures gracefully.** Inspect `error_code` and `error_message` in failed responses to determine the cause and whether the request can be retried.

4. **Use the last frame for continuations.** The `lastFrameImageUrl` from a completed job can be passed as `firstFrameImageUrl` in a new generate video request to create a seamless video continuation.
