Remove watermarks from images, PDFs, and videos via REST API. Authenticate with an API key; each request spends from your subscription or pay-as-you-go credit balance.
Base URL
https://api.erasewatermark.io
Protocol
HTTPS only
Format
multipart/form-data
Pass your API key as a Bearer token in the Authorization header of every request. API keys start with wm_. You can create and revoke keys in the API Keys dashboard.
Authorization: Bearer wm_your_api_key_here
All endpoints are under https://api.erasewatermark.io/api/v1/watermark.
/api/v1/watermark/imageasyncRemove watermark from an image. AI auto-detects the watermark. Processing is asynchronous — poll task status.
Cost: 1 creditRequest body (multipart/form-data)
filerequired | file | Image file. Supported: JPG, PNG, WEBP, AVIF. Max 10 MB, up to 36 megapixels (e.g. 6000×6000). |
curl -X POST https://api.erasewatermark.io/api/v1/watermark/image \ -H "Authorization: Bearer wm_your_key" \ -F "[email protected]"
/api/v1/watermark/imageasyncRemove watermark guided by a brush mask you draw over the watermark area. Subscriber / API-key only.
Cost: 1 creditRequest body (multipart/form-data)
filerequired | file | Image file (same formats as auto). |
maskrequired | file | PNG mask, any size (it is resized to the image). Paint the watermark area WHITE (255); leave everything else BLACK (0). The white region is what gets erased. |
curl -X POST https://api.erasewatermark.io/api/v1/watermark/image \ -H "Authorization: Bearer wm_your_key" \ -F "[email protected]" \ -F "[email protected]"
/api/v1/watermark/maskReturn ONLY the auto-detected watermark mask (base64 PNG) for an image, without removing anything. Synchronous — the mask is in the response, no task to poll. Subscriber / API-key only.
Cost: freeRequest body (multipart/form-data)
filerequired | file | Image file (JPG, PNG, WEBP, AVIF). Max 10 MB. |
Response (application/json)
mask_b64 | string | Base64-encoded PNG mask; white marks the detected watermark area. |
fmt | string | Always "png". |
coverage | number | Fraction of the image covered by the mask (0–1), or null. |
curl -X POST https://api.erasewatermark.io/api/v1/watermark/mask \ -H "Authorization: Bearer wm_your_key" \ -F "[email protected]"
/api/v1/watermark/pdfasyncRemove watermarks from a PDF. Processing is asynchronous — poll task status.
Cost: 1 credit per pageRequest body
filerequired | file | PDF file. Max 50 MB. |
first_page_only | boolean | Optional (default false). true = process only the first page (1 credit); false = process the whole document (1 credit per page). |
/api/v1/watermark/videoasyncRemove watermarks from a video. Processing is asynchronous.
Cost: 1 creditRequest body
filerequired | file | Video file. Supported: MP4, MOV, WEBM, AVI, MKV. Max 500 MB. |
All three endpoints return a task_id. Poll this endpoint until status is completed or failed.
/api/v1/watermark/tasks/{task_id}Poll processing status. Returns progress and download URL on completion.
Cost: freeResponse fields
status | string | "pending" | "processing" | "completed" | "failed" |
progress | number | 0–100 percentage (video only). |
download_url | string | Present when status = completed. Relative path — prepend base URL. |
file_id | string | Stable ID for the processed file. |
error | string | Error message when status = failed. |
curl https://api.erasewatermark.io/api/v1/watermark/tasks/abc123 \ -H "Authorization: Bearer wm_your_key"
/api/v1/watermark/my-recent-tasksList your recent tasks (active jobs + recent history). Returns { tasks: [ { task_id, filename, file_type, status, is_active, created_at, download_available } ] }.
Cost: free/api/v1/watermark/download/{file_id}Stream the processed file. Add ?inline=true to serve inline instead of as attachment.
Cost: freecurl -O -J https://api.erasewatermark.io/api/v1/watermark/download/abc123 \ -H "Authorization: Bearer wm_your_key"
| Operation | Credits consumed |
|---|---|
| Image (auto or manual) | 1 |
| Mask detection | 0 (free) |
| 1 per page | |
| Video | 1 |
| Task status poll | 0 |
| Download | 0 |
Credits are deducted when processing begins. Credits for failed jobs are returned to your balance automatically.
| HTTP status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — invalid file, unsupported format, or file too large |
| 401 | Unauthorized — invalid or revoked API key (a missing key is treated as anonymous) |
| 402 | Insufficient credits |
| 403 | Forbidden — you do not own this task, or the feature (manual brush / mask) requires a subscription |
| 404 | Task or file not found |
| 422 | Validation error — a required field (e.g. file) is missing or malformed |
| 429 | Rate limited (anonymous users only) |
| 500 | Internal server error — please retry |
| 502 | Upstream processing error (e.g. mask detection) — please retry |
All error responses include a detail field with a human-readable message.
import requests, time
API_KEY = "wm_your_api_key"
BASE = "https://api.erasewatermark.io"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# Remove watermark from an image (auto)
with open("photo.jpg", "rb") as f:
r = requests.post(f"{BASE}/api/v1/watermark/image",
headers=HEADERS, files={"file": f})
r.raise_for_status()
task_id = r.json()["task_id"]
# Poll until done
while True:
status = requests.get(f"{BASE}/api/v1/watermark/tasks/{task_id}",
headers=HEADERS).json()
if status["status"] == "completed":
break
if status["status"] == "failed":
raise Exception(status["error"])
time.sleep(2)
# Download result
dl = requests.get(f"{BASE}{status['download_url']}", headers=HEADERS)
with open("result.jpg", "wb") as f:
f.write(dl.content)
print("Done! Saved result.jpg")import fs from 'fs';
import FormData from 'form-data';
import fetch from 'node-fetch';
const API_KEY = 'wm_your_api_key';
const BASE = 'https://api.erasewatermark.io';
const headers = { Authorization: `Bearer ${API_KEY}` };
// Submit image
const form = new FormData();
form.append('file', fs.createReadStream('photo.jpg'));
const { task_id } = await fetch(`${BASE}/api/v1/watermark/image`,
{ method: 'POST', headers: { ...headers, ...form.getHeaders() }, body: form }
).then(r => r.json());
// Poll
let status;
do {
await new Promise(r => setTimeout(r, 2000));
status = await fetch(`${BASE}/api/v1/watermark/tasks/${task_id}`,
{ headers }).then(r => r.json());
} while (!['completed','failed'].includes(status.status));
if (status.status === 'failed') throw new Error(status.error);
// Download
const buf = await fetch(`${BASE}${status.download_url}`, { headers })
.then(r => r.arrayBuffer());
fs.writeFileSync('result.jpg', Buffer.from(buf));
console.log('Done!');