Skip to main content

🎥 Uploading Videos

This guide explains the different methods available for uploading videos using the ZapCap API. Each method has its own benefits and use cases.

Available Upload Methods​

  1. Upload Video by URL
  2. Direct Video Upload
  3. Multipart Upload (Create Upload & Complete Upload)

1. Upload Video by URL​

Endpoint: POST /videos/url​

This method allows you to upload a video by providing a URL where the video is hosted.

Request Body:​

{
"url": "https://example.com/path/to/video.mp4"
}

Benefits:​

  • Convenient for videos already hosted online
  • No need to download and re-upload the video
  • Suitable for small-medium files without worrying about upload speed from your local machine

2. Direct Video Upload​

Endpoint: POST /videos​

This method allows you to directly upload a video file from your local machine or server.

Request Headers and Body:​

  • Content-Type: multipart/form-data; boundary=<boundary_string>
    • The boundary_string is a unique string that separates different parts of the multipart request.
  • Body: Include the video file as form-data

How to set the Content-Type header in different languages:​

  1. cURL:

    curl -X POST "https://api.zapcap.ai/videos" \
    -H "Content-Type: multipart/form-data" \
    -H "x-api-key: YOUR_API_KEY" \
    -F "file=@/path/to/your/video.mp4"

    Note: cURL automatically generates and sets the boundary.

  2. Python (using requests library):

    import requests

    url = "https://api.zapcap.ai/videos"
    files = {'file': open('path/to/your/video.mp4', 'rb')}
    headers = {
    'x-api-key': 'YOUR_API_KEY'
    }

    response = requests.post(url, files=files, headers=headers)

    Note: The requests library automatically sets the Content-Type header with the appropriate boundary.

  3. JavaScript (using fetch API):

    const formData = new FormData();
    formData.append("file", fileInput.files[0]);

    fetch("https://api.zapcap.ai/videos", {
    method: "POST",
    headers: {
    "x-api-key": "YOUR_API_KEY",
    },
    body: formData,
    })
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.error("Error:", error));

    Note: The fetch API automatically sets the Content-Type header with the appropriate boundary when using FormData.

Benefits:​

  • Simple and straightforward for smaller files
  • Useful when you have the video file locally and it's not too large
  • Good for quick uploads without the need for multipart complexity

3. Multipart Upload​

This method is split into two steps: initiating the upload and then completing it. It's ideal for large files.

Step 1: Create Upload​

Endpoint: POST /videos/upload​

Initiates a multipart upload session.

Request Body:​

{
"uploadParts": [
{ "contentLength": 10485760 }
// Add more parts as needed
],
"filename": "sample.mp4"
}

Response:​

Provides uploadId, videoId, and presigned URLs for uploading each part. Refer to the API reference for more details.

Step 2: Complete Upload​

Endpoint: POST /videos/upload/complete​

Finalizes the multipart upload process.

Request Body:​

{
"uploadId": "upload_abc123",
"videoId": "video_xyz789"
}

Complete Multipart Upload Examples​

Here are complete examples showing how to upload large files using multipart upload:

Node.js​

import axios from "axios";
import { createReadStream, statSync } from "fs";

const API_KEY = "YOUR_API_KEY";
const API_BASE = "https://api.zapcap.ai";
const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB per part

async function multipartUpload(filePath) {
const fileSize = statSync(filePath).size;
const numParts = Math.ceil(fileSize / CHUNK_SIZE);

// 1. Calculate part sizes
const uploadParts = [];
for (let i = 0; i < numParts; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, fileSize);
uploadParts.push({ contentLength: end - start });
}

// 2. Initialize multipart upload
console.log(`Initializing upload with ${numParts} parts...`);
const initResponse = await axios.post(
`${API_BASE}/videos/upload`,
{
uploadParts,
filename: filePath.split("/").pop(),
},
{ headers: { "x-api-key": API_KEY } },
);

const { uploadId, videoId, parts } = initResponse.data;
console.log("Upload initialized, videoId:", videoId);

// 3. Upload each part using presigned URLs
const fileStream = createReadStream(filePath, { highWaterMark: CHUNK_SIZE });
let partIndex = 0;

