Manual Video Remover
Manual Video Remover
Submit a video URL and a mask image to remove watermarks from specific regions. The mask must exactly match the video resolution. Charged per second of video processed.
Before you begin
Obtain your API key from API Key Settings before making requests.
Overview
| Item | Value |
|---|---|
| Create job | POST /api/unwatermark/api/v1/ai-manual-remove-video/create-job |
| Query job | GET /api/unwatermark/api/v1/ai-manual-remove-video/get-job/{job_id} |
| Auth | ZF-API-KEY header |
| Credits | 1 per second of video |
| Result availability | 24 hours |
Video constraints: size ≤ 100 MB · resolution ≤ 1080p · duration ≤ 5 min · frame rate ≤ 30 fps
Create Job
Request
Headers
| Name | Required | Value |
|---|---|---|
ZF-API-KEY | Yes | Your API key |
Content-Type | Yes | multipart/form-data |
Body (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
original_video_url | string | Yes | HTTP/HTTPS URL of the source video |
mask_url | string | Yes | Mask image URL — black background, white areas mark watermark regions. Must exactly match the video resolution. |
Example
<!DOCTYPE html>
<html lang="en">
<body>
<label>Video URL: <input type="url" id="videoUrl" placeholder="https://cdn.example.com/video.mp4" size="50" /></label><br/>
<label>Mask URL: <input type="url" id="maskUrl" placeholder="https://cdn.example.com/mask.png" size="50" /></label><br/>
<button onclick="send()">Submit Job</button>
<pre id="out"></pre>
<script>
async function send() {
const videoUrl = document.getElementById('videoUrl').value;
const maskUrl = document.getElementById('maskUrl').value;
if (!videoUrl || !maskUrl) return alert('Please fill in both URLs.');
const form = new FormData();
form.append('original_video_url', videoUrl);
form.append('mask_url', maskUrl);
const res = await fetch(
'https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/create-job',
{ method: 'POST', headers: { 'ZF-API-KEY': 'YOUR_API_KEY' }, body: form }
);
document.getElementById('out').textContent =
JSON.stringify(await res.json(), null, 2);
}
</script>
</body>
</html>import requests
url = "https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/create-job"
headers = {"ZF-API-KEY": "YOUR_API_KEY"}
data = {
"original_video_url": "https://cdn.example.com/video.mp4",
"mask_url": "https://cdn.example.com/mask.png",
}
response = requests.post(url, headers=headers, data=data)
print(response.json())import okhttp3.*;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("original_video_url", "https://cdn.example.com/video.mp4")
.addFormDataPart("mask_url", "https://cdn.example.com/mask.png")
.build();
Request request = new Request.Builder()
.url("https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/create-job")
.post(body)
.addHeader("ZF-API-KEY", "YOUR_API_KEY")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
}
}<?php
$url = "https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/create-job";
$apiKey = "YOUR_API_KEY";
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["ZF-API-KEY: {$apiKey}"],
CURLOPT_POSTFIELDS => [
'original_video_url' => 'https://cdn.example.com/video.mp4',
'mask_url' => 'https://cdn.example.com/mask.png',
],
]);
$data = json_decode(curl_exec($curl), true);
curl_close($curl);
print_r($data);
Response
Success (100000)
{
"code": 100000,
"message": {
"en": "Request Success",
"zh": "提交任务成功"
},
"result": {
"job_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"mask_url": "https://xxxxxx.jpg",
"origin_url": "https://xxxxxx.mp4",
"deduct_credits": 10
}
}Error (400000)
{
"code": 400000,
"message": {
"en": "Request Failure",
"zh": "请求失败"
},
"result": {
"job_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"deduct_credits": 0
}
}Query Job
GET /api/unwatermark/api/v1/ai-manual-remove-video/get-job/{job_id}
Polling required
This endpoint must be polled until the job completes. Keep polling every 5 seconds until code returns 100000 (completed) or 300011 (failed). Do not poll more frequently to avoid rate limiting.
code | Meaning | Action |
|---|---|---|
300010 | Processing | Wait 5 s and retry |
100000 | Completed | Read result.output_url |
300011 | Failed | Stop polling |
Request
Headers
| Name | Required | Value |
|---|---|---|
ZF-API-KEY | Yes | Your API key |
Path Parameters
| Parameter | Type | Description |
|---|---|---|
job_id | string | ID returned by the create-job API |
Example
<!DOCTYPE html>
<html lang="en">
<body>
<label>Job ID: <input type="text" id="jobId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" size="40" /></label>
<button onclick="startPolling()">Poll Job</button>
<p id="status"></p>
<pre id="out"></pre>
<script>
const POLL_INTERVAL = 5000; // 5 seconds
let timer = null;
async function poll(jobId) {
const res = await fetch(
`https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/get-job/${jobId}`,
{ method: 'GET', headers: { 'ZF-API-KEY': 'YOUR_API_KEY' } }
);
const data = await res.json();
document.getElementById('out').textContent = JSON.stringify(data, null, 2);
if (data.code === 300010) {
document.getElementById('status').textContent = 'Processing… next check in 5s';
timer = setTimeout(() => poll(jobId), POLL_INTERVAL);
} else {
clearTimeout(timer);
document.getElementById('status').textContent =
data.code === 100000 ? '✅ Completed' : '❌ Failed';
}
}
function startPolling() {
const jobId = document.getElementById('jobId').value.trim();
if (!jobId) return alert('Please enter a job ID.');
clearTimeout(timer);
document.getElementById('status').textContent = 'Polling…';
poll(jobId);
}
</script>
</body>
</html>import time
import requests
job_id = "YOUR_JOB_ID"
url = f"https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/get-job/{job_id}"
headers = {"ZF-API-KEY": "YOUR_API_KEY"}
INTERVAL = 5 # seconds
while True:
response = requests.get(url, headers=headers)
data = response.json()
print(data)
if data.get("code") == 300010:
print("Processing… retrying in 5s")
time.sleep(INTERVAL)
else:
if data.get("code") == 100000:
print("✅ Completed:", data["result"]["output_url"])
else:
print("❌ Failed:", data.get("message"))
breakimport okhttp3.*;
import org.json.JSONObject;
import java.io.IOException;
public class Main {
static final int POLL_INTERVAL_MS = 5000;
public static void main(String[] args) throws IOException, InterruptedException {
String jobId = "YOUR_JOB_ID";
String url = "https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/get-job/" + jobId;
OkHttpClient client = new OkHttpClient();
while (true) {
Request request = new Request.Builder()
.url(url)
.get()
.addHeader("ZF-API-KEY", "YOUR_API_KEY")
.build();
try (Response response = client.newCall(request).execute()) {
String body = response.body().string();
System.out.println(body);
JSONObject json = new JSONObject(body);
int code = json.getInt("code");
if (code == 300010) {
System.out.println("Processing… retrying in 5s");
Thread.sleep(POLL_INTERVAL_MS);
} else {
System.out.println(code == 100000 ? "✅ Completed" : "❌ Failed");
break;
}
}
}
}
}<?php
$jobId = "YOUR_JOB_ID";
$url = "https://api.unwatermark.ai/api/unwatermark/api/v1/ai-manual-remove-video/get-job/{$jobId}";
$apiKey = "YOUR_API_KEY";
$interval = 5; // seconds
while (true) {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPGET => true,
CURLOPT_HTTPHEADER => ["ZF-API-KEY: {$apiKey}"],
]);
$data = json_decode(curl_exec($curl), true);
curl_close($curl);
echo json_encode($data, JSON_PRETTY_PRINT) . "\n";
if ($data['code'] === 300010) {
echo "Processing… retrying in {$interval}s\n";
sleep($interval);
} else {
echo $data['code'] === 100000 ? "✅ Completed\n" : "❌ Failed\n";
break;
}
}Response
Processing (300010)
{
"code": 300010,
"message": {
"en": "Video generation in progress.",
"zh": "视频生成中。",
"id": "Penciptaan video sedang berlangsung."
},
"result": {
"input_url": "https://xxxxxx.mp4"
}
}Completed (100000)
{
"code": 100000,
"message": {
"en": "Video generated success.",
"zh": "视频处理成功。",
"id": "Generasi video berhasil"
},
"result": {
"input_url": "https://xxxxxx.mp4",
"output_url": ["https://xxxxxx.mp4"]
}
}Error (300011)
{
"code": 300011,
"message": {
"en": "Video generated failed.",
"zh": "视频生成失败。",
"id": "Generasi video gagal."
},
"result": {
"input_url": "https://xxxxxx.mp4"
}
}Status Codes
| Code | Meaning |
|---|---|
100000 | Success |
300010 | Video processing |
300011 | Video processing failed |
400000 | Request failed |
