> ## 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.

# Render Project

> Initiate the rendering process for a project to generate the final video output based on the current project configuration and content.

## Overview

Initiate the rendering process for an existing Pictory project to generate the final video output. This endpoint creates a render job that processes your project's scenes, audio, subtitles, and visual effects into a complete video file. The rendering happens asynchronously, and you can track progress using the job ID returned in the response.

<Note>
  This endpoint renders existing Pictory projects. Projects can be created via the [Create Storyboard Preview API](/api-reference/videos/create-storyboard-preview) with `saveProject: true`, or directly in the [Pictory App](https://app.pictory.ai).
</Note>

<Tip>
  **Automation Workflow:** Create or edit projects in the Pictory App, then use this API to trigger rendering programmatically as part of your automation pipeline.
</Tip>

***

## Render Workflow Options

| Workflow                      | API                                                                          | When to Use                                        |
| ----------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------- |
| **Create Preview**            | [Create Storyboard Preview](/api-reference/videos/create-storyboard-preview) | Generate preview to review scenes before rendering |
| **Render from Preview**       | [Render from Preview](/api-reference/videos/render-from-preview)             | Render preview as-is without modifications         |
| **Render with Modifications** | [Render Video](/api-reference/videos/render-video)                           | Modify preview elements before rendering           |
| **Render Saved Project**      | **This API**                                                                 | Render existing project created in App or via API  |
| **Direct Render**             | [Render Storyboard Video](/api-reference/videos/render-storyboard-video)     | Skip preview, render directly from input           |

### When to Use This Endpoint

<CardGroup cols={2}>
  <Card title="App-Created Projects" icon="desktop">
    Render projects created and edited in the Pictory web application
  </Card>

  <Card title="Saved API Projects" icon="floppy-disk">
    Render projects saved via Storyboard API with saveProject: true
  </Card>

  <Card title="Automation Pipelines" icon="gears">
    Integrate project rendering into automated workflows
  </Card>

  <Card title="Re-rendering" icon="arrows-rotate">
    Re-render projects after manual edits in the App
  </Card>
</CardGroup>

***

## API Endpoint

```http theme={null}
POST https://api.pictory.ai/pictoryapis/v2/projects/{projectid}/render
```

***

## Request Parameters

### Path Parameters

<ParamField path="projectid" type="string" required>
  The unique identifier of the project to render. Get from [Get Projects](/api-reference/projects/get-projects) API.
</ParamField>

### Headers

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

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

***

## Response

When the render request is successfully submitted, a job is created and a job ID is returned.

<ResponseField name="success" type="boolean">
  Indicates whether the request was successful
</ResponseField>

<ResponseField name="data" type="object">
  <ResponseField name="jobId" type="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](/api-reference/jobs/get-video-render-job-by-id) endpoint.
  </ResponseField>
</ResponseField>

<ResponseExample>
  ```json 200 - Job Created theme={null}
  {
      "success": true,
      "data": {
          "jobId": "265a7c1a-4985-4058-9208-68114f131a2b"
      }
  }
  ```

  ```json 401 - Unauthorized theme={null}
  {
      "message": "Unauthorized"
  }
  ```

  ```json 404 - Not Found theme={null}
  {
      "success": false,
      "message": "Project not found"
  }
  ```

  ```json 409 - Conflict theme={null}
  {
      "success": false,
      "message": "Project is already being rendered"
  }
  ```
</ResponseExample>

## Next Steps

Once you have the `jobId`, poll the [Get Video Render Job by ID](/api-reference/jobs/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

<Tip>
  Replace `YOUR_API_KEY` with your actual API key that starts with `pictai_`
</Tip>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://api.pictory.ai/pictoryapis/v2/projects/YOUR_PROJECT_ID/render' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'accept: application/json' | python -m json.tool
  ```

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

  project_id = "20251222191648030d7df02f5b4054d4ca8831f1369459e25"
  url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}/render"
  headers = {
      "Authorization": "YOUR_API_KEY",
      "accept": "application/json"
  }

  response = requests.post(url, headers=headers)
  result = response.json()

  if result.get('success'):
      job_id = result['data']['jobId']
      print(f"Render job started: {job_id}")
  else:
      print(f"Failed to start render: {result.get('message')}")
  ```

  ```javascript JavaScript theme={null}
  const projectId = '20251222191648030d7df02f5b4054d4ca8831f1369459e25';
  const response = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}/render`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'YOUR_API_KEY',
        'accept': 'application/json'
      }
    }
  );

  const result = await response.json();

  if (result.success) {
    const jobId = result.data.jobId;
    console.log(`Render job started: ${jobId}`);
  } else {
    console.log(`Failed to start render: ${result.message}`);
  }
  ```

  ```php PHP theme={null}
  <?php

  $projectId = '20251222191648030d7df02f5b4054d4ca8831f1369459e25';
  $url = "https://api.pictory.ai/pictoryapis/v2/projects/{$projectId}/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',
      'accept: application/json'
  ]);

  $response = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($httpCode === 200) {
      $result = json_decode($response, true);
      if ($result['success']) {
          $jobId = $result['data']['jobId'];
          echo "Render job started: {$jobId}" . PHP_EOL;
      } else {
          echo "Failed to start render: {$result['message']}" . PHP_EOL;
      }
  } else {
      echo "Request failed with status {$httpCode}" . PHP_EOL;
  }
  ?>
  ```

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

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

  type RenderResponse struct {
      Success bool `json:"success"`
      Data    struct {
          JobID string `json:"jobId"`
      } `json:"data"`
      Message string `json:"message"`
  }

  func main() {
      projectID := "20251222191648030d7df02f5b4054d4ca8831f1369459e25"
      url := fmt.Sprintf("https://api.pictory.ai/pictoryapis/v2/projects/%s/render", projectID)

      req, err := http.NewRequest("POST", url, nil)
      if err != nil {
          panic(err)
      }

      req.Header.Set("Authorization", "YOUR_API_KEY")
      req.Header.Set("accept", "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 RenderResponse
      json.Unmarshal(body, &result)

      if result.Success {
          fmt.Printf("Render job started: %s\n", result.Data.JobID)
      } else {
          fmt.Printf("Failed to start render: %s\n", result.Message)
      }
  }
  ```
