VIDing.AI REST API v2.1 — Selective Video Encoding
VIDing.AI supports two authentication methods:
For programmatic access, pass your API key in the Authorization header. API keys start with vd_live_ for production or vd_test_ for sandbox.
$ curl https://api.viding.ai/api/v1/jobs \
-H "Authorization: Bearer vd_live_abc123..."
For browser-based access, authenticate via POST /login. The server sets a session cookie that is sent automatically on subsequent requests.
$ curl -X POST https://viding.ai/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "password=YOUR_PASSWORD" \
-c cookies.txt
# Use the session cookie for subsequent requests
$ curl https://viding.ai/api/compress -b cookies.txt
Store your API key securely. The full key is only shown once at creation time. Revoked keys are rejected instantly.
All API requests are made to:
https://api.viding.ai
For self-hosted or development, replace with your server URL. All endpoints accept and return application/json unless noted otherwise.
Start a multipart upload. The API returns presigned URLs for each chunk. Maximum file size: 10 GB.
| Parameter | Type | Description |
|---|---|---|
filename required | string | Original filename with extension |
mime_type required | string | video/mp4, video/quicktime, video/webm, video/x-matroska, video/x-msvideo |
file_size required | integer | Total file size in bytes (max 10,737,418,240) |
{
"upload_id": "a1b2c3d4-e5f6-...",
"chunk_count": 50,
"chunk_size": 10485760,
"chunk_urls": ["https://storage.viding.ai/...", ...],
"expires_at": "2026-04-01T12:00:00Z"
}
Upload each chunk to its presigned URL. Send the raw binary data as the request body with Content-Type: application/octet-stream.
| Parameter | Type | Description |
|---|---|---|
upload_id required | string (path) | Upload ID from init response |
part_number required | integer (path) | 1-indexed chunk number |
Content-MD5 optional | string (header) | Base64-encoded MD5 for integrity check |
Finalize the upload after all chunks are uploaded. Returns a file_id used to create compression jobs.
{
"file_id": "f1a2b3c4-...",
"filename": "interview.mp4",
"file_size": 524288000,
"status": "ready"
}
For files under 100 MB, you can use the single-file upload endpoint instead:
Upload a video file directly via multipart/form-data. The server compresses it immediately and returns the result. Best for files under 100 MB.
| Parameter | Type | Description |
|---|---|---|
video required | file | Video file (MP4, MOV, MKV, WebM, AVI) |
target_ratio optional | number | Target compression ratio 0.1–0.9 (default: 0.3) |
Submit a video for compression using VIDing.AI's selective encoding engine. The job runs asynchronously — poll the status endpoint or register a webhook.
| Parameter | Type | Description |
|---|---|---|
file_id required | string | UUID from completed upload |
codec optional | string | h264 | h265 | av1 — default: h265 |
preset optional | string | Named preset: fast, balanced, quality (overrides codec/crf/speed) |
crf optional | integer | Constant rate factor 0–63 (default: 23) |
target_vmaf optional | number | Target VMAF score 80–100 |
target_ratio optional | number | Target compression ratio 0.1–0.9 |
webhook_url optional | string | URL to receive job completion event |
{
"id": "job-uuid-...",
"status": "queued",
"codec": "h265",
"preset": "balanced",
"created_at": "2026-04-01T10:30:00Z"
}
Returns the current status and details of a compression job.
| Parameter | Type | Description |
|---|---|---|
job_id required | string (path) | Job UUID |
{
"id": "job-uuid-...",
"status": "complete", // queued | processing | complete | failed
"progress": 100,
"input_size": 524288000,
"output_size": 157286400,
"compression_ratio": 0.30,
"codec": "h265",
"duration_seconds": 42.7,
"created_at": "2026-04-01T10:30:00Z",
"completed_at": "2026-04-01T10:30:42Z"
}
Status transitions: queued → processing → complete | failed
Returns perceptual quality metrics for a completed job.
{
"vmaf": 96.4,
"psnr": 42.1,
"ssim": 0.991,
"bitrate_kbps": 2400,
"frames_analyzed": 7200,
"selective_ratio": 0.68 // fraction of frames using selective encoding
}
Lightweight status polling endpoint. Returns the current state of the active processing job for the authenticated user.
{
"status": "processing", // idle | processing | complete
"message": "Processing video... This may take a few minutes.",
"elapsed_seconds": 18.3
}
Returns a time-limited presigned URL for downloading the compressed video. URLs expire after 60 minutes.
{
"download_url": "https://storage.viding.ai/outputs/...",
"filename": "interview_compressed.mp4",
"file_size": 157286400,
"expires_at": "2026-04-01T11:30:00Z"
}
Validate one or more video files. Returns codec details, bitrate analysis, and encoding quality assessment. Supports batch processing.
| Parameter | Type | Description |
|---|---|---|
filename | string | Single video filename |
filenames | array | Batch mode: list of filenames |
generate_pdf optional | boolean | Generate a PDF report (default: false) |
generate_csv optional | boolean | Generate a CSV report (default: false) |
Requires Authorization: Bearer header.
Register webhook endpoints to receive real-time notifications when jobs complete, fail, or other events occur. All webhook payloads are signed with HMAC-SHA256.
| Parameter | Type | Description |
|---|---|---|
url required | string | HTTPS URL to receive events |
events required | array | Event types to subscribe to (see below) |
secret optional | string | Shared secret for HMAC-SHA256 signature verification |
video.compressed — Compression job completed successfullyvideo.processed — Video processing completedvideo.failed — Job failed with errorvalidation.complete — Encoding validation finishedvideo.compressedSent when a compression job completes successfully.
{
"event": "video.compressed",
"timestamp": "2026-04-01T10:30:42Z",
"data": {
"job_id": "job-uuid-...",
"status": "complete",
"input_file": "interview.mp4",
"input_size": 524288000,
"output_size": 157286400,
"compression_ratio": 0.30,
"codec": "h265",
"duration_seconds": 42.7,
"metrics": {
"vmaf": 96.4,
"psnr": 42.1,
"ssim": 0.991
},
"download_url": "https://storage.viding.ai/outputs/...",
"download_expires_at": "2026-04-01T11:30:42Z"
}
}
video.failedSent when a compression job fails.
{
"event": "video.failed",
"timestamp": "2026-04-01T10:31:05Z",
"data": {
"job_id": "job-uuid-...",
"status": "failed",
"error_code": "PROC_008",
"error_message": "Compression failed",
"input_file": "corrupt-video.mp4"
}
}
If you provide a secret when registering, each webhook request includes an X-VIDing-Signature header containing an HMAC-SHA256 hex digest of the request body.
# Python verification
import hmac, hashlib
def verify_webhook(payload_bytes, signature, secret):
expected = hmac.new(
secret.encode(), payload_bytes, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Rate limits are applied per API key. When exceeded, the API returns 429 Too Many Requests with a Retry-After header.
| Tier | Price | Requests/min | Max Upload | Concurrent Jobs | Monthly Bandwidth |
|---|---|---|---|---|---|
| Free | $0/mo | 10 RPM | 500 MB | 1 | 5 GB |
| Pro | $49/mo | 60 RPM | 5 GB | 5 | 500 GB |
| Enterprise | Custom | 600 RPM | 10 GB | Unlimited | Unlimited |
Rate limit headers are included in every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
All error responses include a JSON body with error_code, message, and HTTP status. Use the error_code for programmatic error handling.
All API errors return a structured JSON body:
{
"error_code": "VIDEO_007",
"message": "File too large",
"status": 413
}
VIDEO_001400VIDEO_002404VIDEO_003400VIDEO_004422VIDEO_005422VIDEO_006422VIDEO_007413VIDEO_008400VIDEO_009400VIDEO_010400PROC_001500PROC_002409PROC_003408PROC_008500AUTH_001401AUTH_002401AUTH_003403AUTH_004401AUTH_005401SYS_003429DB_001503PDF_001500Full error codes list available at /api/docs/errors (JSON) and OpenAPI spec at /api/docs/spec.
End-to-end code samples for the complete video compression workflow.
#!/bin/bash
# Complete upload → compress → download flow
API_KEY="vd_live_abc123..."
BASE="https://api.viding.ai"
# --- Option A: Single-file upload + compress (files under 100 MB) ---
$ curl -X POST "$BASE/api/compress" \
-H "Authorization: Bearer $API_KEY" \
-F "video=@interview.mp4" \
-F "target_ratio=0.3"
# --- Option B: Chunked upload for large files ---
# Step 1: Initialize upload
$ curl -X POST "$BASE/api/v1/uploads/init" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "interview.mp4",
"mime_type": "video/mp4",
"file_size": 524288000
}'
# Step 2: Upload chunks to presigned URLs (from init response)
$ curl -X PUT "PRESIGNED_URL_1" \
--data-binary @chunk_001.bin \
-H "Content-Type: application/octet-stream"
# Step 3: Complete upload
$ curl -X POST "$BASE/api/v1/uploads/UPLOAD_ID/complete" \
-H "Authorization: Bearer $API_KEY"
# Step 4: Create compression job
$ curl -X POST "$BASE/api/v1/jobs" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file_id": "FILE_ID_FROM_COMPLETE",
"preset": "balanced",
"webhook_url": "https://your-server.com/webhook"
}'
# Step 5: Poll status (or wait for webhook)
$ curl "$BASE/api/v1/jobs/JOB_ID" \
-H "Authorization: Bearer $API_KEY"
# Step 6: Download compressed file
$ curl "$BASE/api/v1/jobs/JOB_ID/download" \
-H "Authorization: Bearer $API_KEY"
# Response includes a presigned download_url — fetch it directly:
$ curl -o compressed.mp4 "DOWNLOAD_URL"
"""
VIDing.AI — Complete upload → compress → download flow (Python)
pip install requests
"""
import requests, time, os
API_KEY = os.environ["VIDING_API_KEY"]
BASE = "https://api.viding.ai"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# --- Option A: Single-file upload (under 100 MB) ---
def compress_simple(filepath, target_ratio=0.3):
with open(filepath, "rb") as f:
resp = requests.post(
f"{BASE}/api/compress",
headers=HEADERS,
files={"video": f},
data={"target_ratio": target_ratio},
)
resp.raise_for_status()
return resp.json()
# --- Option B: Chunked upload for large files ---
def compress_large(filepath, preset="balanced"):
file_size = os.path.getsize(filepath)
filename = os.path.basename(filepath)
# Step 1: Initialize upload
init = requests.post(f"{BASE}/api/v1/uploads/init", headers=HEADERS, json={
"filename": filename,
"mime_type": "video/mp4",
"file_size": file_size,
}).json()
upload_id = init["upload_id"]
chunk_size = init["chunk_size"]
chunk_urls = init["chunk_urls"]
# Step 2: Upload chunks
with open(filepath, "rb") as f:
for i, url in enumerate(chunk_urls):
chunk = f.read(chunk_size)
requests.put(url, data=chunk,
headers={"Content-Type": "application/octet-stream"})
print(f" Uploaded chunk {i+1}/{len(chunk_urls)}")
# Step 3: Complete upload
complete = requests.post(
f"{BASE}/api/v1/uploads/{upload_id}/complete",
headers=HEADERS,
).json()
# Step 4: Create compression job
job = requests.post(f"{BASE}/api/v1/jobs", headers=HEADERS, json={
"file_id": complete["file_id"],
"preset": preset,
}).json()
job_id = job["id"]
print(f"Job created: {job_id}")
# Step 5: Poll until complete
while True:
status = requests.get(
f"{BASE}/api/v1/jobs/{job_id}", headers=HEADERS
).json()
print(f" Status: {status['status']} ({status.get('progress', 0)}%)")
if status["status"] == "complete":
break
if status["status"] == "failed":
raise Exception(f"Job failed: {status}")
time.sleep(5)
# Step 6: Download result
dl = requests.get(
f"{BASE}/api/v1/jobs/{job_id}/download", headers=HEADERS
).json()
output_path = f"compressed_{filename}"
with requests.get(dl["download_url"], stream=True) as r:
with open(output_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Downloaded: {output_path} ({dl['file_size']} bytes)")
return dl
# Usage
compress_large("interview.mp4", preset="balanced")
/**
* VIDing.AI — Complete upload → compress → download flow (Node.js)
* npm install node-fetch
*/
import fs from "fs";
import fetch from "node-fetch";
import path from "path";
const API_KEY = process.env.VIDING_API_KEY;
const BASE = "https://api.viding.ai";
const HEADERS = { "Authorization": `Bearer ${API_KEY}` };
async function compressVideo(filepath, preset = "balanced") {
const fileSize = fs.statSync(filepath).size;
const filename = path.basename(filepath);
// Step 1: Initialize upload
const init = await fetch(`${BASE}/api/v1/uploads/init`, {
method: "POST",
headers: { ...HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({
filename, mime_type: "video/mp4", file_size: fileSize,
}),
}).then(r => r.json());
const { upload_id, chunk_size, chunk_urls } = init;
// Step 2: Upload chunks
const fd = fs.openSync(filepath, "r");
for (let i = 0; i < chunk_urls.length; i++) {
const buf = Buffer.alloc(Math.min(chunk_size, fileSize - i * chunk_size));
fs.readSync(fd, buf, 0, buf.length, i * chunk_size);
await fetch(chunk_urls[i], {
method: "PUT", body: buf,
headers: { "Content-Type": "application/octet-stream" },
});
console.log(` Uploaded chunk ${i + 1}/${chunk_urls.length}`);
}
fs.closeSync(fd);
// Step 3: Complete upload
const complete = await fetch(
`${BASE}/api/v1/uploads/${upload_id}/complete`,
{ method: "POST", headers: HEADERS }
).then(r => r.json());
// Step 4: Create compression job
const job = await fetch(`${BASE}/api/v1/jobs`, {
method: "POST",
headers: { ...HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({ file_id: complete.file_id, preset }),
}).then(r => r.json());
console.log(`Job created: ${job.id}`);
// Step 5: Poll until complete
while (true) {
const status = await fetch(
`${BASE}/api/v1/jobs/${job.id}`, { headers: HEADERS }
).then(r => r.json());
console.log(` Status: ${status.status} (${status.progress || 0}%)`);
if (status.status === "complete") break;
if (status.status === "failed") throw new Error(`Job failed: ${JSON.stringify(status)}`);
await new Promise(r => setTimeout(r, 5000));
}
// Step 6: Download result
const dl = await fetch(
`${BASE}/api/v1/jobs/${job.id}/download`, { headers: HEADERS }
).then(r => r.json());
const res = await fetch(dl.download_url);
const dest = fs.createWriteStream(`compressed_${filename}`);
await new Promise((resolve, reject) => {
res.body.pipe(dest);
dest.on("finish", resolve);
dest.on("error", reject);
});
console.log(`Downloaded: compressed_${filename} (${dl.file_size} bytes)`);
}
compressVideo("interview.mp4");
The full OpenAPI 3.0 spec is available as machine-readable JSON for code generation and API client tools:
New to VIDing.AI? Start with the Quick Start Guide to compress your first video in under 5 minutes.