Skip to main content
GET
/
pictoryapis
/
v1
/
jobs
/
{jobid}
Get Video Render Job by ID
curl --request GET \
  --url https://api.pictory.ai/pictoryapis/v1/jobs/{jobid} \
  --header 'Authorization: <authorization>'
import requests

url = "https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}"

headers = {"Authorization": "<authorization>"}

response = requests.get(url, headers=headers)

print(response.text)
const options = {method: 'GET', headers: {Authorization: '<authorization>'}};

fetch('https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}"

req, _ := http.NewRequest("GET", url, nil)

req.Header.Add("Authorization", "<authorization>")

res, _ := http.DefaultClient.Do(req)

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

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}")
.header("Authorization", "<authorization>")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = '<authorization>'

response = http.request(request)
puts response.read_body
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": true,
    "data": {
        "status": "in-progress"
    }
}
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": true,
    "data": {
        "status": "in-progress",
        "renderProgress": 2,
        "renderProgressMessage": "Generating video",
        "renderState": "RUNNING"
    }
}
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": true,
    "data": {
        "status": "completed",
        "progress": 100,
        "videoURL": "https://d3uryq9bhgb5qr.cloudfront.net/.../demo_text_to_video3.mp4",
        "videoShareURL": "https://video.pictory.ai/.../20260326170050790Tkrsq44GvHibYw5",
        "videoEmbedURL": "https://video.pictory.ai/embed/.../20260326170050790Tkrsq44GvHibYw5",
        "audioURL": "https://d3uryq9bhgb5qr.cloudfront.net/.../demo_text_to_video3.mp3",
        "thumbnail": "https://d3uryq9bhgb5qr.cloudfront.net/.../265a7c1a-4985-4058-9208-68114f131a2b.jpg",
        "srtFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../6392d927-1ccd-4ea0-a167-a6ad7b643132.srt",
        "txtFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../438e574f-c9cb-4eed-82ea-8cf0b6629664.txt",
        "vttFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../a6cc12f5-f016-481f-ab0c-44cba00e048e.vtt",
        "videoDuration": 65.6,
        "encodingDuration": 117,
        "aiCreditsUsed": 48
    }
}
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": false,
    "data": {
        "status": "failed",
        "error_code": "TEXT_TO_VIDEO_FAILED",
        "error_message": "The AI voice speaker [Timm] is invalid and is not supported. Please provide valid AI voice speaker."
    }
}
{
    "id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": false,
    "data": {
        "error_code": "5000",
        "error_message": "JOB_NOT_FOUND"
    }
}
{
    "message": "Unauthorized"
}

Overview

This endpoint retrieves the current status and results of a video render job using its unique job ID. While rendering is in progress, it returns the job status along with render progress percentage and state. Once the render completes, it returns the full set of output URLs for the rendered video, audio, subtitles, and thumbnail.
A valid API key is required to use this endpoint. Obtain your API key from the API Access page in your Pictory dashboard.

API Endpoint

GET https://api.pictory.ai/pictoryapis/v1/jobs/{jobid}

Request Parameters

Path Parameters

jobid
uuid
required
The unique identifier (UUID) of the video render job. This value is the jobId returned by the Render from Preview, Render Video, Render Storyboard Video, or Render Project endpoints.Example: "265a7c1a-4985-4058-9208-68114f131a2b"

Headers

Authorization
string
required
API key for authentication (starts with pictai_)
Authorization: YOUR_API_KEY

Response

In-Progress Response

Returned while the video is being rendered. The response includes progress information once rendering has started:
job_id
string
The unique identifier of the render job
success
boolean
true while the job is processing
data
object
status
string
"in-progress" — the video is still being rendered
renderProgress
number
Rendering progress as a percentage (0–100). Present only after rendering has started.
renderProgressMessage
string
Descriptive progress message (e.g., "Generating video"). Present only after rendering has started.
renderState
string
Current render state (e.g., "RUNNING"). Present only after rendering has started.

Failed Response

Returned when the render job has failed:
job_id
string
The unique identifier of the render job
success
boolean
false when the job has failed
data
object
status
string
"failed" — the video render has failed
error_code
string
Error code identifying the failure type
error_message
string
Descriptive message explaining the cause of the failure

Completed Response

