Create a split screen project
create_split_screen_projectPOST/v1/projects/split-screenBeta
Creates a split screen project from two videos you uploaded: the main video on top and a background video under it.
Beta. This endpoint is supported and can gain new fields. If a change would break your code, we tell you before it takes effect and list it in the changelog.
About
Call it after complete_upload (POST /v1/uploads/{uploadId}/complete), with the ids of two videos. The two videos and the language are chosen here and only here: to use other videos, create another project. The layout, the captions and the start of the background can be changed later with update_split_screen_project (PATCH /v1/projects/{projectId}/split-screen). Nothing is built and no credits are used until you call build_split_screen_video (POST /v1/projects/{projectId}/split-screen/build). Send an Idempotency-Key: a retry without one creates a second project.
Example use
A channel that posts interview moments uploads an interview clip and a gameplay video, then creates a project with the interview on top and the gameplay under it. It checks the price with estimate_analysis (POST /v1/projects/{projectId}/analyze/estimate) before it builds.
Endpoint
POST https://api.gpt-video.com/v1/projects/split-screen- Scope
clips:write. - Rate limit 60 requests a minute for each key and 120 for each account, shared by every
writecall. See rate limits. Idempotency-Keyis optional.
Parameters
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | Optional | Send the same key on every retry of this request. From 1 to 255 visible characters, such as a UUID. See idempotency. |
Body
| Name | Type | Required | Description |
|---|---|---|---|
mainAssetId | string | Required | The main video, shown on top and transcribed for the captions: the asset id that complete_upload (POST /v1/uploads/{uploadId}/complete) returned. It cannot be changed later. Matches ^ast_n?[0-9a-f]{20}$. |
backgroundAssetId | string | Required | The background video, shown under the main video, such as gameplay footage. It cannot be changed later. Matches ^ast_n?[0-9a-f]{20}$. |
layout | string | Required | How the screen is split: 50-50 gives each video half the screen, 60-40 gives the main video more. One of 50-50, 60-40. |
captionStyle | string or null | Required | The caption style, or null for a video with no captions. get_setting_options (GET /v1/reference/setting-options) lists every style with what it looks like. One of bold-karaoke, word-pop, boxed, outline. |
language | string | Required | The language spoken in the main video: en, it, or auto to detect it. It cannot be changed later. One of auto, it, en. |
backgroundStartSec | number | Optional | Where the background video starts, in seconds, before the end of the background video. Leave it out to let the first build choose a random start, which is then kept. At least 0. |
vocabulary | string | Optional | Names, brands and jargon the transcription should spell right, separated by commas. At most 311 characters. At most 311 characters. |
title | string | Optional | A name for the project, shown in the GPT-Video app. Leave it out to get a default name. From 1 to 120 characters. Matches \S. |
Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.
Request
Create a split screen project.
# Create the key once. Send this same key again if you retry this request.
IDEMPOTENCY_KEY=$(uuidgen)
curl -X POST "https://api.gpt-video.com/v1/projects/split-screen" \
-H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
"layout": "60-40",
"captionStyle": "bold-karaoke",
"language": "en",
"title": "Product demo over the city"
}'// Create the key once. Send this same key again if you retry this request.
const idempotencyKey = crypto.randomUUID();
const res = await fetch("https://api.gpt-video.com/v1/projects/split-screen", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
"layout": "60-40",
"captionStyle": "bold-karaoke",
"language": "en",
"title": "Product demo over the city"
}),
});
const data = await res.json();
console.log(data);import os
import uuid
import requests
# Create the key once. Send this same key again if you retry this request.
idempotency_key = str(uuid.uuid4())
r = requests.post(
"https://api.gpt-video.com/v1/projects/split-screen",
headers={
"Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
"Idempotency-Key": idempotency_key,
},
json={
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
"layout": "60-40",
"captionStyle": "bold-karaoke",
"language": "en",
"title": "Product demo over the city"
},
)
data = r.json()
print(data){
"method": "tools/call",
"params": {
"name": "create_split_screen_project",
"arguments": {
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
"layout": "60-40",
"captionStyle": "bold-karaoke",
"language": "en",
"title": "Product demo over the city"
}
}
}Response
A successful call answers HTTP 201. Returns a Project object in project.
Fields to check
| Name | Type | Description |
|---|---|---|
project.id | string | The project id. It starts with prj_. |
project.status | string | draft has not been analyzed yet, processing is being analyzed, ready has clips, failed means the last analysis failed and errorMessage says why. |
Response example
{
"project": {
"id": "prj_5b8e2d71c4a94f06b3e9",
"kind": "splitscreen",
"title": "Product demo over the city",
"status": "draft",
"sourceAssetId": "ast_8e21b0d94c6f4a17b358",
"compositionId": null,
"settings": null,
"missingSettings": [],
"errorMessage": null,
"splitScreen": {
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
"layout": "60-40",
"captionStyle": "bold-karaoke",
"language": "en",
"backgroundStartSec": null,
"vocabulary": null
},
"streamer": null,
"redditStory": null,
"fakeText": null,
"vibeEdit": null,
"createdAt": "2026-09-01T11:00:00.000Z",
"updatedAt": "2026-09-01T11:00:00.000Z"
}
}All response fields
| Name | Type | Required | Description |
|---|---|---|---|
project | Project object | Required | Described in Project object. |
More examples
A background video that does not exist
POST /v1/projects/split-screen
{
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_0b1e5c9d3a7f40628e2d",
"layout": "50-50",
"captionStyle": null,
"language": "en"
}{
"error": {
"code": "NOT_FOUND",
"message": "Asset ast_0b1e5c9d3a7f40628e2d not found",
"hint": "Check the id. An id that belongs to another account is not found either.",
"requestId": "req_6d2a9f1c4e7b03a85d1f2c96"
}
}A background that is not a video
POST /v1/projects/split-screen
{
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_e8a35ed674148f27b585",
"layout": "50-50",
"captionStyle": null,
"language": "en"
}{
"error": {
"code": "CONFLICT",
"message": "This asset is not a video. A split screen needs two videos: the main video and the background video.",
"requestId": "req_1f8c3e6a0d2b49f75a3e8c01"
}
}A background start past the end of the background
POST /v1/projects/split-screen
{
"mainAssetId": "ast_8e21b0d94c6f4a17b358",
"backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
"layout": "50-50",
"captionStyle": "word-pop",
"language": "en",
"backgroundStartSec": 90
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "backgroundStartSec (90) is past the end of the background video (60 seconds). Use a smaller value.",
"hint": "Fix the request as the message says, then send it again.",
"requestId": "req_9b4e1a7d3c0f5e28b6a1d4c7"
}
}Errors
| Code | Status | When | What to do |
|---|---|---|---|
NOT_FOUND | 404 | A background video that does not exist | Change the request, then send it again. |
CONFLICT | 409 | A background that is not a video | Read the resource again, then decide. Do not send the same request again. |
Every call can also answer these errors. VALIDATION_FAILED, UNKNOWN_PARAMETER, UNAUTHORIZED, SCOPE_MISSING, IDEMPOTENCY_IN_PROGRESS, PAYLOAD_TOO_LARGE, IDEMPOTENCY_KEY_REUSED, RATE_LIMITED, INTERNAL, UNAVAILABLE.
Cost
No credits are used. The build is charged when you call build_split_screen_video (POST /v1/projects/{projectId}/split-screen/build).
Related operations
update_split_screen_project(PATCH /v1/projects/{projectId}/split-screen) Update a split screen projectbuild_split_screen_video(POST /v1/projects/{projectId}/split-screen/build) Build a split screen videoestimate_analysis(POST /v1/projects/{projectId}/analyze/estimate) Estimate the credits of an analysis or a buildcomplete_upload(POST /v1/uploads/{uploadId}/complete) Complete an uploadget_setting_options(GET /v1/reference/setting-options) Get the setting options
Questions
Can I change the videos or the language after creating the project?
No. The two videos and the language are chosen when the project is created, because the price and the captions depend on them. To use other videos, create another project.
What does a split screen build cost?
One credit per started minute of the main video and of the background video together. estimate_analysis (POST /v1/projects/{projectId}/analyze/estimate) tells you the price before you build.