Skip to main content
This guide walks you through building an n8n workflow that integrates with the Pictory API to automate video creation. You’ll learn how to render storyboard videos, poll for job completion with retry logic, and optionally read input from and write results back to a Google Sheets spreadsheet.

Workflow Overview

The n8n workflow automates the full Pictory video rendering pipeline:
  1. Trigger the workflow (manually or from a spreadsheet)
  2. Call the Pictory Render Storyboard Video API
  3. Extract the jobId and initialize polling variables
  4. Poll the job status every 30 seconds
  5. Branch based on whether the job completed, failed, or is still in progress
  6. Output the video URL on success, or handle failure/timeout
n8n workflow for Pictory API integration

Before You Begin

Make sure you have:
  • A Pictory API key (get one here)
  • An n8n instance (self-hosted or cloud)
  • Basic familiarity with n8n workflow editor

Import the Workflow

You can import the ready-made workflow JSON directly into n8n:
  1. Open your n8n instance
  2. Click Add workflow (or press Ctrl+N)
  3. Click the three-dot menu (top-right) and select Import from file…
  4. Upload the n8n-workflow.json file
  5. Replace YOUR_PICTORY_API_KEY with your actual Pictory API key in the Pictory Render Storyboard Video and Get Job nodes
Never commit or share workflow files containing your actual API key. Always use placeholder values like YOUR_PICTORY_API_KEY in shared workflows and set the real key only in your private n8n instance.

Node-by-Node Explanation

1. When clicking ‘Execute workflow’ (Manual Trigger)

PropertyValue
Typen8n-nodes-base.manualTrigger
PurposeStarts the workflow on demand
This is the entry point of the workflow. When you click Execute workflow in the n8n editor, this node triggers and passes execution to the next node. In production, you can replace this with a Schedule Trigger, Webhook Trigger, or a Google Sheets Trigger to automate execution.

2. Pictory Render Storyboard Video (HTTP Request)

PropertyValue
Typen8n-nodes-base.httpRequest
MethodPOST
URLhttps://api.pictory.ai/pictoryapis/v2/video/storyboard/render
Always Output Datatrue
PurposeSends the storyboard payload to Pictory and initiates video rendering
This node makes a POST request to the Pictory Render Storyboard Video API. It sends:
  • Headers: Content-Type: application/json and Authorization: YOUR_PICTORY_API_KEY
  • Body: A JSON payload containing:
    • videoName — the name for the generated video
    • smartLayoutName — the visual layout theme (e.g., "Wanderlust")
    • voiceOver — AI voice configuration with speaker name
    • backgroundMusic — auto-selected background music at 10% volume
    • scenes — the text story content, with createSceneOnNewLine and createSceneOnEndOfSentence both enabled to automatically split the story into multiple scenes
Response: Returns a jobId in data.jobId that you use to track the rendering progress.
{
  "data": {
    "jobId": "abc123-def456-..."
  }
}

3. Set JobId (Set Node)

PropertyValue
Typen8n-nodes-base.set
Always Output Datatrue
PurposeExtracts the job ID and initializes polling counters
This node extracts three variables from the API response and stores them for use throughout the workflow:
VariableValueDescription
jobId{{ $json.data.jobId }}The rendering job identifier from Pictory
retryCount0Current poll attempt counter (starts at 0)
maxRetries30Maximum number of polling attempts before timeout
With a 30-second wait between polls and 30 max retries, the workflow will wait up to 15 minutes for the video to render before timing out.

4. Wait for Job to Complete (Wait Node)

PropertyValue
Typen8n-nodes-base.wait
Wait Time30 seconds
Always Output Datatrue
PurposePauses execution before polling the job status
This node introduces a 30-second delay before checking the job status. Video rendering takes time, so polling immediately after submission would return an in-progress status. This wait ensures the API has time to process.
The Wait node pauses the entire workflow execution. In n8n, this means the execution is suspended and resumed after the wait period, so it does not consume resources while waiting.

5. Get Job (HTTP Request)

PropertyValue
Typen8n-nodes-base.httpRequest
MethodGET
URLhttps://api.pictory.ai/pictoryapis/v1/jobs/{{ $('Set JobId').item.json.jobId }}
Always Output Datatrue
PurposePolls the Pictory Jobs API to check rendering status
This node calls the Get Job API using the jobId extracted earlier. The URL dynamically references the jobId from the Set JobId node using n8n’s expression syntax {{ $('Set JobId').item.json.jobId }}. Response: Returns the job status along with video URLs when completed:
{
  "data": {
    "status": "completed",
    "videoURL": "https://...",
    "videoShareURL": "https://..."
  }
}
Possible status values: in-progress, completed, failed.

