Remove watermarks with one API call
Send an image, get a clean one back — visible AI logos and hidden tags removed. Not a developer? Use the free web tool.
Easy to integrate
One endpoint, no SDK required. Here's what to know:
Source image
Upload the image as multipart form-data (PNG, JPG or WebP, up to 25 MB).
Result
The cleaned image bytes are returned directly, plus an X-Unmarkr-Removed header listing what was removed.
Resolution
standard keeps your original resolution with no recompression. full rebuilds the image and returns it at high resolution.
What it removes
Visible logos (like Gemini’s sparkle), hidden AI tags, and Google’s invisible SynthID watermark.
Get started
- 1
Get your API key
Create a key in your dashboard. API calls spend credits — subscribe (Basic/Pro/Creator) or buy a one-time credit pack.
- 2
Use a code sample
Copy one of the samples below and drop in your key.
- 3
Read the reference
Check the parameters, response and limits in the reference section.
Sample code
Send an image file (replace unmarkr_live_xxx with your key):
curl -X POST https://unmarkr.io/api/remove \
-H "Authorization: Bearer unmarkr_live_xxx" \
-F "image=@photo.png" \
-F "mode=standard" \
-o clean.pngOutput & resolution
The cleaned image is returned in the same format you sent. Resolution depends on your access:
| Tier | Format | Max resolution |
|---|---|---|
| Free web tool | PNG / JPG / WebP | Preview (~0.25 MP) |
| API / HD (credits) | PNG / JPG / WebP | Original (standard) · high-res (full) |
PNG keeps transparency and is lossless; JPG/WebP are smaller. Output format matches the input.
API reference
One endpoint cleans an image. Send it as multipart form-data, get the cleaned bytes back.
Endpoint
POST https://unmarkr.io/api/removeAuthentication
Send your secret key as a bearer token. Create one in your dashboard and keep it server-side.
Authorization: Bearer unmarkr_live_xxxRequest parameters
Body is multipart/form-data.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| image | file | Required | — | The image to clean — PNG, JPG or WebP. |
| mode | string | Optional | standard | Which removal to run — standard or full. See Modes below. |
Modes
Pick standard (the default) or full:
| mode | Removes | Credits | Returns |
|---|---|---|---|
| standard | Visible logos & badges + hidden AI tags (C2PA, “Made with AI”, EXIF/XMP). | 1 | Your cleaned image |
| full | Everything in standard, plus the invisible SynthID watermark. | 8 | A job to check back on |
standard returns your cleaned image straight away. full also removes Google’s invisible SynthID watermark — that takes a little longer, so instead of the image it returns a job you check back on.
Full removal (checking back)
Send mode=full and you get a 202 with a job to follow:
{ "jobId": "…", "status": "processing",
"poll": "/api/jobs/…", "result": "/api/jobs/…/result" }- 1. Poll
GET /api/jobs/<id>with the same API key untilstatusisdone(usually well under a minute). - 2. Download the cleaned image from
GET /api/jobs/<id>/result. - 3. If a full removal fails, your credits are refunded automatically.
Response
For standard, 200 OK returns the cleaned image bytes directly (same format you sent, no JSON wrapper). For full, you get 202 with the job JSON above. On any error, the body is a JSON object — see Errors & retries.
Response headers
| Header | Description |
|---|---|
| Content-Type | Format of the cleaned image, matching your input (e.g. image/png). |
| X-Unmarkr-Removed | Comma-separated list of what was removed, e.g. Gemini sparkle, metadata:c2pa. |
Credits
standard costs 1 credit per image; full costs 4 credits. Subscription credits refill monthly; purchased credits never expire. A 402means you're out of credits.
Error format
Errors return a JSON body with a human-readable error message (and sometimes a machine-readable code):
{
"error": "Out of credits. Buy a credit pack or subscribe to continue.",
"code": "no_credits"
}Rate limit
Requests are limited per API key (default 60 / minute). Exceeding it returns 429 Too Many Requests with a Retry-After header (seconds to wait). Need higher limits? Contact us.
Errors & retries
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key. |
| 402 | Out of credits — buy credits or subscribe. |
| 413 | Image exceeds the 25 MB limit. |
| 429 | Rate limit exceeded — wait Retry-After seconds. |
| 503 | Full removal temporarily unavailable — try standard. |
| 5xx | Temporary engine error — retry with backoff. |
Exponential backoff
For 5xx errors, retry with increasing delays (don't retry 429 immediately — honour Retry-After):
import time, requests
def remove(path, key, attempts=4):
for i in range(attempts):
r = requests.post(
"https://unmarkr.io/api/remove",
headers={"Authorization": f"Bearer {key}"},
files={"image": open(path, "rb")},
)
if r.status_code < 500:
return r
time.sleep(2 ** i) # 1s, 2s, 4s, 8s
r.raise_for_status()