Skip to content
Create a key

Paste a key to use it in the examples on these pages. The key stays in this browser tab. These pages never send it anywhere.

Complete an upload

complete_uploadPOST/v1/uploads/{uploadId}/completeBeta

Turns an upload whose parts all arrived into an asset that a project can use as its source.

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 once receivedParts lists every part. The file is checked and becomes an asset; pass its id to create_project (POST /v1/projects) to analyze it. If parts are missing, nothing changes and the answer names them. The first bytes of the file are checked before anything else: only MP4, MOV, MKV, WebM, AVI and MPEG-TS files go on. A playlist (M3U8), a DASH manifest, a web page or any other text file is refused, and the upload stays open: cancel it with cancel_upload (DELETE /v1/uploads/{uploadId}). If the file passes that check but cannot be read as a video, the upload also stays open with all its parts: call complete_upload (POST /v1/uploads/{uploadId}/complete) again, or cancel it. After a success the upload no longer exists, so a retry without an Idempotency-Key answers not found; send a key, and a retry with the same key returns the same asset.

Example use

Once receivedParts lists every part, a script completes the upload with a title for the video. It passes the id of the new asset to create_project (POST /v1/projects) to start an Auto Clip project on it.

Endpoint

Code
POST https://api.gpt-video.com/v1/uploads/{uploadId}/complete
  • Scope clips:write.
  • Rate limit 60 requests a minute for each key and 120 for each account, shared by every write call. See rate limits.
  • Idempotency-Key is optional.

Parameters

Path

NameTypeRequiredDescription
uploadIdstringRequiredThe id of the upload, as create_upload (POST /v1/uploads) returned it. Matches ^upl_[0-9a-f]{20}$.

Headers

NameTypeRequiredDescription
Idempotency-KeystringOptionalSend the same key on every retry of this request. From 1 to 255 visible characters, such as a UUID. See idempotency.

Body

The body is optional. Send only the fields you want to change.

NameTypeRequiredDescription
titlestringOptionalThe title of the video in the app. Leave it out to use the name of the file without its extension. From 1 to 200 characters.

Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.

Request

Complete an upload.

Request example
# 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/uploads/upl_e7b04c92a15d4f36b8c7/complete" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Product teaser"
  }'
// 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/uploads/upl_e7b04c92a15d4f36b8c7/complete", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "title": "Product teaser"
  }),
});
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/uploads/upl_e7b04c92a15d4f36b8c7/complete",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json={
        "title": "Product teaser"
    },
)
data = r.json()
print(data)
{
  "method": "tools/call",
  "params": {
    "name": "complete_upload",
    "arguments": {
      "uploadId": "upl_e7b04c92a15d4f36b8c7",
      "title": "Product teaser"
    }
  }
}

Response

A successful call answers HTTP 201.

Fields to check

NameTypeDescription
asset.idstringThe asset id. It starts with ast_.

Response example

HTTP 201
{
  "asset": {
    "id": "ast_5b0e7c3a9d1f4286b7e4",
    "kind": "video",
    "title": "Product teaser",
    "filename": "teaser.mp4",
    "origin": "upload",
    "mimeType": "video/mp4",
    "sizeBytes": 4096,
    "durationSec": 42.5,
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "hasAudio": true,
    "createdAt": "2026-09-01T10:42:00.000Z",
    "deletesAt": null
  }
}
All response fields
NameTypeRequiredDescription
assetobjectRequiredThe new asset. Its id is what create_project (POST /v1/projects) takes as sourceAssetId.
asset.idstringRequiredThe asset id. It starts with ast_.
asset.kindstringRequiredOne of video, audio, image, font, other.
asset.titlestringRequired
asset.filenamestring or nullRequiredThe name of the file as it was uploaded, when there was one.
asset.originstringRequiredupload was sent by you, link was imported from a link, converted is a playable copy made for the analysis, export is the file of an export, voice is the audio of a voiceover, separated is the audio an audio separation kept. One of upload, link, converted, export, voice, separated.
asset.mimeTypestringRequired
asset.sizeBytesintegerRequired
asset.durationSecnumber or nullRequired
asset.widthinteger or nullRequiredThe width as the video is shown, rotation applied.
asset.heightinteger or nullRequiredThe height as the video is shown, rotation applied.
asset.fpsnumber or nullRequired
asset.hasAudioboolean or nullRequired
asset.createdAtstringRequiredAn ISO 8601 time in UTC.
asset.deletesAtstring or nullRequiredThe first time the file may be deleted to free space: files are kept for a limited time. null when nothing deletes it.
More examples

Complete an upload with parts missing

POST /v1/uploads/upl_5d2a8f1e6c3b4079e0a1/complete

HTTP 409
{
  "error": {
    "code": "CONFLICT",
    "message": "Parts 1, 2 and 3 are missing. Send them, then complete the upload again.",
    "requestId": "req_4d9f3b7e1a5c0d8b2f6e9a3c"
  }
}

Complete an upload that is not a video

POST /v1/uploads/upl_e7b04c92a15d4f36b8c7/complete

HTTP 400
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "We could not read this file as a video. If it is a video, call complete_upload again; otherwise cancel this upload with cancel_upload and upload the file again as MP4 or MOV.",
    "hint": "Fix the request as the message says, then send it again.",
    "requestId": "req_1a7c5e9b3d0f6a2c8e4b7d1f"
  }
}

Complete an upload whose file is a playlist

POST /v1/uploads/upl_e7b04c92a15d4f36b8c7/complete

HTTP 400
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "This file is not a video file GPT-Video can read. Cancel this upload with cancel_upload and upload the file again as MP4 or MOV.",
    "hint": "Fix the request as the message says, then send it again.",
    "requestId": "req_6b2e8d4a0c7f3e9b1d5a8c2f"
  }
}

Complete an upload that does not exist

POST /v1/uploads/upl_3b6f9e2d7c0a4e51a8d9/complete

HTTP 404
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Upload upl_3b6f9e2d7c0a4e51a8d9 not found",
    "hint": "Check the id. An id that belongs to another account is not found either.",
    "requestId": "req_8f2c6a0e4b9d3f7a1c5e8b2d"
  }
}

Errors

CodeStatusWhenWhat to do
NOT_FOUND404Complete an upload that does not existChange the request, then send it again.
CONFLICT409Complete an upload with parts missingRead 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. Analyzing the video later is what uses credits.

Questions

Does completing an upload start the analysis?

No. It only creates the asset. Create a project with the asset as its source, then analyze the project.

What if the file is not a readable video?

The answer is a validation error and the upload stays open. If the start of the file is not a video format GPT-Video reads (MP4, MOV, MKV, WebM, AVI or MPEG-TS), calling again gives the same answer: cancel the upload and send the file again as MP4 or MOV. If the format is right but the file could not be read, call complete_upload (POST /v1/uploads/{uploadId}/complete) again; if it still fails, cancel the upload and send the file again.

View as Markdown