Developer
API Documentation
Compress and convert media files programmatically. Upload a file, get a job ID, poll for status, and download the result.
Quick Start
# 1. Compress a video
curl -X POST https://your-domain.com/api/v1/compress \
-H "Authorization: Bearer kvrt_your_api_key" \
-F "file=@video.mp4" \
-F "codec=h264" \
-F "quality=23"
# Response: { "job_id": "j_abc123", "status": "pending", "status_url": "/api/v1/status/j_abc123" }
# 2. Check status
curl https://your-domain.com/api/v1/status/j_abc123 \
-H "Authorization: Bearer kvrt_your_api_key"
# 3. Download when complete
curl -O https://your-domain.com/api/v1/download/j_abc123 \
-H "Authorization: Bearer kvrt_your_api_key"Authentication
All API requests require an API key in the Authorization header. Create your API key in the dashboard. API access requires a Pro or Team plan.
Authorization: Bearer kvrt_your_api_key_herekvrt_ followed by 32 alphanumeric characters. Keys start with kvrt_ prefix.Endpoints
/api/v1/compressUpload and compress a video file. Returns a job ID for tracking.
Request (multipart/form-data)
| Field | Type | Description |
|---|---|---|
file | binary | Video file (required, max 2GB) |
codec | string | h264, h265, av1 (default: h264) |
quality | number | CRF value 18-51 (default: 23) |
method | string | crf, bitrate, size (default: crf) |
resolution | string | e.g. 1920x1080, 1280x720 |
webhook_url | string | URL to POST when job completes |
Response (202 Accepted)
{
"success": true,
"job_id": "j_abc123def456",
"status": "pending",
"status_url": "/api/v1/status/j_abc123def456",
"credits_charged": 5
}/api/v1/status/:jobIdCheck the status of a processing job.
Response (200)
{
"job_id": "j_abc123def456",
"status": "completed",
"progress": 100,
"input": {
"filename": "video.mp4",
"size_bytes": 104857600
},
"output": {
"filename": "video-compressed.mp4",
"size_bytes": 31457280,
"compression_ratio": 70,
"download_url": "/api/v1/download/j_abc123def456",
"expires_at": "2026-04-13T12:00:00Z"
}
}pending | processing | completed | failed/api/v1/download/:jobIdDownload the processed file. Returns the file binary.
Content-Type header. Download links expire 24 hours after job completion./api/v1/usageGet your current plan and credit balance.
Response (200)
{
"plan": "pro",
"credits": {
"balance": 420,
"total_purchased": 500
}
}/api/v1/convertConvert an image between formats using server-side Sharp.
Request (multipart/form-data)
| Field | Type | Description |
|---|---|---|
file | binary | Image file (required, max 50MB) |
format | string | Output format: jpg, png, webp, avif, gif, tiff |
quality | number | 1-100 (default: 80) |
width | number | Resize width in pixels |
height | number | Resize height in pixels |
X-Original-Size, X-Output-Size, and X-Output-Format.Rate Limits
| Plan | Requests/min | Max file size |
|---|---|---|
| Pro | 60 | 2GB |
| Team | 60 | 2GB |
When rate limited you'll receive a 429 Too Many Requests response with a Retry-After header.
Credits
API usage is billed per-job based on input file size. Credits are deducted when a job is submitted.
| Pack | Credits | Price |
|---|---|---|
| Starter | 1,000 | $5 |
| Growth | 5,000 | $20 |
| Scale | 20,000 | $60 |
Pro plans include 500 credits. Team plans include 2,000 credits. Purchase additional credits from the pricing page.
Webhooks
Pass a webhook_url when creating a job to receive a POST notification when the job completes or fails.
{
"event": "job.completed",
"job_id": "j_abc123def456",
"status": "completed",
"output": {
"download_url": "/api/v1/download/j_abc123def456",
"size_bytes": 31457280,
"expires_at": "2026-04-13T12:00:00Z"
},
"timestamp": "2026-04-12T12:00:00Z"
}{
"event": "job.failed",
"job_id": "j_abc123def456",
"status": "failed",
"error": "FFmpeg exited with code 1",
"timestamp": "2026-04-12T12:00:00Z"
}Error Codes
| Code | Meaning |
|---|---|
400 | Bad request (missing fields) |
401 | Invalid or missing API key |
404 | Job not found |
410 | Download link expired (24h) |
413 | File too large (max 2GB) |
429 | Rate limit exceeded |
Code Examples
Node.js
const fs = require("fs");
const FormData = require("form-data");
const form = new FormData();
form.append("file", fs.createReadStream("input.mp4"));
form.append("codec", "h264");
form.append("quality", "23");
const res = await fetch("https://your-domain.com/api/v1/compress", {
method: "POST",
headers: { Authorization: "Bearer kvrt_your_key" },
body: form,
});
const { job_id } = await res.json();
// Poll for completion
let status = "pending";
while (status === "pending" || status === "processing") {
await new Promise((r) => setTimeout(r, 3000));
const check = await fetch(`https://your-domain.com/api/v1/status/${job_id}`, {
headers: { Authorization: "Bearer kvrt_your_key" },
});
const data = await check.json();
status = data.status;
console.log(`Progress: ${data.progress}%`);
}
console.log("Done! Download from:", `/api/v1/download/${job_id}`);Python
import requests
import time
# Upload and compress
with open("input.mp4", "rb") as f:
res = requests.post(
"https://your-domain.com/api/v1/compress",
headers={"Authorization": "Bearer kvrt_your_key"},
files={"file": f},
data={"codec": "h264", "quality": "23"},
)
job_id = res.json()["job_id"]
# Poll for completion
while True:
check = requests.get(
f"https://your-domain.com/api/v1/status/{job_id}",
headers={"Authorization": "Bearer kvrt_your_key"},
)
data = check.json()
print(f"Status: {data['status']} ({data['progress']}%)")
if data["status"] in ("completed", "failed"):
break
time.sleep(3)
# Download result
if data["status"] == "completed":
dl = requests.get(
f"https://your-domain.com/api/v1/download/{job_id}",
headers={"Authorization": "Bearer kvrt_your_key"},
)
with open("output.mp4", "wb") as f:
f.write(dl.content)