Create a project
create_projectPOST/v1/projectsBeta
Creates an Auto Clip project on a video you uploaded, with the settings you choose.
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 id of the video it returned. The source video is chosen here and only here: to analyze another video, create another project. You can set the settings now or later with update_project (PATCH /v1/projects/{projectId}); every setting listed in missingSettings must be chosen before analyze_project (POST /v1/projects/{projectId}/analyze). Nothing is analyzed and no credits are used until you call analyze_project. Send an Idempotency-Key: a retry without one creates a second project.
Example use
A podcast team uploads each new episode, creates a project on it with the language and the number of clips it wants, and checks the price with estimate_analysis (POST /v1/projects/{projectId}/analyze/estimate) before it calls analyze_project (POST /v1/projects/{projectId}/analyze).
Endpoint
POST https://api.gpt-video.com/v1/projects- 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 |
|---|---|---|---|
sourceAssetId | string | Required | The video to analyze: the asset id that complete_upload (POST /v1/uploads/{uploadId}/complete) returned. It cannot be changed later. Matches ^ast_n?[0-9a-f]{20}$. |
title | string | Optional | A name for the project. Leave it out to get a default name. From 1 to 120 characters. Matches \S. |
settings | object | Optional | Auto Clip settings. A field you leave out is left as it is, and every one can be set later with update_project (PATCH /v1/projects/{projectId}). |
settings.language | string | Optional | The language spoken in the video: en, it, or auto to detect it. One of auto, it, en. |
settings.clipCount | integer | Optional | How many clips the analysis proposes. From 1 to 100. From 1 to 100. |
settings.minDurationSec | number | Optional | The shortest clip the analysis may propose, in seconds. From 5 to 180, and not longer than maxDurationSec when the analysis starts. From 5 to 180. |
settings.maxDurationSec | number | Optional | The longest clip the analysis may propose, in seconds. From 10 to 300. From 10 to 300. |
settings.objective | string | Optional | What the clips should be good at. get_setting_options (GET /v1/reference/setting-options) lists every objective with what it means. One of viral, educational, entertaining, emotional, controversial, howto, business. |
settings.captionStyle | string | Optional | The caption style of the clips. get_setting_options (GET /v1/reference/setting-options) lists every style with what it looks like. One of bold-karaoke, word-pop, boxed, outline. |
settings.reframe | string | Optional | How a clip follows the speaker: face follows the face of the speaker, center keeps the middle of the frame. One of face, center. |
settings.instructions | string or null | Optional | Free text that tells the analysis what to look for, such as only the moments where a given person speaks. At most 2000 characters; null removes them. At most 2000 characters. |
settings.vocabulary | string or null | Optional | Names, brands and jargon the transcription should spell right, separated by commas. It is used when the video is transcribed, so set it before the first analysis. At most 311 characters; null removes it. At most 311 characters. |
Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.
Request
Create a project on an uploaded video.
# 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" \
-H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceAssetId": "ast_8e21b0d94c6f4a17b358",
"title": "Product demo clips",
"settings": {
"language": "en",
"clipCount": 5,
"minDurationSec": 20,
"maxDurationSec": 45,
"objective": "howto",
"captionStyle": "bold-karaoke",
"reframe": "face"
}
}'// 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", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"sourceAssetId": "ast_8e21b0d94c6f4a17b358",
"title": "Product demo clips",
"settings": {
"language": "en",
"clipCount": 5,
"minDurationSec": 20,
"maxDurationSec": 45,
"objective": "howto",
"captionStyle": "bold-karaoke",
"reframe": "face"
}
}),
});
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",
headers={
"Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
"Idempotency-Key": idempotency_key,
},
json={
"sourceAssetId": "ast_8e21b0d94c6f4a17b358",
"title": "Product demo clips",
"settings": {
"language": "en",
"clipCount": 5,
"minDurationSec": 20,
"maxDurationSec": 45,
"objective": "howto",
"captionStyle": "bold-karaoke",
"reframe": "face"
}
},
)
data = r.json()
print(data){
"method": "tools/call",
"params": {
"name": "create_auto_clip_project",
"arguments": {
"sourceAssetId": "ast_8e21b0d94c6f4a17b358",
"title": "Product demo clips",
"settings": {
"language": "en",
"clipCount": 5,
"minDurationSec": 20,
"maxDurationSec": 45,
"objective": "howto",
"captionStyle": "bold-karaoke",
"reframe": "face"
}
}
}
}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_27dd3348d28146ce5a3a",
"kind": "autoclip",
"title": "Product demo clips",
"status": "draft",
"sourceAssetId": "ast_8e21b0d94c6f4a17b358",
"compositionId": null,
"settings": {
"language": "en",
"clipCount": 5,
"minDurationSec": 20,
"maxDurationSec": 45,
"objective": "howto",
"captionStyle": "bold-karaoke",
"reframe": "face",
"instructions": null,
"vocabulary": null
},
"missingSettings": [],
"splitScreen": null,
"streamer": null,
"redditStory": null,
"fakeText": null,
"vibeEdit": null,
"errorMessage": 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 video that does not exist
POST /v1/projects
{
"sourceAssetId": "ast_0b1e5c9d3a7f40628e2d"
}{
"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_14c0d8b7f8af5ac83e1b0c01"
}
}Errors
| Code | Status | When | What to do |
|---|---|---|---|
NOT_FOUND | 404 | A video that does not exist | Change the request, then send it again. |
CONFLICT | 409 | The asset is not a video, or the file of the video was deleted to free up storage. The message says which one. | 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 analysis is charged when you call analyze_project (POST /v1/projects/{projectId}/analyze).
Related operations
update_project(PATCH /v1/projects/{projectId}) Update a projectestimate_analysis(POST /v1/projects/{projectId}/analyze/estimate) Estimate the credits of an analysis or a buildanalyze_project(POST /v1/projects/{projectId}/analyze) Analyze a projectcomplete_upload(POST /v1/uploads/{uploadId}/complete) Complete an uploadget_setting_options(GET /v1/reference/setting-options) Get the setting optionslist_assets(GET /v1/assets) List your videos
Questions
Can I change the video of a project after creating it?
No. A project analyzes the video it was created with. To analyze another video, create another project with that video as sourceAssetId.
Do I have to choose every setting when I create the project?
No. Send the settings you know now and set the rest with update_project (PATCH /v1/projects/{projectId}). analyze_project (POST /v1/projects/{projectId}/analyze) refuses until missingSettings is empty.
What happens if I retry a create that timed out?
Send it again with the same Idempotency-Key. If the first try succeeded, you get its answer back and no second project is created. Without a key, a retry creates another project.