DUCKDUCKGOOSE · VIDEO ANALYSIS API
One deepfake verdict per second of video.
Upload a video; we run every second through our selfie deepfake detector and return the timeline below as JSON. You decide what the timeline means for the video.
0:00one cell = one second0:27
accept
inspect
reject
1AUTHENTICATE
Every request carries your existing DuckDuckGoose API key — note the
scheme is Token, not Bearer:
Authorization: Token <your_api_key>
Base URL: https://dsta-video.duckduckgoose.ai
2UPLOAD A VIDEO
# multipart/form-data, field name: file
curl -X POST https://dsta-video.duckduckgoose.ai/videos \
-H "Authorization: Token <your_api_key>" \
-F file=@video.mp4
# 202 Accepted
{"video_id": "b07f60c03a7347ee9b3187161ec44eaf", "status": "queued"}
mp4 · webm · avi · mov · mkv max 5 GB first 15 min analysed 1 frame analysed per second
3POLL FOR THE RESULT
Ask every few seconds until status is terminal. A one-minute
video typically finishes in under a minute.
curl https://dsta-video.duckduckgoose.ai/videos/<video_id> \
-H "Authorization: Token <your_api_key>"
queued → analyzing → completed · failed (see error) · interrupted (re-upload)
{
"video_id": "b07f60c0…",
"file_name": "video.mp4",
"status": "completed",
"total_frames": 60,
"frames": [
{"second": 0, "status": "completed", "output": "accept",
"perc_fake": 11.53, "num_faces": 1, "error": null},
{"second": 1, "status": "completed", "output": "accept",
"perc_fake": 10.39, "num_faces": 1, "error": null}
],
"error": null
}
4READ THE TIMELINE
| Field | Meaning |
|---|---|
second | Timestamp in the video this frame represents |
output | accept · inspect · reject — our verdict for this frame |
perc_fake | 0–100, higher = more likely manipulated |
num_faces | Faces detected in the frame |
status | "failed" means no face was visible that second — no signal, not an error |
PYTHON
The whole flow in one script (pip install requests):
# python analyze_video.py video.mp4
import sys, time, requests
BASE = "https://dsta-video.duckduckgoose.ai"
HEADERS = {"Authorization": "Token <your_api_key>"}
def analyze(path):
with open(path, "rb") as f:
r = requests.post(f"{BASE}/videos", headers=HEADERS, files={"file": f})
r.raise_for_status()
video_id = r.json()["video_id"]
while True:
r = requests.get(f"{BASE}/videos/{video_id}", headers=HEADERS)
r.raise_for_status()
job = r.json()
if job["status"] in ("completed", "failed", "interrupted"):
return job
time.sleep(5)
job = analyze(sys.argv[1])
print(job["status"], job.get("error") or "")
for frame in job["frames"]:
print(f'{frame["second"]:>4}s {frame["output"] or "no face":8} '
f'perc_fake={frame["perc_fake"]}')
ERRORS
| Code | Meaning |
|---|---|
401 | Missing or invalid Authorization: Token <key> header |
404 | Unknown video_id, or it belongs to another key |
413 | Video larger than 5 GB |
415 | Unsupported file extension |
429 | Too many requests — retry later |
507 | Server temporarily out of storage — retry later |