> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.synthesia.io/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.synthesia.io/docs/_mcp/server.

# Create a video

POST https://api.synthesia.io/v2/videos
Content-Type: application/json

Create a video within your Synthesia account. To get started on this endpoint check the [API Quickstart guide](https://docs.synthesia.io/reference/synthesia-api-quickstart).

Reference: https://developers.synthesia.io/docs/video-api-reference/videos/create-a-video

## Authentication

- `Authorization` header (required) — API Key authentication via header

## Request

### Body (application/json)

This endpoint expects a CreateVideoRequest.

- `input` (list of Input, required) — An array of objects that each describe a clip of a multi-clip video. You can think of the clips as different scenes in the video.
- `folderId` (string, optional, nullable) — Optional folder ID. If provided, the new video will be created under this folder and inherit its permissions.
- `aspectRatio` (enum, optional, default: 16:9) — Aspect ratio of the video. Default is `landscape (16:9)`.
  - Allowed values: `16:9`, `9:16`, `1:1`, `4:5`, `5:4`
- `ctaSettings` (CtaSettings, optional) — Settings for a call-to-action button.
- `callbackId` (string, optional) — Use callback ID to link videos back to the initial request. For example, if you are making a personalized video for a customer, you could enter the customer's email as a callback ID. This way, you can tell who the video is for, once its generated..
- `description` (string, optional, default: ) — Description of the video to be shown on the share page.
- `soundtrack` (enum, optional) — soundtrack option is supported for backward compatibility. You should use the templates functionality for rich videos.
  - Allowed values: `corporate`, `inspirational`, `modern`, `urban`
- `soundSettings` (ParentSoundSettings, optional)
- `test` (boolean, optional, default: false) — Test videos are free and not counted towards your quota. If you create a video in the “test” mode, we will overlay a watermark over your video.
- `title` (string, optional, default: ) — Title of the video to be shown on the share page.
- `visibility` (enum, optional, default: private) — Public videos will be visible to anyone with a share URL. Private videos can only be downloaded via a time-limited download link. See Retrieve a video for details. Visibility can be changed also once the video is created via Update a video.
  - Allowed values: `private`, `public`

## Response

### 200

OK.

- `callbackId` (string, optional) — Use callback ID to link videos back to the initial request. For example, if you are making a personalized video for a customer, you could enter the customer's email as a callback ID. This way, you can tell who the video is for, once its generated..
- `createdAt` (integer, optional) — UNIX timestamp representing the time video was created.
- `ctaSettings` (CtaSettings, optional) — Settings for a call-to-action button.
- `description` (string, optional, default: ) — Description of the video to be shown on the share page.
- `test` (boolean, optional, default: false) — Test videos are free and not counted towards your quota. If you create a video in the “test” mode, we will overlay a watermark over your video.
- `download` (string, optional) — Withheld for rejected videos and videos held for moderation review.
- `duration` (string, optional)
- `id` (string, optional)
- `lastUpdatedAt` (integer, optional)
- `status` (enum, optional) — Video status. A rendered video that is still awaiting moderation review is reported as `in_progress` until the review is final.
  - Allowed values: `complete`, `deleted`, `error`, `in_progress`, `rejected`, `approved`
- `thumbnail` (VideoThumbnailTypesResponse, optional, nullable)
- `title` (string, optional, default: ) — Title of the video to be shown on the share page.
- `visibility` (enum, optional, default: private) — Public videos will be visible to anyone with a share URL. Private videos can only be downloaded via a time-limited download link. See Retrieve a video for details. Visibility can be changed also once the video is created via Update a video.
  - Allowed values: `private`, `public`
- `captions` (VideoAssetCaptionTypesResponse, optional) — Downloadable captions files. Omitted when no captions are available, and for videos that are rejected or held for moderation review.
- `contentProvenance` (ContentProvenanceResponse, optional, nullable) — Content provenance (C2PA) information for the video. Only present when the video has been signed.

## Errors

### 400 Bad Request Error

Bad Request.

- `error` (string, required)
- `context` (string, optional)

### 403 Forbidden Error

Forbidden - Wrong API key.

- `error` (string, required)
- `context` (string, optional)

## Types

### Input

- `avatar` (string, required) — Use one of [Synthesia stock avatars or your custom avatar](https://docs.synthesia.io/reference/avatars).
- `background` (string, required) — Use Synthesia' stock backgrounds or your custom background. For the custom background, provide the ID of an uploaded asset (see [Create an asset](https://docs.synthesia.io/reference/create-an-asset)), or URL from where Synthesia should download the background. Transparent: green_screen Green screen background can be used if you want to replace background using FFMPEG or any of the video editing software. Solid: `off_white` / `warm_white` / `light_pink` / `soft_pink` / `light_blue` / `dark_blue` / `soft_cyan` / `strong_cyan` / `light_orange` / `soft_orange` Image: `white_studio` / `white_cafe` / `luxury_lobby` / `large_window` / `white_meeting_room` / `open_office`
- `avatarSettings` (ActorSettings, optional, default: {"horizontalAlign":"center","scale":1,"seamless":false,"style":"rectangular","voice":null}) — Avatar settings.
- `backgroundSettings` (InputBackgroundSettings, optional) — Video Settings object
- `scriptAudio` (string, optional) — As an alternative to `scriptText`, provide the ID of uploaded script audio. See [Upload script audio for details](https://docs.synthesia.io/reference/upload-script-audio). If you use `scriptAudio` you must provide `scriptLanguage`.
- `scriptLanguage` (enum, optional) — Language code of the language the script audio was generated in. The scriptLanguage option must be used with `scriptAudio`. To control the language/voice of the scriptText, use `input[].avatarSettings.voice` option. Language code should be in the format: `en-US`, where the first two characters represent language and the second two-character represent the country.
  - Allowed values: `MISSING`, `af`, `af-001`, `sq-AL`, `sq-001`, `am`, `am-001`, `ar-DZ`, `ar-BH`, `ar-EG`, `ar-IQ`, `ar-JO`, `ar-KW`, `ar-LY`, `ar-MA`, `ar-QA`, `ar-SA`, `ar-SY`, `ar-TN`, `ar-AE`, `ar-YE`, `ar-001`, `as-IN`, `as-001`, `hy-AM`, `hy-001`, `az-AZ`, `az-001`, `eu-ES`, `eu-001`, `bs-BA`, `bs-001`, `bg`, `bg-001`, `bn-BD`, `bn-IN`, `bn-001`, `my`, `my-001`, `yue-CN`, `ca`, `ca-001`, `zh-CN`, `zh-HK`, `zh-TW`, `zh-001`, `hr`, `hr-001`, `cs`, `cs-001`, `da`, `da-001`, `nl-NL`, `nl-001`, `nl-BE`, `ka-GE`, `ka-001`, `en-AU`, `en-CA`, `en-GB`, `en-HK`, `en-IE`, `en-IN`, `en-KE`, `en-MX`, `en-NG`, `en-NZ`, `en-PH`, `en-US`, `en-SG`, `en-ZA`, `en-TZ`, `en-001`, `et`, `et-001`, `fil`, `fil-001`, `fi`, `fi-001`, `fr-BE`, `fr-CA`, `fr-CH`, `fr-FR`, `fr-001`, `gl`, `gl-001`, `de-AT`, `de-DE`, `de-CH`, `de-001`, `el`, `el-001`, `gu`, `gu-001`, `iw`, `iw-001`, `hi`, `hi-001`, `ht-HT`, `hu`, `hu-001`, `is`, `is-001`, `id`, `id-001`, `ga`, `ga-001`, `it`, `it-001`, `ja`, `ja-001`, `jv`, `jv-001`, `kn`, `kn-001`, `kk-KZ`, `kk-001`, `km`, `km-001`, `ko`, `ko-001`, `lt`, `lt-001`, `lv`, `lv-001`, `lo-LA`, `lo-001`, `mi-NZ`, `mk-MK`, `mk-001`, `ms`, `ms-001`, `ml`, `ml-001`, `mt`, `mt-001`, `mr`, `mr-001`, `mn-MN`, `mn-001`, `ne-NP`, `ne-001`, `nb`, `nb-001`, `or-IN`, `or-001`, `ps-AF`, `ps-001`, `fa`, `fa-001`, `pl`, `pl-001`, `pt-BR`, `pt-PT`, `pt-001`, `pa-IN`, `pa-001`, `ro`, `ro-001`, `ru`, `ru-001`, `sr`, `sr-001`, `si-LK`, `si-001`, `sk`, `sk-001`, `sl`, `sl-001`, `so`, `so-001`, `es-AR`, `es-BO`, `es-CL`, `es-CO`, `es-CR`, `es-CU`, `es-DO`, `es-EC`, `es-SV`, `es-GQ`, `es-GT`, `es-HN`, `es-LA`, `es-MX`, `es-NI`, `es-PA`, `es-PY`, `es-PE`, `es-PR`, `es-ES`, `es-US`, `es-UY`, `es-VE`, `es-001`, `su`, `su-001`, `sv`, `sv-001`, `sw-KE`, `sw-TZ`, `sw-001`, `vi`, `vi-001`, `ta-IN`, `ta-SG`, `ta-LK`, `ta-001`, `te`, `te-001`, `tr`, `tr-001`, `th`, `th-001`, `uk`, `uk-001`, `ur-IN`, `ur-PK`, `ur-001`, `uz`, `uz-001`, `cy`, `cy-001`, `wuu-CN`, `zu`, `zu-001`
- `scriptText` (string, optional) — Script for text-to-voice can be entered in any of the [supported languages](https://docs.synthesia.io/reference/voices).
- `soundSettings` (InputSoundSettings, optional)
- `transition` (string, optional, nullable)

### CtaSettings

- `label` (string, required) — Label for a call-to-action button.
- `url` (string, required) — URL to navigate to, when the call-to-action button is clicked.

### ParentSoundSettings

- `defaultSoundtrackVolume` (double, optional, default: 0.18)
- `soundtracks` (list of ParentSoundtrackRegion, optional, default: [])

### VideoThumbnailTypesResponse

- `image` (string, optional, nullable)
- `gif` (string, optional, nullable)

### VideoAssetCaptionTypesResponse

- `srt` (string, optional, nullable)
- `vtt` (string, optional, nullable)

### ContentProvenanceResponse

- `c2pa` (C2PAContentProvenanceResponse, optional, nullable)

### ActorSettings

- `style` (enum, required) — `rectangular` style corresponds to the "Full body" avatar style in STUDIO. `circular style corresponds to the "Circle" avatar style in STUDIO. The position of circular avatar is fixed to the center of the video both vertically and horizontally, and can't be changed. With scale 1.0, circular avatar will cover total height of the video. Use circular style, if you need to make a round cutout of the avatar to integrate into e.g. screen recording app
  - Allowed values: `circular`, `rectangular`, `voiceOnly`
- `backgroundColor` (any, optional) — HEX color code (e.g. #F2F7FF) for the background of circular style avatar.
- `horizontalAlign` (enum, optional, nullable) — At the moment `verticalAlign` field is not exposed via API and is fixed to `bottom`, however together with `horizontalAlign` they define a reference point from where we scale the rectangular style avatar. For example, `horizontal alignment: left`, would mean that avatar is called from the bottom-left corner towards the top-right corner.
  - Allowed values: `left`, `center`, `right`
- `scale` (double, optional, nullable) — The scale of the avatar.
- `voice` (string, optional, nullable) — If a voice is not provided, a default voice is assigned automatically. This default is subject to change, which may cause inconsistent voices across videos. Specify voiceId explicitly to ensure consistent output. See the complete list of voices.
- `seamless` (boolean, optional, default: false) — When the seamless option is enabled, a video of the avatar will be generated in a way that first and last frames match, so videos can be concatenated seamlessly. Use seamless option, if you need to play videos back to back seamlessly e.g. in a video chatbot app. Limitations: - Only work with anna_costume1_cameraA and mia_costume1_cameraA actors. - Only work with static (image) backgrounds. - Videos will have noticeably lower quality. Though, this might not matter, depending on your use case.

### InputBackgroundSettings

- `position` (InputBackgroundPosition, optional, nullable)
- `scale` (double, optional, default: 1)
- `videoSettings` (InputVideoBackgroundSettings, optional, nullable)

### InputSoundSettings

- `soundtrackVolume` (double, optional, nullable)

### ParentSoundtrackRegion

- `soundtrack` (string, required)
- `slideRange` (string, required)
- `fadeInSeconds` (double, optional, default: 0)
- `fadeOutSeconds` (double, optional, default: 0)

### C2PAContentProvenanceResponse

- `signedAt` (string, required)
- `claimGenerator` (string, required)

### InputBackgroundPosition

- `x` (integer, required)
- `y` (integer, required)

### InputVideoBackgroundSettings

- `trim` (InputBackgroundTrim, optional, nullable)
- `shortBackgroundContentMatchMode` (enum, optional, default: freeze) — Possible values: - `freeze` freezes the last frame of the background until the content is finished - `loop` loops the background until the content is finished - `slow_down` slows down the background so that its duration matches the content duration
  - Allowed values: `freeze`, `loop`, `slow_down`
- `longBackgroundContentMatchMode` (enum, optional, default: trim) — Possible values - `trim` trims the background to the duration of the content - `speed_up` speeds up the background so that its duration matches the content duration
  - Allowed values: `extend_content`, `trim`, `speed_up`
- `volume` (double, optional, nullable)

### InputBackgroundTrim

- `startTime` (double, required)
- `endTime` (double, required)

## Examples

**Request**

```json
{
  "input": [
    {
      "avatar": "string",
      "background": "string"
    }
  ]
}
```

**Response**

```json
{
  "callbackId": "string",
  "createdAt": 1,
  "ctaSettings": {
    "label": [
      "Learn More",
      "Sign up"
    ],
    "url": [
      "https://example.com",
      "https://signup.example.com"
    ]
  },
  "description": "",
  "test": false,
  "download": "string",
  "duration": "string",
  "id": "string",
  "lastUpdatedAt": 1,
  "status": "complete",
  "thumbnail": {
    "image": "string",
    "gif": "string"
  },
  "title": "",
  "visibility": "private",
  "captions": {
    "srt": "string",
    "vtt": "string"
  },
  "contentProvenance": {
    "c2pa": {
      "signedAt": "string",
      "claimGenerator": "string"
    }
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.synthesia.io/v2/videos"

payload = { "input": [
        {
            "avatar": "string",
            "background": "string"
        }
    ] }
headers = {
    "Authorization": "<apiKey>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

```javascript
const url = 'https://api.synthesia.io/v2/videos';
const options = {
  method: 'POST',
  headers: {Authorization: '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"input":[{"avatar":"string","background":"string"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api.synthesia.io/v2/videos"

	payload := strings.NewReader("{\n  \"input\": [\n    {\n      \"avatar\": \"string\",\n      \"background\": \"string\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

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

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

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.synthesia.io/v2/videos")

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

request = Net::HTTP::Post.new(url)
request["Authorization"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"input\": [\n    {\n      \"avatar\": \"string\",\n      \"background\": \"string\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.synthesia.io/v2/videos")
  .header("Authorization", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"input\": [\n    {\n      \"avatar\": \"string\",\n      \"background\": \"string\"\n    }\n  ]\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.synthesia.io/v2/videos', [
  'body' => '{
  "input": [
    {
      "avatar": "string",
      "background": "string"
    }
  ]
}',
  'headers' => [
    'Authorization' => '<apiKey>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.synthesia.io/v2/videos");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"input\": [\n    {\n      \"avatar\": \"string\",\n      \"background\": \"string\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["input": [
    [
      "avatar": "string",
      "background": "string"
    ]
  ]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.synthesia.io/v2/videos")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```