Returned when the render has finished successfully. The response contains all output URLs:
job_id
string
The unique identifier of the render job
success
boolean
true when the job has completed successfully
data
object
Contains the complete render output.
status
string
"completed" — the video has been rendered successfully
progress
number
100 — rendering is complete
videoURL
string
Direct download URL for the rendered video file (MP4)
videoShareURL
string
Shareable URL for the rendered video
videoEmbedURL
string
Embeddable URL for the rendered video, suitable for iframe integration
audioURL
string
Direct download URL for the audio track (MP3)
thumbnail
string
URL for the video thumbnail image (JPG)
srtFile
string
URL for the SRT subtitle file
txtFile
string
URL for the plain text transcript file
vttFile
string
URL for the WebVTT subtitle file
videoDuration
number
Total duration of the rendered video in seconds
encodingDuration
number
Time taken to encode and render the video in seconds
aiCreditsUsed
number
Total AI credits consumed for generating AI visuals (images and video clips) during rendering. Present only when the video includes scenes with aiVisual configuration. The value is the sum of credits used across all AI-generated scenes.

Response Examples

{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": true,
    "data": {
        "status": "in-progress"
    }
}
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": true,
    "data": {
        "status": "in-progress",
        "renderProgress": 2,
        "renderProgressMessage": "Generating video",
        "renderState": "RUNNING"
    }
}
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": true,
    "data": {
        "status": "completed",
        "progress": 100,
        "videoURL": "https://d3uryq9bhgb5qr.cloudfront.net/.../demo_text_to_video3.mp4",
        "videoShareURL": "https://video.pictory.ai/.../20260326170050790Tkrsq44GvHibYw5",
        "videoEmbedURL": "https://video.pictory.ai/embed/.../20260326170050790Tkrsq44GvHibYw5",
        "audioURL": "https://d3uryq9bhgb5qr.cloudfront.net/.../demo_text_to_video3.mp3",
        "thumbnail": "https://d3uryq9bhgb5qr.cloudfront.net/.../265a7c1a-4985-4058-9208-68114f131a2b.jpg",
        "srtFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../6392d927-1ccd-4ea0-a167-a6ad7b643132.srt",
        "txtFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../438e574f-c9cb-4eed-82ea-8cf0b6629664.txt",
        "vttFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../a6cc12f5-f016-481f-ab0c-44cba00e048e.vtt",
        "videoDuration": 65.6,
        "encodingDuration": 117,
        "aiCreditsUsed": 48
    }
}
{
    "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": false,
    "data": {
        "status": "failed",
        "error_code": "TEXT_TO_VIDEO_FAILED",
        "error_message": "The AI voice speaker [Timm] is invalid and is not supported. Please provide valid AI voice speaker."
    }
}
{
    "id": "265a7c1a-4985-4058-9208-68114f131a2b",
    "success": false,
    "data": {
        "error_code": "5000",
        "error_message": "JOB_NOT_FOUND"
    }
}
{
    "message": "Unauthorized"
}

Completed Response Fields

FieldDescription
videoURLDirect download link for the rendered MP4 video file
videoShareURLShareable link to view the video on Pictory
videoEmbedURLEmbeddable URL suitable for iframe integration
audioURLDirect download link for the extracted MP3 audio track
thumbnailURL for the auto-generated video thumbnail (JPG)
srtFileSRT subtitle file compatible with standard video players
txtFilePlain text transcript file
vttFileWebVTT subtitle file for web-based video players
videoDurationTotal video length in seconds
encodingDurationTotal rendering time in seconds
aiCreditsUsedTotal AI credits consumed for generating AI visuals (images and video clips). Present only when the video includes scenes with aiVisual configuration.

Code Examples

Replace YOUR_API_KEY with your actual API key and use the jobId returned from any render endpoint.
curl --request GET \
  --url 'https://api.pictory.ai/pictoryapis/v1/jobs/265a7c1a-4985-4058-9208-68114f131a2b' \
  --header 'Authorization: YOUR_API_KEY' \
  --header 'accept: application/json' | python -m json.tool
import requests
import time

