Phone videos carry the recording location, the phone model and software version, the time, and sometimes hidden data tracks. On one iPhone 13 Pro clip we tested, those tracks held face detection frame by frame. AI video tools add Content Credentials naming the tool. If videos pass through your app (user uploads, a media library, a delivery pipeline), you usually want all of that gone before the file moves on.
This post shows two ways to do it from Python: running ffmpeg yourself, and calling our metadata removal API. Both remove the metadata without re-encoding, so the video and sound are untouched. All code here was run against real files.
How to remove metadata from a video in Python with ffmpeg
ffmpeg is free and fast. The command matters more than the language:
import subprocess
def strip_video(src, dst):
subprocess.run([
"ffmpeg", "-nostdin", "-v", "error", "-y", "-i", src,
"-map", "0:V", "-map", "0:a?", # picture and sound only
"-map_metadata", "-1", "-map_metadata:s", "-1", "-map_chapters", "-1",
"-fflags", "+bitexact", "-flags:v", "+bitexact", "-flags:a", "+bitexact",
"-c", "copy", dst,
], check=True)
Passed as a list, the arguments need no shell quoting. Typed into a Mac terminal, 0:a? needs quotes, or zsh refuses the command.
What each part is for:
-map 0:V -map 0:a?keeps the picture and sound only. The common shortcut-map 0also copies iPhone's hidden data tracks, face detection included.-map_metadata -1 -map_metadata:s -1 -map_chapters -1removes the file's details, each track's details, and chapter titles.- The
bitexactflags stop ffmpeg writing its own version number into the file. -c copycopies instead of re-encoding: no quality loss, and seconds per file.
On our iPhone 13 Pro test video, this removed the GPS, phone model, software and dates, and dropped the five data tracks. The video and audio were byte-identical.
What it won't do: tell you whether it worked. ffmpeg exits successfully whether or not something identifying remains. It can't write AVI back cleanly with AAC audio, and it knows nothing about Content Credentials. It happens to drop them because it rebuilds the container, but nothing checks. If files leave your system automatically, you also need a check afterwards (for example exiftool -gps:all file.mp4 should print nothing), and someone to keep ffmpeg installed and updated on every machine that runs this.
Is there an API to remove metadata from videos?
Our API does the same removal, then re-reads the output and checks that the video and audio are identical to the original before calling it clean. One call per file:
curl -X POST https://api.aimetadatacleaner.com/v1/clean \
-H "Authorization: Bearer $AMC_API_KEY" \
-F [email protected]
The response lists what was removed, and gives a one-time link to the cleaned file:
{
"status": "clean",
"file_type": "MOV",
"kind": "video",
"removed": ["Dates and times", "Device make and model", "GPS location",
"Software / OS version", "Text fields (titles, captions, comments)",
"Timed metadata tracks (e.g. face detection, scene data)"],
"download_url": "https://api.aimetadatacleaner.com/v1/files/...",
"expires_in": 900
}
That's the real report for the iPhone 13 Pro video above.
How to clean a folder of large videos through an API in Python
Videos are big, and one detail matters: requests.post(..., files=...) builds the whole upload in memory. Uploading a 74MB video that way, our script's memory peaked at 181MB. For a 2GB video you'd need several gigabytes. MultipartEncoder from requests-toolbelt streams the file instead: the same upload peaked at 56MB, however large the file.
import os, time, requests
from concurrent.futures import ThreadPoolExecutor
from requests_toolbelt import MultipartEncoder
API = "https://api.aimetadatacleaner.com"
KEY = os.environ["AMC_API_KEY"]
def clean_video(path, out_path):
for attempt in range(5):
with open(path, "rb") as f: # streamed, never loaded into memory
body = MultipartEncoder({"file": (os.path.basename(path), f, "application/octet-stream")})
r = requests.post(f"{API}/v1/clean", data=body,
headers={"Authorization": f"Bearer {KEY}",
"Content-Type": body.content_type})
if r.status_code not in (429, 503): # 429/503: rate-limited or busy, try again
break
time.sleep(int(r.headers.get("Retry-After", 10)))
report = r.json()
if not r.ok:
raise RuntimeError(f"{path}: {report['error']['message']}")
with requests.get(report["download_url"], stream=True) as dl, open(out_path, "wb") as out:
for chunk in dl.iter_content(1024 * 1024):
out.write(chunk)
return report
def clean_one(path):
stem, ext = os.path.splitext(path)
try:
report = clean_video(path, f"{stem}-clean{ext}")
return f"{report['status']:<10} {path}: {', '.join(report['removed'])}"
except RuntimeError as e:
return f"failed {e}"
folder = "batch"
videos = [os.path.join(folder, n) for n in sorted(os.listdir(folder))
if n.lower().endswith((".mp4", ".mov", ".m4v", ".3gp", ".mkv", ".webm"))]
with ThreadPoolExecutor(max_workers=3) as pool:
for line in pool.map(clean_one, videos):
print(line)
Install the two libraries with pip install requests requests-toolbelt. It cleans three videos at a time, retries when the server is busy or you pass 60 requests a minute, and streams the download too. We ran it on a folder with an iPhone MOV, an iPhone MP4, two MP4s with Content Credentials, an MKV and a WebM: all six came back clean. An AVI in the same folder was skipped by the extension filter. Sent on its own, it's refused with "AVI isn't supported yet. Convert it to MP4 first."
ffmpeg or an API: which should you use?
| ffmpeg in your code | Our API | |
|---|---|---|
| Cost | Free | Business plan, $49/month for 10,000 files |
| Re-encoding | None | None |
| Checks the result | No, you add it | Yes, every file |
| Content Credentials, hidden tracks | Dropped by the right command | Removed and reported |
| Also cleans PDF, RAW, images | No | Yes, same call |
| Runs on | Your servers | Ours (Germany); files deleted after cleaning |
ffmpeg is the right choice if you only handle video, can keep the command right, and can add your own check. The API is simpler when files of several kinds pass through, or when you need a record of what was removed from each one.
What are the API's limits?
- Files up to 2GB, 10,000 files and 1TB a month, 60 requests a minute (Business plan).
- Formats: MP4, MOV, M4V, 3GP, MKV and WebM. AVI isn't supported yet.
- The cleaned file's link works once and expires after 15 minutes; files are deleted after cleaning.
Full reference: API docs. The same cleaning without code is on our video page, and in Claude, Cursor or VS Code through the MCP add-on.
Frequently asked questions
Does removing video metadata reduce quality?
Not when the video is copied instead of re-encoded. Both methods here copy the video and audio as they are; we checked that the output is byte-identical.
How do I upload large videos to an API from Python without running out of memory?
Stream the upload. requests with files= holds the whole body in memory; requests_toolbelt.MultipartEncoder reads the file as it sends. Stream the download too, with stream=True and iter_content().
What metadata is in a phone video?
Usually the GPS position, phone model, software version and recording time. iPhones can add hidden data tracks, such as face detection. Videos from AI tools can carry Content Credentials naming the tool.
How do I remove metadata from many videos at once?
Loop over the folder with the ffmpeg function or the API script above. Without code, our video page cleans 10 videos per batch on Pro and 50 on Business. See our guide to bulk removing metadata from videos.