6. If Job is Completed or Failed (IF Node)

PropertyValue
Typen8n-nodes-base.if
Conditiondata.status == "completed" OR data.status == "failed"
Always Output Datatrue
PurposeRoutes execution based on whether the job has reached a terminal state
This is the primary branching node:
  • True branch (status is completed or failed): Proceeds to the If Job Completed node for further evaluation
  • False branch (status is still in-progress): Proceeds to the Increment Retry node to poll again

7. If Job Completed (IF Node)

PropertyValue
Typen8n-nodes-base.if
Conditiondata.status == "completed"
Always Output Datatrue
PurposeDistinguishes between a completed job and a failed job
This second-level branch runs only when the job has reached a terminal state:
  • True branch (completed): Routes to Set Job Output to extract video URLs
  • False branch (failed): Routes to Set Failed Job Status to record the failure

8. Set Job Output (Set Node)

PropertyValue
Typen8n-nodes-base.set
Always Output Datatrue
PurposeExtracts video URLs from a successfully completed job
When the job completes successfully, this node extracts:
VariableValueDescription
videoUrl{{ $json.data.videoURL }}Direct download URL for the rendered video
videoShareUrl{{ $json.data.videoShareURL }}Shareable video URL
jobId{{ $('Set JobId').item.json.jobId }}The original job ID for reference
This is the success endpoint of the workflow. You can connect additional nodes here to send notifications, upload the video, or write results back to a spreadsheet.

9. Set Failed Job Status (Set Node)

PropertyValue
Typen8n-nodes-base.set
PurposeRecords a job failure with a reason
When the job status is failed, this node sets:
VariableValue
status"failed"
reasonForFailure"job execution failed"
This is a failure endpoint. You can connect notification nodes here (e.g., Slack, email) to alert you when a video render fails.

10. Increment Retry (Set Node)

PropertyValue
Typen8n-nodes-base.set
Always Output Datatrue
PurposeIncrements the retry counter for the polling loop
When the job is still in progress, this node:
  • Increments retryCount by 1: {{ $('Set JobId').item.json.retryCount + 1 }}
  • Preserves jobId and maxRetries from the original Set JobId node
This creates the polling loop by updating the counter before checking whether to continue polling.

11. Can Poll Job (IF Node)

PropertyValue
Typen8n-nodes-base.if
ConditionretryCount == maxRetries
Always Output Datatrue
PurposeGuards against infinite polling by enforcing a maximum retry limit
This node checks if the retry limit has been reached:
  • True branch (retryCount equals maxRetries): The job has timed out. Routes to Set Timeout Job Status
  • False branch (retryCount is less than maxRetries): Loops back to Wait for Job to Complete to poll again

12. Set Timeout Job Status (Set Node)

PropertyValue
Typen8n-nodes-base.set
Always Output Datatrue
PurposeRecords a timeout when polling exhausts all retries
When max retries are reached without the job completing, this node sets:
VariableValue
renderStatus"failed"
reasonForFailure"job timed out"
jobIdThe original job ID
This is the timeout endpoint. Consider increasing maxRetries in the Set JobId node if your videos consistently need more time to render.

Understanding alwaysOutputData

Most nodes in this workflow have alwaysOutputData set to true. This is a critical n8n node setting that controls what happens when a node produces no output items. What it does: When alwaysOutputData is enabled, the node will always output at least one empty item ([{}]), even if the node itself produces no data. Without this setting, a node that returns no items would cause downstream nodes to be skipped entirely. Why it matters in this workflow:
  • Polling loop continuity: The polling loop (Wait -> Get Job -> IF -> Increment Retry -> Can Poll Job -> back to Wait) must never break due to an empty output. If the Get Job node returned no data (e.g., due to a network issue), alwaysOutputData ensures the workflow still routes through the IF nodes and continues polling rather than silently stopping.
  • IF node branching: IF nodes with alwaysOutputData ensure that both the true and false branches always emit an item. This prevents the workflow from stalling when a condition does not match any input items.
  • Error visibility: Without alwaysOutputData, a node that fails silently (no output) would make the workflow appear to hang. With it enabled, the empty item propagates through the chain, making it easier to debug where things went wrong.
As a best practice, enable alwaysOutputData on all nodes in polling/retry workflows. This ensures the loop never breaks silently and all branches always execute, giving you predictable behavior even when API responses are unexpected.

Workflow Flow Diagram

Google Sheets Integration