def poll_render_job(api_key, job_id, max_wait=900, poll_interval=15):
    """
    Poll for video render job completion.

    Args:
        api_key: Your Pictory API key
        job_id: The render job ID
        max_wait: Maximum wait time in seconds (default 15 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":
            progress = data["data"].get("renderProgress", 0)
            message = data["data"].get("renderProgressMessage", "Processing")
            print(f"Rendering... {progress}% - {message}")
            time.sleep(poll_interval)
            continue

        if status == "completed":
            result = data["data"]
            print(f"Render complete!")
            print(f"Video URL: {result.get('videoURL')}")
            print(f"Duration: {result.get('videoDuration')}s")
            print(f"Encoding time: {result.get('encodingDuration')}s")
            return data

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

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

    print("Timeout waiting for render")
    return None

# Usage
result = poll_render_job("YOUR_API_KEY", "265a7c1a-4985-4058-9208-68114f131a2b")

if result and result.get("success") and result["data"].get("status") == "completed":
    data = result["data"]
    print(f"\nVideo: {data['videoURL']}")
    print(f"Share: {data['videoShareURL']}")
    print(f"Thumbnail: {data['thumbnail']}")
    print(f"SRT: {data['srtFile']}")
const jobId = '265a7c1a-4985-4058-9208-68114f131a2b';

async function pollRenderJob(apiKey, jobId, maxWait = 900000, 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') {
      const progress = data.data?.renderProgress || 0;
      const message = data.data?.renderProgressMessage || 'Processing';
      console.log(`Rendering... ${progress}% - ${message}`);
      await new Promise(resolve => setTimeout(resolve, pollInterval));
      continue;
    }

    if (status === 'completed') {
      console.log('Render complete!');
      console.log(`Video URL: ${data.data.videoURL}`);
      console.log(`Duration: ${data.data.videoDuration}s`);
      return data;
    }

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

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

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

// Usage
const result = await pollRenderJob('YOUR_API_KEY', jobId);

if (result?.success && result.data?.status === 'completed') {
  console.log(`\nVideo: ${result.data.videoURL}`);
  console.log(`Share: ${result.data.videoShareURL}`);
}
<?php
function pollRenderJob($apiKey, $jobId, $maxWait = 900, $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') {
            $progress = $data['data']['renderProgress'] ?? 0;
            $message = $data['data']['renderProgressMessage'] ?? 'Processing';
            echo "Rendering... {$progress}% - {$message}\n";
            sleep($pollInterval);
            continue;
        }

        if ($status === 'completed') {
            echo "Render complete!\n";
            echo "Video URL: " . ($data['data']['videoURL'] ?? '') . "\n";
            echo "Duration: " . ($data['data']['videoDuration'] ?? 0) . "s\n";
            return $data;
        }

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

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

    echo "Timeout waiting for render\n";
    return null;
}

// Usage
$result = pollRenderJob('YOUR_API_KEY', '265a7c1a-4985-4058-9208-68114f131a2b');

if ($result && $result['success'] && ($result['data']['status'] ?? '') === 'completed') {
    echo "\nVideo: " . $result['data']['videoURL'] . "\n";
    echo "Share: " . $result['data']['videoShareURL'] . "\n";
}
?>
package main

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

func pollRenderJob(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" {
            progress, _ := jobData["renderProgress"].(float64)
            fmt.Printf("Rendering... %.0f%%\n", progress)
            time.Sleep(pollInterval)
            continue
        }

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

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

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

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

func main() {
    result, err := pollRenderJob(
        "YOUR_API_KEY",
        "265a7c1a-4985-4058-9208-68114f131a2b",
        15*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["videoURL"].(string); ok {
            fmt.Printf("\nVideo: %s\n", videoURL)
        }
    }
}

Render Progress Stages

The render job progresses through the following stages:
StagestatusrenderStateDescription
Queuedin-progressJob is queued; pre-processing has not started
Processingin-progressRUNNINGPre-render processing (scene composition, audio mixing)
Renderingin-progressRUNNINGVideo frame rendering is in progress
CompletedcompletedVideo is ready for download
FailedfailedRender has failed; check error_code and error_message for details

Polling Best Practices

Use a polling interval of 10–30 seconds when checking job status. Polling too frequently may result in rate limiting.
  1. Use webhooks when possible. Configure a webhook URL in the original render request to receive automatic notification when the job completes, rather than polling.
  2. Monitor renderProgress. Use the renderProgress field to display progress to users. This field provides a percentage value from 0 to 100.
  3. Implement timeouts. Set a maximum wait time based on expected video length. Longer videos require more rendering time.
  4. Cache results. Once a render job completes, store the output URLs locally. Completed job data may be cleaned after a retention period.
  5. Handle failures gracefully. Inspect error_code and error_message in failed responses to determine whether the issue is recoverable (for example, an invalid voice name can be corrected and the request resubmitted).