Metadata Cleaning API
Remove metadata from images, videos, PDFs and RAW photos with one request. Every file is checked after cleaning, and we only call it clean when we can prove it.
Get a key
The API is part of the Business plan ($49 a month, or $449 a year). Subscribe and create keys on the API keys page. Keys start with amc_live_ and are shown once, so store yours somewhere safe. You can revoke a key at any time.
Send it with every request:
Authorization: Bearer amc_live_... Quick start
Upload a file to /v1/clean as a form field named file:
curl -X POST https://api.aimetadatacleaner.com/v1/clean \
-H "Authorization: Bearer $AMC_API_KEY" \
-F [email protected] You get back a report of what was found and removed, and a link to the cleaned file:
{
"status": "clean",
"file_type": "MOV",
"kind": "video",
"removed": ["Dates and times", "Device make and model", "GPS location",
"Timed metadata tracks (e.g. face detection, scene data)"],
"kept": [],
"left": {},
"notes": [],
"size_in": 73905295,
"size_out": 73221270,
"seconds": 1.3,
"download_url": "https://api.aimetadatacleaner.com/v1/files/45e1e41dddf84308a16486cc1bbb830f",
"expires_in": 900,
"usage": { "jobs_used": 12, "job_limit": 10000 }
} Download the cleaned file from download_url. No key is needed for this, so you can pass the link on. It works once and expires after 15 minutes.
curl -o holiday-clean.mov "https://api.aimetadatacleaner.com/v1/files/45e1e41d..." What the status means
| Status | Meaning |
|---|---|
clean | Nothing identifying is left, and the content is proven unchanged (video and audio data, RAW sensor data, or image pixels, depending on the file). |
unverified | We produced a file but can't prove it's clean. left and problems say why. Example: a RAW file from a camera too new for any raw decoder. |
| HTTP 422 | We couldn't clean it without risking damage, so no file is returned and the job isn't counted. |
kept lists data left in on purpose, with the reason in notes. Example: RAW files keep camera make, model and lens, because raw editors need them to show the photo correctly.
Supported files
| Kind | Formats | What's removed |
|---|---|---|
| Images | JPEG, PNG, WebP | EXIF, GPS, XMP, IPTC, C2PA Content Credentials, AI generation data (prompts, parameters) |
| Video | MP4, MOV, M4V, 3GP, MKV, WebM | GPS, device, dates, C2PA, XMP, author, and hidden per-frame data tracks. Not re-encoded, so there's no quality loss. |
| Author, software, dates, XMP, C2PA, metadata inside embedded photos, and digital signatures (optional) | ||
| RAW | CR2, CR3, NEF, ARW, RAF, RW2, DNG | GPS, serial numbers, owner, dates, shutter count, XMP. The photo data is untouched. Nikon files keep their serial number and shutter count, because Nikon uses them to decode the photo's colours. |
Not supported yet: AVI (convert to MP4 first) and password-protected PDFs.
Options
Pass as query parameters or as extra form fields.
| Option | Applies to | Effect |
|---|---|---|
mode=reencode | Images | Default. Redraws the image so none of the original file's bytes remain, and makes it harder to match with reverse image search. Changes are invisible. |
mode=strip | Images | Removes metadata only. Pixels stay byte-for-byte identical. |
format=jpeg|png|webp | Images | Output format. Default: the same as the input. |
keep_signature=true | Keep digital signatures. They still name the signer and will show as invalid, because cleaning changes the file. Default: removed. |
curl -X POST "https://api.aimetadatacleaner.com/v1/clean?mode=strip" \
-H "Authorization: Bearer $AMC_API_KEY" \
-F [email protected] Limits
| Business | |
|---|---|
| Files per month | 10,000 |
| Data per month | 1 TB uploaded |
| Largest file | 2 GB |
| Requests per minute | 60 |
Monthly limits reset on the 1st (UTC). Files that fail or aren't supported don't count. Check where you stand with:
curl https://api.aimetadatacleaner.com/v1/usage -H "Authorization: Bearer $AMC_API_KEY" Errors
Errors come back as JSON with a code you can act on:
{ "error": { "code": "job_limit", "message": "You've used all 10,000 server jobs this month. ..." } } | HTTP | Code | What to do |
|---|---|---|
| 401 | missing_key, invalid_key | Check the Authorization header, or create a new key |
| 402 | job_limit, data_limit | Monthly allowance used; it resets on the 1st |
| 413 | file_too_large | The file is over 2 GB |
| 415 | unsupported_type | See supported files above |
| 422 | could_not_clean | The file couldn't be cleaned safely; the message says why |
| 429 | rate_limited | Wait for the Retry-After seconds |
| 503 | busy | Retry after Retry-After seconds |
Python
import requests
API = "https://api.aimetadatacleaner.com"
headers = {"Authorization": "Bearer " + API_KEY}
with open("photo.jpg", "rb") as f:
r = requests.post(f"{API}/v1/clean", headers=headers, files={"file": f})
r.raise_for_status()
report = r.json()
if report["status"] == "clean":
cleaned = requests.get(report["download_url"])
open("photo-clean.jpg", "wb").write(cleaned.content)
else:
print("Not verified clean:", report["left"], report["problems"]) JavaScript (Node 18+)
import { readFile, writeFile } from 'node:fs/promises';
const form = new FormData();
form.append('file', new Blob([await readFile('report.pdf')]), 'report.pdf');
const res = await fetch('https://api.aimetadatacleaner.com/v1/clean', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.AMC_API_KEY}` },
body: form,
});
const report = await res.json();
if (!res.ok) throw new Error(report.error.message);
const file = await fetch(report.download_url);
await writeFile('report-clean.pdf', Buffer.from(await file.arrayBuffer())); Your files
Files are processed on our server in Germany and never kept. The upload is deleted as soon as cleaning finishes, and the cleaned file is deleted once you download it, or after 15 minutes if you don't. We record the file type, size and result for billing and support, never the file name or contents.
Questions or a use case the limits don't fit? Contact us.