</CodeGroup>

***

## Usage Notes

<Note>
  **Asynchronous Processing**: Rendering happens asynchronously. The API returns immediately with a job ID. Use the [Get Video Render Job by ID](/api-reference/jobs/get-video-render-job-by-id) API to poll and track rendering progress.
</Note>

<Warning>
  **Rendering Time**: Rendering time varies based on project complexity, video length, number of scenes, and applied effects. Simple projects may render in minutes, while complex projects can take longer.
</Warning>

<Tip>
  **Job Status Polling**: Poll the [Get Video Render Job by ID](/api-reference/jobs/get-video-render-job-by-id) API periodically (recommended: every 10–30 seconds) to determine when the video is ready. The completed job response includes the `videoURL`.
</Tip>

***

## Rendering Behavior

### What Happens During Rendering

1. **Job Creation**: A render job is created and queued in the rendering system
2. **Processing**: The system processes all project elements:
   * Compiles all scene visuals (images/videos)
   * Generates and synchronizes voice-over audio
   * Applies text overlays and subtitle formatting
   * Processes transitions and effects
   * Combines all elements into the final video
3. **Encoding**: The final video is encoded in the specified format and quality
4. **Completion**: The job status changes to complete, and video URLs become available

***

## Code Examples: Poll for Completion

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function waitForVideo(jobId, apiKey) {
    const maxAttempts = 60;
    const pollInterval = 10000; // 10 seconds

    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      const response = await fetch(
        `https://api.pictory.ai/pictoryapis/v1/jobs/${jobId}`,
        { headers: { 'Authorization': apiKey } }
      );

      const data = await response.json();
      const status = data.data.status;

      console.log(`Attempt ${attempt + 1}: Status = ${status}`);

      if (status === 'completed') {
        console.log('Video URL:', data.data.videoURL);
        return data.data;
      }

      if (status === 'failed') {
        throw new Error('Video rendering failed: ' + JSON.stringify(data));
      }

      await new Promise(resolve => setTimeout(resolve, pollInterval));
    }

    throw new Error('Timeout waiting for video to render');
  }

  // Usage
  const projectId = 'your-project-id';
  const response = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}/render`,
    {
      method: 'POST',
      headers: { 'Authorization': 'YOUR_API_KEY' }
    }
  );
  const result = await response.json();
  const videoResult = await waitForVideo(result.data.jobId, 'YOUR_API_KEY');
  ```

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

  def wait_for_video(job_id, api_key, max_attempts=60, poll_interval=10):
      for attempt in range(max_attempts):
          response = requests.get(
              f'https://api.pictory.ai/pictoryapis/v1/jobs/{job_id}',
              headers={'Authorization': api_key}
          )

          data = response.json()
          status = data['data']['status']

          print(f'Attempt {attempt + 1}: Status = {status}')

          if status == 'completed':
              print('Video URL:', data['data']['videoURL'])
              return data['data']

          if status == 'failed':
              raise Exception(f'Video rendering failed: {data}')

          time.sleep(poll_interval)

      raise Exception('Timeout waiting for video to render')

  # Usage
  project_id = 'your-project-id'
  render_response = requests.post(
      f'https://api.pictory.ai/pictoryapis/v2/projects/{project_id}/render',
      headers={'Authorization': 'YOUR_API_KEY'}
  )
  result = render_response.json()
  video_result = wait_for_video(result['data']['jobId'], 'YOUR_API_KEY')
  ```
</CodeGroup>

***

## Best Practices

### Polling Strategy

1. **Start Interval**: Begin with 10-second intervals for the first minute
2. **Increase Gradually**: Increase to 15-30 seconds for longer renders
3. **Maximum Attempts**: Set a reasonable timeout (e.g., 20-30 minutes)
4. **Exponential Backoff**: Use increasing intervals to reduce API calls

### Error Handling

* **Retry Logic**: Implement automatic retry for transient failures
* **Timeout Handling**: Set appropriate timeouts based on project complexity
* **Status Validation**: Always check the response status before proceeding
* **Fallback Strategy**: Have a plan for handling permanent failures

***

## Related APIs

<CardGroup cols={2}>
  <Card title="Get Render Job Status" icon="spinner" href="/api-reference/jobs/get-video-render-job-by-id">
    Monitor rendering progress
  </Card>

  <Card title="Get Projects" icon="folder" href="/api-reference/projects/get-projects">
    List projects to get project IDs
  </Card>

  <Card title="Get Project By Id" icon="folder-open" href="/api-reference/projects/get-project-by-id">
    Get project details and video URL
  </Card>

  <Card title="Create Storyboard Preview" icon="eye" href="/api-reference/videos/create-storyboard-preview">
    Create new video projects via API
  </Card>
</CardGroup>
