Skip to main content

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

  1. Request Upload URL: Get a pre-signed URL for direct upload to cloud storage
  2. Upload File: Upload your video file directly to the provided URL
  3. Start Processing: Initiate transcoding to create multiple quality versions
  4. 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

ParameterTypeRequiredDescription
orgIDstringYesYour organization ID (e.g., org_123456789)
envIDstringYesEnvironment identifier (e.g., prod, staging, dev)

Request Body

{
"filename": "product-demo-2024.mp4",
"fileSize": 104857600,
"contentType": "video/mp4"
}
FieldTypeRequiredDefaultDescription
filenamestringYes-Original filename (used for metadata and processing hints)
fileSizeintegerYes-File size in bytes (used for validation and progress tracking)
contentTypestringNovideo/mp4MIME type of the video file

Supported File Types

FormatMIME TypeMax SizeNotes
MP4video/mp45GBRecommended format
MOVvideo/quicktime5GBApple QuickTime
AVIvideo/x-msvideo5GBWindows format
WebMvideo/webm5GBWeb-optimized
MKVvideo/x-matroska5GBContainer 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
}
}
FieldTypeDescription
signedURLstringPre-signed URL for direct upload (valid for 1 hour)
bucketPathstringInternal storage path (required for processing step)
expiresAtintegerUnix timestamp when the upload URL expires

Error Responses

StatusErrorDescription
400invalid_file_sizeFile size exceeds 5GB limit
400unsupported_formatFile type not supported
401unauthorizedInvalid or missing API key
403forbiddenInsufficient permissions for environment
429rate_limit_exceededToo many upload URL requests

Example: Request Upload URL

Step 2: Upload File

After receiving the signed URL, upload your video file directly to cloud storage:

Upload Methods

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

FieldTypeDescription
gcsPathstringStorage path from upload response
videoQualitystringQuality preset: standard, high, ultra
allowPublicAccessbooleanWhether video can be accessed without authentication

Optional Parameters

FieldTypeDefaultDescription
videoNamestring-Display name for the video
maxEdgeLengthinteger-1 (auto)Maximum resolution: 480, 720, 1080, 1440, 2160
maxFrameRateinteger-1 (auto)Maximum frame rate (fps)
pixelFormatstringyuv420pPixel format (see advanced options)
rateControlModestringvariableBitrateconstantBitrate or variableBitrate
twoPassVBREncodingbooleanfalseEnable two-pass encoding for better quality
compressionRatiostringveryfastCompression preset (see advanced options)
audioCodecstringaacAudio codec: aac, mp3, ac3, eac3

Quality Presets

Choose the quality preset that best fits your use case:

PresetResolutionsBitratesUse CaseProcessing Time
standard360p, 480p, 720pOptimized for bandwidthGeneral content, mobile~2-5 minutes
high480p, 720p, 1080pHigher qualityPremium content, desktop~3-7 minutes
ultra720p, 1080p, 1440p, 2160pMaximum qualityProfessional content, 4K~5-15 minutes

Response

Success Response (200 OK)

{
"body": {
"videoID": "vid_AbCdEfGhIjKlMnOp"
}
}
FieldTypeDescription
videoIDstringUnique identifier for the video (use for status checks and playback)

Error Responses

StatusErrorDescription
400invalid_gcs_pathInvalid or non-existent storage path
400invalid_quality_presetUnsupported quality preset
400invalid_parametersInvalid encoding parameters
401unauthorizedInvalid or missing API key
403forbiddenInsufficient permissions
409processing_in_progressVideo already being processed

Example: Start Processing

Advanced Encoding Options

Pixel Formats

Choose the pixel format based on your quality and compatibility requirements:

FormatBit DepthChroma SubsamplingFile SizeCompatibilityUse Case
yuv420p8-bit4:2:0StandardExcellentDefault choice
yuv422p8-bit4:2:2+20%GoodBetter color accuracy
yuv444p8-bit4:4:4+50%LimitedMaximum color fidelity
yuv420p10b10-bit4:2:0+30%LimitedHDR content
yuv422p10b10-bit4:2:2+50%LimitedProfessional HDR
yuv444p10b10-bit4:4:4+70%LimitedMaximum HDR quality
yuv420p12b12-bit4:2:0+40%Very LimitedCinema HDR
yuv422p12b12-bit4:2:2+60%Very LimitedProfessional cinema
yuv444p12b12-bit4:4:4+80%Very LimitedMaximum cinema quality
autoDetect--VariableVariableMatch source format

Compression Presets

Balance encoding speed with file size and quality:

PresetEncoding SpeedQualityFile SizeUse Case
ultrafastFastestLowestLargestQuick previews
superfastVery FastLowLargeRapid prototyping
veryfastFastGoodLargeDevelopment
fasterFastGoodMediumGeneral use
fastMediumBetterMediumBalanced
mediumMediumBetterMediumDefault
slowerSlowHighSmallHigh quality
veryslowVery SlowHighestSmallArchive quality
ultraslowSlowestMaximumSmallestMaximum compression

Rate Control Modes

ModeDescriptionUse Case
variableBitrateBitrate varies based on content complexityGeneral content, streaming
constantBitrateFixed bitrate throughout videoBroadcast, fixed bandwidth

Audio Codecs

CodecQualityFile SizeCompatibilityUse Case
aacHighMediumExcellentDefault choice
aac-heHighSmallGoodMobile optimization
aac-he-v2HighSmallestLimitedMaximum compression
mp3GoodMediumExcellentLegacy compatibility
ac3HighLargeGoodProfessional audio
eac3HighMediumLimitedEnhanced AC3

Processing Status

After initiating processing, your video will go through several states:

StatusDescriptionDuration
transcodingCreating multiple quality versions2-15 minutes
readyProcessing complete, ready for playback-
failedProcessing 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

IssueCauseSolution
Upload fails with 403Expired signed URLRequest new upload URL
Processing stuckInvalid source fileCheck file format and integrity
Large file timeoutSlow upload speedUse resumable uploads or chunked transfer
Quality not as expectedWrong presetChoose appropriate quality preset

Support

For additional help:

  • Check our health endpoint GET /health for service issues
  • Review error codes for detailed explanations
  • Contact support with your organization ID and video ID for assistance