Video Upload
Video Upload API
HesedVid’s video upload system uses a secure, two-step process that allows you to upload large video files directly to our cloud storage without proxying through your servers. This approach provides better performance, reliability, and cost efficiency.
How It Works
- Request Upload URL: Get a pre-signed URL for direct upload to cloud storage
- Upload File: Upload your video file directly to the provided URL
- Start Processing: Initiate transcoding to create multiple quality versions
- Monitor Progress: Track processing status via polling
Tip
The entire upload and processing workflow is designed for reliability. Files are uploaded directly to cloud storage, eliminating bandwidth costs and improving upload speeds.
Step 1: Request Upload URL
/{orgID}/environments/{envID}/videos/upload-url
Generate a secure, time-limited URL for uploading your video file directly to our cloud storage.
Authentication
All requests require authentication via API key:
X-Api-Key: hv_live_123456789abcdef...Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
orgID | string | Yes | Your organization ID (e.g., org_123456789) |
envID | string | Yes | Environment identifier (e.g., prod, staging, dev) |
Request Body
{ "filename": "product-demo-2024.mp4", "fileSize": 104857600, "contentType": "video/mp4"}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
filename | string | Yes | - | Original filename (used for metadata and processing hints) |
fileSize | integer | Yes | - | File size in bytes (used for validation and progress tracking) |
contentType | string | No | video/mp4 | MIME type of the video file |
Supported File Types
| Format | MIME Type | Max Size | Notes |
|---|---|---|---|
| MP4 | video/mp4 | 5GB | Recommended format |
| MOV | video/quicktime | 5GB | Apple QuickTime |
| AVI | video/x-msvideo | 5GB | Windows format |
| WebM | video/webm | 5GB | Web-optimized |
| MKV | video/x-matroska | 5GB | Container format |
Response
Success Response (200 OK)
{ "body": { "signedURL": "https://storage.googleapis.com/hesedvid-uploads/org_123/env_prod/videos/uuid-123.mp4?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=...", "bucketPath": "gs://hesedvid-uploads/org_123/env_prod/videos/uuid-123.mp4", "expiresAt": 1640995200 }}| Field | Type | Description |
|---|---|---|
signedURL | string | Pre-signed URL for direct upload (valid for 1 hour) |
bucketPath | string | Internal storage path (required for processing step) |
expiresAt | integer | Unix timestamp when the upload URL expires |
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | invalid_file_size | File size exceeds 5GB limit |
| 400 | unsupported_format | File type not supported |
| 401 | unauthorized | Invalid or missing API key |
| 403 | forbidden | Insufficient permissions for environment |
| 429 | rate_limit_exceeded | Too many upload URL requests |
Example: Request Upload URL
curl -X POST https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/upload-url \ -H "X-Api-Key: hv_live_123456789abcdef..." \ -H "Content-Type: application/json" \ -d '{ "filename": "product-demo-2024.mp4", "fileSize": 52428800, "contentType": "video/mp4" }'const response = await fetch( 'https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/upload-url', { method: 'POST', headers: { 'X-Api-Key': 'hv_live_123456789abcdef...', 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: 'product-demo-2024.mp4', fileSize: 52428800, contentType: 'video/mp4' }) });
const { body } = await response.json();console.log('Upload URL:', body.signedURL);import requests
response = requests.post( 'https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/upload-url', headers={ 'X-Api-Key': 'hv_live_123456789abcdef...', 'Content-Type': 'application/json' }, json={ 'filename': 'product-demo-2024.mp4', 'fileSize': 52428800, 'contentType': 'video/mp4' })
data = response.json()upload_url = data['body']['signedURL']print(f'Upload URL: {upload_url}')<?php$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/upload-url');curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'X-Api-Key: hv_live_123456789abcdef...', 'Content-Type: application/json']);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'filename' => 'product-demo-2024.mp4', 'fileSize' => 52428800, 'contentType' => 'video/mp4']));curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);$data = json_decode($response, true);$upload_url = $data['body']['signedURL'];
curl_close($ch);?>Step 2: Upload File
After receiving the signed URL, upload your video file directly to cloud storage:
Upload Methods
curl -X PUT "SIGNED_URL_FROM_STEP_1" \ -H "Content-Type: video/mp4" \ --data-binary @product-demo-2024.mp4const file = document.getElementById('video-file').files[0];
const uploadResponse = await fetch(signedURL, { method: 'PUT', headers: { 'Content-Type': file.type }, body: file});
if (uploadResponse.ok) { console.log('Upload successful');}import requests
with open('product-demo-2024.mp4', 'rb') as f: upload_response = requests.put( signed_url, headers={'Content-Type': 'video/mp4'}, data=f )
if upload_response.status_code == 200: print('Upload successful')Upload Progress Tracking
For large files, you may want to track upload progress:
const uploadWithProgress = async (file, signedURL) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const percentComplete = (event.loaded / event.total) * 100; console.log(`Upload progress: ${percentComplete.toFixed(2)}%`); } });
xhr.addEventListener('load', () => { if (xhr.status === 200) { resolve('Upload complete'); } else { reject(new Error('Upload failed')); } });
xhr.open('PUT', signedURL); xhr.setRequestHeader('Content-Type', file.type); xhr.send(file); });};Caution
Upload URLs expire after 1 hour. For large files, ensure your upload completes within this timeframe. If needed, request a new upload URL.
Step 3: Start Processing
/{orgID}/environments/{envID}/videos/process
After successful upload, initiate video processing to create multiple quality versions and prepare for delivery.
Request Body
{ "videoName": "Product Demo 2024", "gcsPath": "gs://hesedvid-uploads/org_123/env_prod/videos/uuid-123.mp4", "videoQuality": "high", "allowPublicAccess": false, "maxEdgeLength": 1080, "maxFrameRate": 30, "pixelFormat": "yuv420p", "rateControlMode": "variableBitrate", "twoPassVBREncoding": false, "compressionRatio": "veryfast", "audioCodec": "aac"}Required Parameters
| Field | Type | Description |
|---|---|---|
gcsPath | string | Storage path from upload response |
videoQuality | string | Quality preset: standard, high, ultra |
allowPublicAccess | boolean | Whether video can be accessed without authentication |
Optional Parameters
| Field | Type | Default | Description |
|---|---|---|---|
videoName | string | - | Display name for the video |
maxEdgeLength | integer | -1 (auto) | Maximum resolution: 480, 720, 1080, 1440, 2160 |
maxFrameRate | integer | -1 (auto) | Maximum frame rate (fps) |
pixelFormat | string | yuv420p | Pixel format (see advanced options) |
rateControlMode | string | variableBitrate | constantBitrate or variableBitrate |
twoPassVBREncoding | boolean | false | Enable two-pass encoding for better quality |
compressionRatio | string | veryfast | Compression preset (see advanced options) |
audioCodec | string | aac | Audio codec: aac, mp3, ac3, eac3 |
Quality Presets
Choose the quality preset that best fits your use case:
| Preset | Resolutions | Bitrates | Use Case | Processing Time |
|---|---|---|---|---|
standard | 360p, 480p, 720p | Optimized for bandwidth | General content, mobile | ~2-5 minutes |
high | 480p, 720p, 1080p | Higher quality | Premium content, desktop | ~3-7 minutes |
ultra | 720p, 1080p, 1440p, 2160p | Maximum quality | Professional content, 4K | ~5-15 minutes |
Response
Success Response (200 OK)
{ "body": { "videoID": "vid_AbCdEfGhIjKlMnOp" }}| Field | Type | Description |
|---|---|---|
videoID | string | Unique identifier for the video (use for status checks and playback) |
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | invalid_gcs_path | Invalid or non-existent storage path |
| 400 | invalid_quality_preset | Unsupported quality preset |
| 400 | invalid_parameters | Invalid encoding parameters |
| 401 | unauthorized | Invalid or missing API key |
| 403 | forbidden | Insufficient permissions |
| 409 | processing_in_progress | Video already being processed |
Example: Start Processing
curl -X POST https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/process \ -H "X-Api-Key: hv_live_123456789abcdef..." \ -H "Content-Type: application/json" \ -d '{ "videoName": "Product Demo 2024", "gcsPath": "gs://hesedvid-uploads/org_123/env_prod/videos/uuid-123.mp4", "videoQuality": "high", "allowPublicAccess": false, "maxEdgeLength": 1080, "audioCodec": "aac" }'const response = await fetch( 'https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/process', { method: 'POST', headers: { 'X-Api-Key': 'hv_live_123456789abcdef...', 'Content-Type': 'application/json' }, body: JSON.stringify({ videoName: "Product Demo 2024", gcsPath: "gs://hesedvid-uploads/org_123/env_prod/videos/uuid-123.mp4", videoQuality: "high", allowPublicAccess: false, maxEdgeLength: 1080, audioCodec: "aac" }) });
const { body } = await response.json();console.log('Video ID:', body.videoID);import requests
response = requests.post( 'https://api.hesedvid.com/v1/api/org_123/environments/prod/videos/process', headers={ 'X-Api-Key': 'hv_live_123456789abcdef...', 'Content-Type': 'application/json' }, json={ 'videoName': 'Product Demo 2024', 'gcsPath': 'gs://hesedvid-uploads/org_123/env_prod/videos/uuid-123.mp4', 'videoQuality': 'high', 'allowPublicAccess': False, 'maxEdgeLength': 1080, 'audioCodec': 'aac' })
data = response.json()video_id = data['body']['videoID']print(f'Video ID: {video_id}')Advanced Encoding Options
Pixel Formats
Choose the pixel format based on your quality and compatibility requirements:
| Format | Bit Depth | Chroma Subsampling | File Size | Compatibility | Use Case |
|---|---|---|---|---|---|
yuv420p | 8-bit | 4:2:0 | Standard | Excellent | Default choice |
yuv422p | 8-bit | 4:2:2 | +20% | Good | Better color accuracy |
yuv444p | 8-bit | 4:4:4 | +50% | Limited | Maximum color fidelity |
yuv420p10b | 10-bit | 4:2:0 | +30% | Limited | HDR content |
yuv422p10b | 10-bit | 4:2:2 | +50% | Limited | Professional HDR |
yuv444p10b | 10-bit | 4:4:4 | +70% | Limited | Maximum HDR quality |
yuv420p12b | 12-bit | 4:2:0 | +40% | Very Limited | Cinema HDR |
yuv422p12b | 12-bit | 4:2:2 | +60% | Very Limited | Professional cinema |
yuv444p12b | 12-bit | 4:4:4 | +80% | Very Limited | Maximum cinema quality |
autoDetect | - | - | Variable | Variable | Match source format |
Compression Presets
Balance encoding speed with file size and quality:
| Preset | Encoding Speed | Quality | File Size | Use Case |
|---|---|---|---|---|
ultrafast | Fastest | Lowest | Largest | Quick previews |
superfast | Very Fast | Low | Large | Rapid prototyping |
veryfast | Fast | Good | Large | Development |
faster | Fast | Good | Medium | General use |
fast | Medium | Better | Medium | Balanced |
medium | Medium | Better | Medium | Default |
slower | Slow | High | Small | High quality |
veryslow | Very Slow | Highest | Small | Archive quality |
ultraslow | Slowest | Maximum | Smallest | Maximum compression |
Rate Control Modes
| Mode | Description | Use Case |
|---|---|---|
variableBitrate | Bitrate varies based on content complexity | General content, streaming |
constantBitrate | Fixed bitrate throughout video | Broadcast, fixed bandwidth |
Audio Codecs
| Codec | Quality | File Size | Compatibility | Use Case |
|---|---|---|---|---|
aac | High | Medium | Excellent | Default choice |
aac-he | High | Small | Good | Mobile optimization |
aac-he-v2 | High | Smallest | Limited | Maximum compression |
mp3 | Good | Medium | Excellent | Legacy compatibility |
ac3 | High | Large | Good | Professional audio |
eac3 | High | Medium | Limited | Enhanced AC3 |
Processing Status
After initiating processing, your video will go through several states:
| Status | Description | Duration |
|---|---|---|
transcoding | Creating multiple quality versions | 2-15 minutes |
ready | Processing complete, ready for playback | - |
failed | Processing failed (check error message) | - |
Monitoring Processing
You can monitor processing status by polling video details:
const checkProcessingStatus = async (videoID) => { const response = await fetch( `https://api.hesedvid.com/v1/api/org_123/videos/${videoID}/details`, { headers: { 'X-Api-Key': 'hv_live_123456789abcdef...' } } );
const { body } = await response.json(); return body.status; // 'transcoding', 'ready', or 'failed'};Best Practices
File Preparation
- Use MP4 format for best compatibility
- Optimize source files to reduce upload time
- Include metadata (title, description) for better organization
- Test with small files before uploading large videos
Error Handling
const uploadVideo = async (file) => { try { // Step 1: Get upload URL const uploadResponse = await requestUploadURL(file); const { signedURL, bucketPath } = uploadResponse.body;
// Step 2: Upload file const uploadResult = await uploadFile(file, signedURL); if (!uploadResult.ok) { throw new Error('Upload failed'); }
// Step 3: Start processing const processResponse = await startProcessing(bucketPath); return processResponse.body.videoID;
} catch (error) { console.error('Upload process failed:', error); throw error; }};Rate Limiting
Upload URL requests are rate limited:
- 100 requests per hour per organization
- 10 requests per minute per API key
Tip
For high-volume uploads, implement exponential backoff and retry logic. Consider requesting multiple upload URLs in advance for batch uploads.
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Upload fails with 403 | Expired signed URL | Request new upload URL |
| Processing stuck | Invalid source file | Check file format and integrity |
| Large file timeout | Slow upload speed | Use resumable uploads or chunked transfer |
| Quality not as expected | Wrong preset | Choose appropriate quality preset |
Support
For additional help:
- Check our health endpoint
GET /healthfor service issues - Review error codes for detailed explanations
- Contact support with your organization ID and video ID for assistance