Render Video with Modifications
curl --request POST \
--url https://api.pictory.ai/pictoryapis/v2/video/render \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>'import requests
url = "https://api.pictory.ai/pictoryapis/v2/video/render"
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'}
};
fetch('https://api.pictory.ai/pictoryapis/v2/video/render', 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/v2/video/render",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$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/v2/video/render"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pictory.ai/pictoryapis/v2/video/render")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pictory.ai/pictoryapis/v2/video/render")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"jobId": "265a7c1a-4985-4058-9208-68114f131a2b"
}
}
{
"message": "Unauthorized"
}
Videos
Render Video with Modifications
Render final video from modified storyboard elements returned in the preview response
POST
/
pictoryapis
/
v2
/
video
/
render
Render Video with Modifications
curl --request POST \
--url https://api.pictory.ai/pictoryapis/v2/video/render \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>'import requests
url = "https://api.pictory.ai/pictoryapis/v2/video/render"
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.post(url, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'}
};
fetch('https://api.pictory.ai/pictoryapis/v2/video/render', 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/v2/video/render",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$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/v2/video/render"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pictory.ai/pictoryapis/v2/video/render")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pictory.ai/pictoryapis/v2/video/render")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"jobId": "265a7c1a-4985-4058-9208-68114f131a2b"
}
}
{
"message": "Unauthorized"
}
Overview
The Render Video API generates a final video from storyboard elements that can be modified before rendering. This endpoint uses therenderParams property returned from a completed Create Storyboard Preview job, allowing you to make changes to scenes, visuals, text, and other elements before producing the final video.
This endpoint requires the
renderParams object from a completed preview job. First create a preview, retrieve the job output to get renderParams, modify as needed, then submit to this endpoint.Use this endpoint when you need to modify the preview - change scenes, update visuals, adjust timing, or customize content before final render.
Render Workflow Options
| Workflow | API | When to Use |
|---|---|---|
| Create Preview | Create Storyboard Preview | Generate preview to review scenes before rendering |
| Render from Preview | Render from Preview | Render preview as-is without modifications |
| Render with Modifications | This API | Modify preview elements before rendering |
| Render Saved Project | Render Project | Render existing project created in App or via API |
| Direct Render | Render Storyboard Video | Skip preview, render directly from input |
When to Use This Endpoint
Scene Modifications
Add, remove, or reorder scenes from the preview
Visual Replacements
Replace AI-selected backgrounds with custom visuals
Text Updates
Modify subtitle text or scene content
Timing Adjustments
Change scene durations or transitions
API Endpoint
POST https://api.pictory.ai/pictoryapis/v2/video/render
Request Parameters
Headers
string
required
API key for authentication
Authorization: YOUR_API_KEY
string
required
Must be
application/jsonRequest Body
The request body should contain the values from therenderParams object returned in a completed preview job response. Send the renderParams content directly as the request body (not wrapped in a renderParams property). You can modify any properties before submitting.
Getting renderParams from Preview Job
After creating a storyboard preview, retrieve the job output using the Get Storyboard Preview Job by ID API. The response includes arenderParams object containing the complete storyboard configuration:
{
"job_id": "2cc183fc-f07a-4bf1-b7a0-92ec999b22f3",
"success": true,
"data": {
"status": "completed",
"renderParams": {
"output": {
"width": 1920,
"height": 1080,
"format": "mp4",
"name": "my_video.mp4",
"title": "my_video",
"description": ""
},
"elements": [
{
"type": "audio",
"id": "voiceOver",
"elementType": "audioElement",
"url": "https://...",
"segments": [...]
},
{
"type": "audio",
"id": "bgMusic",
"elementType": "audioElement",
"url": "https://...",
"segments": [...]
},
{
"id": "backgroundElement_1",
"elementType": "backgroundElement",
"type": "video",
"url": "https://...",
"visualUrl": "https://...",
"startTime": 0,
"duration": 10.53
},
{
"id": "SceneText_1_1",
"elementType": "SceneText",
"type": "text",
"text": "Your subtitle text here...",
"fontFamily": "Arial",
"fontSize": "24",
"startTime": 0,
"duration": 10.53
}
],
"sceneMarkers": [...],
"subtitles": [...],
"projectId": "1767054556293"
},
"previewUrl": "https://video.pictory.ai/v2/preview/...",
"projectUrl": "https://app.pictory.ai/v2/storyboard/...",
"projectId": "1767054556293"
}
}
renderParams directly as the request body for this API:
{
"output": {
"width": 1920,
"height": 1080,
"format": "mp4",
"name": "my_video.mp4",
"title": "my_video"
},
"elements": [...],
"sceneMarkers": [...],
"subtitles": [...],
"projectId": "1767054556293"
}
Modifiable Elements
WithinrenderParams, you can modify:
| Element | Description |
|---|---|
output | Video output settings (width, height, format, name, title) |
elements | Array of all video elements - audio, backgrounds, and text |
elements[].url | Change background video/image URL for backgroundElement types |
elements[].text | Update subtitle text for SceneText elements |
elements[].duration | Adjust element timing |
elements[].fontFamily, fontSize, fontColor | Modify text styling |
sceneMarkers | Scene timing and markers |
subtitles | Subtitle text content |
Element Types
| elementType | Description |
|---|---|
audioElement | Voice-over or background music audio |
backgroundElement | Scene background video or image |
SceneText | Subtitle/caption text overlay |
Response
When the render request is successfully submitted, a job is created and a job ID is returned.boolean
Indicates whether the request was successful
object
string
Unique identifier for the render job. Use this to track the job status and retrieve results via the Get Video Render Job by ID endpoint.
{
"success": true,
"data": {
"jobId": "265a7c1a-4985-4058-9208-68114f131a2b"
}
}
{
"message": "Unauthorized"
}
Next Steps
Once you have thejobId, poll the Get Video Render Job by ID endpoint to check the render status and retrieve the output URLs when complete. Use a polling interval of 10–30 seconds.
Code Examples
Replace
YOUR_API_KEY with your actual API key.Basic Render with Modified Elements
curl --request POST \
--url https://api.pictory.ai/pictoryapis/v2/video/render \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"output": {
"width": 1920,
"height": 1080,
"format": "mp4",
"name": "modified_video.mp4",
"title": "modified_video"
},
"elements": [...],
"sceneMarkers": [...],
"subtitles": [...],
"projectId": "1767054556293"
}' | python -m json.tool
// Assume renderParams was retrieved from preview job response
const renderParams = previewJobResponse.data.renderParams;
// Modify as needed - change output title
renderParams.output.title = 'modified_video';
renderParams.output.name = 'modified_video.mp4';
// Send renderParams directly as the request body
const response = await fetch('https://api.pictory.ai/pictoryapis/v2/video/render', {
method: 'POST',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(renderParams)
});
const data = await response.json();
console.log('Render Job ID:', data.data.jobId);
import requests
# Assume render_params was retrieved from preview job response
render_params = preview_job_response['data']['renderParams']
# Modify as needed - change output title
render_params['output']['title'] = 'modified_video'
render_params['output']['name'] = 'modified_video.mp4'
# Send render_params directly as the request body
response = requests.post(
'https://api.pictory.ai/pictoryapis/v2/video/render',
headers={
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
json=render_params
)
data = response.json()
print('Render Job ID:', data['data']['jobId'])
<?php
// Assume $renderParams was retrieved from preview job response
$renderParams = $previewJobResponse['data']['renderParams'];
// Modify as needed - change output title
$renderParams['output']['title'] = 'modified_video';
$renderParams['output']['name'] = 'modified_video.mp4';
// Send renderParams directly as the request body
$url = 'https://api.pictory.ai/pictoryapis/v2/video/render';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: YOUR_API_KEY',
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($renderParams));
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
echo 'Render Job ID: ' . $data['data']['jobId'] . PHP_EOL;
?>
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
// Assume renderParams was retrieved from preview job response
var renderParams map[string]interface{}
// renderParams = previewJobResponse["data"].(map[string]interface{})["renderParams"]
// Modify as needed - change output title
output := renderParams["output"].(map[string]interface{})
output["title"] = "modified_video"
output["name"] = "modified_video.mp4"
// Send renderParams directly as the request body
url := "https://api.pictory.ai/pictoryapis/v2/video/render"
requestBody, _ := json.Marshal(renderParams)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
data := result["data"].(map[string]interface{})
fmt.Println("Render Job ID:", data["jobId"])
}
Complete Workflow: Preview, Modify, Render
const API_KEY = 'YOUR_API_KEY';
const API_BASE = 'https://api.pictory.ai/pictoryapis';
// Step 1: Create storyboard preview
async function createPreview() {
const response = await fetch(`${API_BASE}/v2/video/storyboard`, {
method: 'POST',
headers: {
'Authorization': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
videoName: 'my_video',
voiceOver: {
enabled: true,
aiVoices: [{ speaker: 'Brian', speed: 100 }]
},
scenes: [{
story: 'Welcome to our product demo. We will show you amazing features.',
createSceneOnEndOfSentence: true
}]
})
});
const data = await response.json();
return data.data.jobId;
}
// Step 2: Wait for job and get renderParams
async function getJobResult(jobId) {
while (true) {
const response = await fetch(`${API_BASE}/v1/jobs/${jobId}`, {
headers: { 'Authorization': API_KEY }
});
const data = await response.json();
if (data.data.status === 'completed') {
return data.data;
}
if (data.data.status === 'failed') {
throw new Error('Job failed');
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
// Step 3: Modify renderParams and render
async function renderWithModifications(renderParams) {
// Example modifications:
// Change output title
renderParams.output.title = 'modified_video';
renderParams.output.name = 'modified_video.mp4';
// Modify a background element's URL (find backgroundElement by id)
// const bgElement = renderParams.elements.find(el => el.id === 'backgroundElement_1');
// if (bgElement) bgElement.url = 'https://example.com/new-bg.mp4';
// Submit renderParams directly as the request body
const response = await fetch(`${API_BASE}/v2/video/render`, {
method: 'POST',
headers: {
'Authorization': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(renderParams)
});
const data = await response.json();
return data.data.jobId;
}
// Execute workflow
async function main() {
try {
// Create preview
console.log('Creating preview...');
const previewJobId = await createPreview();
console.log('Preview Job ID:', previewJobId);
// Wait for preview and get renderParams
console.log('Waiting for preview...');
const previewResult = await getJobResult(previewJobId);
console.log('Preview completed!');
const renderParams = previewResult.renderParams;
// Modify and render
console.log('Rendering with modifications...');
const renderJobId = await renderWithModifications(renderParams);
console.log('Render Job ID:', renderJobId);
// Wait for render
const renderResult = await getJobResult(renderJobId);
console.log('Video URL:', renderResult.videoURL);
} catch (error) {
console.error('Error:', error.message);
}
}
main();
import requests
import time
API_KEY = 'YOUR_API_KEY'
API_BASE = 'https://api.pictory.ai/pictoryapis'
def create_preview():
"""Step 1: Create storyboard preview"""
response = requests.post(
f'{API_BASE}/v2/video/storyboard',
headers={
'Authorization': API_KEY,
'Content-Type': 'application/json'
},
json={
'videoName': 'my_video',
'voiceOver': {
'enabled': True,
'aiVoices': [{'speaker': 'Brian', 'speed': 100}]
},
'scenes': [{
'story': 'Welcome to our product demo. We will show you amazing features.',
'createSceneOnEndOfSentence': True
}]
}
)
return response.json()['data']['jobId']
def get_job_result(job_id):
"""Step 2: Wait for job and get result"""
while True:
response = requests.get(
f'{API_BASE}/v1/jobs/{job_id}',
headers={'Authorization': API_KEY}
)
data = response.json()
if data['data']['status'] == 'completed':
return data['data']
if data['data']['status'] == 'failed':
raise Exception('Job failed')
time.sleep(5)
def render_with_modifications(render_params):
"""Step 3: Modify renderParams and render"""
# Example modifications:
# Change output title
render_params['output']['title'] = 'modified_video'
render_params['output']['name'] = 'modified_video.mp4'
# Modify a background element's URL (find backgroundElement by id)
# for el in render_params['elements']:
# if el.get('id') == 'backgroundElement_1':
# el['url'] = 'https://example.com/new-bg.mp4'
# Submit render_params directly as the request body
response = requests.post(
f'{API_BASE}/v2/video/render',
headers={
'Authorization': API_KEY,
'Content-Type': 'application/json'
},
json=render_params
)
return response.json()['data']['jobId']
def main():
try:
# Create preview
print('Creating preview...')
preview_job_id = create_preview()
print(f'Preview Job ID: {preview_job_id}')
# Wait for preview and get renderParams
print('Waiting for preview...')
preview_result = get_job_result(preview_job_id)
print('Preview completed!')
render_params = preview_result['renderParams']
# Modify and render
print('Rendering with modifications...')
render_job_id = render_with_modifications(render_params)
print(f'Render Job ID: {render_job_id}')
# Wait for render
render_result = get_job_result(render_job_id)
print(f'Video URL: {render_result["videoURL"]}')
except Exception as e:
print(f'Error: {e}')
if __name__ == '__main__':
main()
Example: Replace Scene Background
// Get renderParams from preview job
const renderParams = previewJobResponse.data.renderParams;
// Find and replace background for first scene (backgroundElement_1)
const bgElement = renderParams.elements.find(el => el.id === 'backgroundElement_1');
if (bgElement) {
bgElement.url = 'https://your-domain.com/custom-background.mp4';
bgElement.visualUrl = 'https://your-domain.com/custom-background.mp4';
}
// Render with new background - send renderParams directly
const response = await fetch('https://api.pictory.ai/pictoryapis/v2/video/render', {
method: 'POST',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(renderParams)
});
# Get render_params from preview job
render_params = preview_job_response['data']['renderParams']
# Find and replace background for first scene (backgroundElement_1)
for element in render_params['elements']:
if element.get('id') == 'backgroundElement_1':
element['url'] = 'https://your-domain.com/custom-background.mp4'
element['visualUrl'] = 'https://your-domain.com/custom-background.mp4'
break
# Render with new background - send render_params directly
response = requests.post(
'https://api.pictory.ai/pictoryapis/v2/video/render',
headers={
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
json=render_params
)
<?php
// Get renderParams from preview job
$renderParams = $previewJobResponse['data']['renderParams'];
// Find and replace background for first scene (backgroundElement_1)
foreach ($renderParams['elements'] as &$element) {
if ($element['id'] === 'backgroundElement_1') {
$element['url'] = 'https://your-domain.com/custom-background.mp4';
$element['visualUrl'] = 'https://your-domain.com/custom-background.mp4';
break;
}
}
// Render with new background - send renderParams directly
$url = 'https://api.pictory.ai/pictoryapis/v2/video/render';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: YOUR_API_KEY',
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($renderParams));
$response = curl_exec($ch);
curl_close($ch);
?>
package main
import (
"bytes"
"encoding/json"
"net/http"
)
func main() {
// Get renderParams from preview job
var renderParams map[string]interface{}
// renderParams = previewJobResponse["data"].(map[string]interface{})["renderParams"]
// Find and replace background for first scene (backgroundElement_1)
elements := renderParams["elements"].([]interface{})
for _, el := range elements {
element := el.(map[string]interface{})
if element["id"] == "backgroundElement_1" {
element["url"] = "https://your-domain.com/custom-background.mp4"
element["visualUrl"] = "https://your-domain.com/custom-background.mp4"
break
}
}
// Render with new background - send renderParams directly
url := "https://api.pictory.ai/pictoryapis/v2/video/render"
requestBody, _ := json.Marshal(renderParams)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
req.Header.Set("Authorization", "YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
client.Do(req)
}
Error Handling
400 - Missing Required Fields
400 - Missing Required Fields
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "elements is required"
}
}
renderParams including output, elements, sceneMarkers, and projectId. Send the renderParams content directly as the request body.400 - Invalid Request Structure
400 - Invalid Request Structure
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "Invalid request structure"
}
}
renderParams. Only modify specific properties, do not remove required fields.400 - Invalid Element Configuration
400 - Invalid Element Configuration
{
"success": false,
"error": {
"message": "Invalid element configuration at index 0"
}
}
id, elementType, and type properties.401 - Unauthorized
401 - Unauthorized
{
"message": "Unauthorized"
}
Related APIs
Create Storyboard Preview
Generate preview to get renderParams (Step 1)
Render from Preview
Render preview as-is without modifications
Get Render Job Status
Monitor rendering progress
Search Media
Find replacement visuals for scenes
Was this page helpful?
⌘I