for await (const chunk of fileStream) {
const { presignedUrl } = parts[partIndex];
console.log(`Uploading part ${partIndex + 1}/${numParts}...`);

await axios.put(presignedUrl, chunk, {
headers: { "Content-Length": chunk.length },
});
partIndex++;
}

// 4. Complete the upload
console.log("Completing upload...");
await axios.post(
`${API_BASE}/videos/upload/complete`,
{ uploadId, videoId },
{ headers: { "x-api-key": API_KEY } },
);

console.log("Upload complete! Video ID:", videoId);
return videoId;
}

multipartUpload("/path/to/large-video.mp4");

Python​

import requests
import os
import math

API_KEY = 'YOUR_API_KEY'
API_BASE = 'https://api.zapcap.ai'
CHUNK_SIZE = 10 * 1024 * 1024 # 10MB per part

def multipart_upload(file_path):
file_size = os.path.getsize(file_path)
num_parts = math.ceil(file_size / CHUNK_SIZE)

# 1. Calculate part sizes
upload_parts = []
for i in range(num_parts):
start = i * CHUNK_SIZE
end = min(start + CHUNK_SIZE, file_size)
upload_parts.append({'contentLength': end - start})

# 2. Initialize multipart upload
print(f'Initializing upload with {num_parts} parts...')
init_response = requests.post(
f'{API_BASE}/videos/upload',
headers={'x-api-key': API_KEY},
json={
'uploadParts': upload_parts,
'filename': os.path.basename(file_path)
}
)
init_response.raise_for_status()
data = init_response.json()
upload_id = data['uploadId']
video_id = data['videoId']
parts = data['parts']
print(f'Upload initialized, videoId: {video_id}')

# 3. Upload each part using presigned URLs
with open(file_path, 'rb') as f:
for i, part in enumerate(parts):
chunk = f.read(CHUNK_SIZE)
print(f'Uploading part {i + 1}/{num_parts}...')

response = requests.put(
part['presignedUrl'],
data=chunk,
headers={'Content-Length': str(len(chunk))}
)
response.raise_for_status()

# 4. Complete the upload
print('Completing upload...')
complete_response = requests.post(
f'{API_BASE}/videos/upload/complete',
headers={'x-api-key': API_KEY},
json={'uploadId': upload_id, 'videoId': video_id}
)
complete_response.raise_for_status()

print(f'Upload complete! Video ID: {video_id}')
return video_id

multipart_upload('/path/to/large-video.mp4')

Benefits of Multipart Upload:​

  • Ideal for large files
  • Allows for pause and resume of uploads
  • Better handling of network interruptions
  • Improved upload speeds for large files

General Notes​

  • All methods support an optional ttl query parameter (values: "1d", "7d", "30d") for specifying the Time to Live for the uploaded video.
  • After successful upload, you'll receive a video ID which you can use to create video tasks for further processing.

Audio-Only Uploads (Transparent Overlay Renders)​

When you only need a caption overlay (not a re-rendered MP4), you can upload an audio-only file (e.g. .mp3, .wav) instead of a full video. This skips the video-bytes upload entirely and is ideal for NLE plugin workflows where the editor's source clip stays on the timeline and only the captions need to be rendered.

Audio-only uploads work with all three upload methods (URL, direct, multipart). The downstream task must then specify a transparent render with explicit dimensions:

POST /videos/{videoId}/task
{
"templateId": "a51c5222-47a7-4c37-b052-7b9853d66bf6",
"autoApprove": true,
"exportSettings": {
"outputMode": "transparent",
"outputCodec": "prores4444",
"fps": 23.976,
"width": 1920,
"height": 1080
}
}

If you upload audio but request a non-transparent render, the task will fail with AudioSourceRequiresTransparent — see Advanced Options → Transparent Overlay Renders.

Choosing the Right Method​

  • Upload by URL: Best for videos already hosted online or medium-sized` files.
  • Direct Upload: Ideal for smaller files and quick, simple uploads.
  • Multipart Upload: Recommended for large files, especially in environments with unreliable network connections.

Remember to check the API reference for any updates or additional parameters that may be required for each method.