检测视频画面、音频与元数据中的 AI 生成信号。
传递视频 URL → 创建检测 → 查询结果
https://videodetector.org/api/v1仅支持传递视频 URL,不接受直接上传视频文件或上传 ID。支持 MP4、MOV、AVI、FLV、TS,最大 400,000,000 字节、1,800 秒。URL 由你提供,应指向可公开访问的视频文件,不能是播放页、分享页或需要登录的链接。服务端读取视频信息校验大小和时长,随后直接使用原 URL 检测,不转存视频文件。
{
"input": {
"type": "url",
"url": "https://media.example.com/video.mp4"
},
"client_reference": "order-123"
}429 响应包含 Retry-After(秒)。创建接口必须携带 Idempotency-Key(1–128 位字母、数字、点、下划线、冒号或连字符)。重试必须使用相同请求内容和相同 Key;修改内容返回 409。超时后保留原 Key,已过期资源不能通过旧 Key 重建。
响应包含 id、service、status、client_reference、input.duration_seconds、billing、result、error 和时间信息。结论为 suspected_ai、not_detected 或 unknown,缺少证据时保持 unknown。上游置信度仅描述单个标签,不代表整段视频为 AI 生成的概率。检测具有不确定性,不能作为真实性证明。
billing.state 为 held、charged 或 released。held_credits 为预占金额,charged_credits 为实际扣费。仅排队中的任务可取消,已开始任务不可取消。timed_out 对 API 为最终状态,即使上游之后回传,也不会再次收费。
GET /video-detections/{id}/evidence 每页返回最多 50 条证据,包括画面时间点、语音时间范围和检测标签;按 next_cursor 继续查询。API 保存检测记录与结果,不托管 URL 对应的视频文件,也不提供原视频下载。
画面时间点及音频起止时间单位均为秒。证据保留上游标签和数值置信度,不生成虚构的综合评分。
{
"id": "DETECTION_ID",
"service": "video_ai",
"status": "succeeded",
"client_reference": "order-123",
"input": {
"duration_seconds": 61
},
"billing": {
"state": "charged",
"held_credits": 0,
"charged_credits": 2
},
"result": {
"schema_version": "1",
"verdict": "suspected_ai",
"visual": {
"verdict": "suspected_ai",
"returned_frame_count": 12,
"matched_returned_frame_count": 3
},
"audio": {
"verdict": "unknown",
"reason": "provider_result_missing"
},
"metadata": {
"ai_marker_detected": null
}
},
"error": null,
"created_at": "2026-10-10T00:00:00Z",
"started_at": "2026-10-10T00:00:01Z",
"completed_at": "2026-10-10T00:01:00Z",
"expires_at": null,
"request_id": "REQUEST_ID"
}| GET | /account |
|---|---|
| POST · GET | /video-detections |
| GET | /video-detections/{id} |
| GET | /video-detections/{id}/evidence |
| POST | /video-detections/{id}/cancel |
Python 3.10+(标准库)和 Node.js 20+(无需依赖)示例均接收视频 URL,提交任务并轮询结果;遵循账号每秒 1 个请求的限制,重试复用同一个幂等键。示例假设当前只有一个客户端使用该账号。
设置 VIDEO_API_TOKEN 后,运行 python3 video-api.py "https://media.example.com/video.mp4" 或 node video-api.mjs "https://media.example.com/video.mp4"。VIDEO_API_BASE 默认是 https://videodetector.org/api/v1。保存输出的幂等键和任务 ID;恢复时设置 VIDEO_DETECTION_KEY 并传入相同 URL。
# Python 3.10+. Standard library only. Run: python video-api.py https://media.example.com/video.mp4
# Set VIDEO_API_TOKEN; optionally set VIDEO_API_BASE (default below).
import json
import os
import sys
import time
import uuid
import urllib.error
import urllib.request
BASE = os.environ.get("VIDEO_API_BASE", "https://videodetector.org/api/v1").rstrip("/")
TOKEN = os.environ["VIDEO_API_TOKEN"]
next_request = 0.0
def request(method, path, body=None, key=None):
global next_request
headers = {"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"}
if key:
headers["Idempotency-Key"] = key
data = None if body is None else json.dumps(body).encode()
for attempt in range(6):
time.sleep(max(0, next_request - time.monotonic()))
req = urllib.request.Request(BASE + path, data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(req, timeout=120) as response:
return json.load(response)
except urllib.error.HTTPError as error:
try:
detail = json.loads(error.read())
except (ValueError, UnicodeError):
detail = {"error": {"code": "HTTP_ERROR", "status": error.code}}
code = detail.get("error", {}).get("code")
if attempt == 5 or not (error.code in (429, 500, 502, 503, 504) or code in ("RESOURCE_BUSY",)):
raise RuntimeError(detail) from None
time.sleep(max(float(error.headers.get("Retry-After", 0)), min(2 ** attempt, 16)))
except (urllib.error.URLError, TimeoutError):
if attempt == 5:
raise
time.sleep(min(2 ** attempt, 16))
finally:
# Wait after the response; connection latency must not compress arrival gaps.
next_request = time.monotonic() + 1.05
raise RuntimeError("Request retries exhausted")
# The URL must stay accessible and its contents unchanged until detection finishes.
video_url = sys.argv[1]
if not video_url.startswith(("https://", "http://")):
raise ValueError("Pass a publicly accessible video URL")
detection_key = os.environ.get("VIDEO_DETECTION_KEY", str(uuid.uuid4()))
print("Recovery key:", detection_key)
job = request("POST", "/video-detections", {
"input": {"type": "url", "url": video_url},
"client_reference": "video-example"
}, detection_key)
print("Detection ID:", job["id"])
deadline = time.monotonic() + 1800
while job["status"] in ("queued", "processing"):
if time.monotonic() > deadline:
raise RuntimeError("Still processing. Query the printed detection ID later; do not create another task.")
time.sleep(15)
job = request("GET", "/video-detections/" + job["id"])
print(json.dumps(job, indent=2, ensure_ascii=False))
400 参数或 URL 无效;401 Token 无效;402 可用积分不足;403 API 停用;404 记录不存在;409 幂等冲突、未完成任务数超限或任务状态冲突;410 记录已过期;413 请求过大;422 视频不可访问、不支持分段读取、缺少内容标识、内容变化或格式/时长不符合要求;429 账号限流;5xx 暂时不可用。错误响应包含 error.code 和 request_id。临时错误退避后使用原 Key 重试;受理状态未知时不要换 Key 重建任务。