A common use case is reading video content from a Google Sheets spreadsheet, rendering videos for each row, and writing the results (job ID and video URL) back to the spreadsheet.

Spreadsheet Structure

Set up your Google Sheet with the following columns:
Column AColumn BColumn CColumn DColumn E
Story TextVideo NameJob IDStatusVideo URL
Your story content here…my_video_1(filled by workflow)(filled by workflow)(filled by workflow)

Modified Workflow for Spreadsheet Integration

To integrate with Google Sheets, modify the workflow with these additional nodes:

Step 1: Read from Google Sheets (Replace Manual Trigger)

Replace the Manual Trigger node with a Google Sheets node:
  • Operation: Read Rows
  • Document: Select your spreadsheet
  • Sheet: Select the sheet name
  • Options: Filter rows where Job ID column is empty (to only process new rows)
Each row becomes an item that flows through the workflow. For example, if you have 20 rows, the workflow processes 20 videos.

Step 2: Use Spreadsheet Data in the Render Request

In the Pictory Render Storyboard Video node, replace the hardcoded body with dynamic expressions referencing the spreadsheet columns:
{
  "videoName": "={{ $json['Video Name'] }}",
  "smartLayoutName": "Wanderlust",
  "voiceOver": {
    "enabled": true,
    "aiVoices": [{ "speaker": "Brian" }]
  },
  "backgroundMusic": {
    "enabled": true,
    "volume": 0.1,
    "autoMusic": true
  },
  "scenes": [
    {
      "story": "={{ $json['Story Text'] }}",
      "createSceneOnNewLine": true,
      "createSceneOnEndOfSentence": true
    }
  ]
}

Step 3: Write Job ID to Spreadsheet

After the Set JobId node, add a Google Sheets node to write the job ID back:
  • Operation: Update Row
  • Document: Select your spreadsheet
  • Sheet: Select the sheet name
  • Mapping Column: Use the row number or a unique identifier
  • Values to Update:
    • Job ID column: {{ $json.jobId }}
    • Status column: in-progress
This immediately records the job ID so you can track which rows have been submitted for rendering.

Step 4: Write Video URL to Spreadsheet on Completion

After the Set Job Output node (success endpoint), add another Google Sheets node:
  • Operation: Update Row
  • Document: Select your spreadsheet
  • Sheet: Select the sheet name
  • Matching Column: Match on the Job ID column with value {{ $json.jobId }}
  • Values to Update:
    • Status column: completed
    • Video URL column: {{ $json.videoUrl }}
Use the Job ID column as the matching key to ensure the video URL is written to the correct row. The Google Sheets node’s Update Row operation can match on any column value. It finds the row where the Job ID matches and updates the corresponding Video URL column.

Step 5: Write Failure Status to Spreadsheet

Similarly, after the Set Failed Job Status and Set Timeout Job Status nodes, add Google Sheets nodes to update the status:
  • Matching Column: Match on the Job ID column
  • Values to Update:
    • Status column: failed or timed out
    • Video URL column: (leave empty)

Complete Spreadsheet Workflow Diagram

Best Practices

Use n8n’s Credentials feature or Environment Variables to store your Pictory API key instead of hardcoding it in HTTP Request nodes. This prevents accidental exposure when sharing or exporting workflows.
The default configuration polls every 30 seconds with a maximum of 30 retries (15 minutes total). For longer videos, increase maxRetries in the Set JobId node. The recommended polling interval is 10–30 seconds.
If processing many videos in batch (e.g., from a large spreadsheet), add a short delay between render requests to avoid hitting Pictory API rate limits. You can use a Wait node with a 2-5 second delay before the Render node.
Connect the failure endpoints (Set Failed Job Status and Set Timeout Job Status) to notification nodes such as Slack, email, or Discord to get alerted when a render fails or times out.
Filter spreadsheet rows by checking if the Job ID column is empty before processing. This ensures rows that have already been submitted are not processed again if the workflow is re-run.

Troubleshooting

Verify your API key is correct and includes the full key string. The Authorization header value should be your complete API key (e.g., pictai_xxxx...). Do not add Bearer prefix.
Increase maxRetries in the Set JobId node. Longer stories or higher-quality renders may take more than 15 minutes. Also verify that the initial render request returned a valid jobId.
Ensure the Matching Column is set correctly in the Google Sheets Update Row operation. The Job ID value must exactly match what was written earlier. Check that your Google Sheets credentials have write permissions.
In n8n, the Wait node suspends workflow execution. If you are using n8n in queue mode, ensure your workers are running. For the default main mode, the workflow resumes automatically after the wait period.

Next Steps