OriginAI API
For developers: the same requests this website makes to scan a video. It's a test API for our thesis demo, open without keys, so it may be busy, offline or changed without notice.
Before you start
Every address below starts with the address of this website. Replace https://<your-originai-site> in the examples with it.
A test API
It runs on the same rented GPU as the website, with a daily limit on checks. Don't build anything important on it.
No keys
There are no accounts or API keys. Anyone with a check's id can read its result, so treat ids like private links.
Kept for 1 hour
Results and their evidence images are deleted an hour after a check starts. After that, asking for them returns 404.
Scan a video in three requests
Checks run in the background. Start one, then ask for the result every few seconds until it's ready, without keeping a connection open.
Pick a detector
Lists the four detectors with their id, name and the collection they learned from:
veni-hq,veni-lq,vidiandvici.GET /api/modelsStart a check
Send one video file as
videoand a detector id asmodel, as multipart form data. The answer is202with the check'sidand"status": "processing".POST /api/analysesGet the result
Ask about every 3 seconds.
statusstaysprocessinguntil it becomescompletedorfailed. The first check after a quiet spell can take 5 to 10 minutes while a GPU starts.GET /api/analyses/{id}
An example
Vidi's check of the example clip on the home page. The finished result is shortened: clips and heatmaps hold one entry per part of the video.
curl -X POST "https://<your-originai-site>/api/analyses" \
-F "video=@clip.mp4" \
-F "model=vidi"
{
"id": "8c1f3e2a9b7d4f60a5e2c9d1b3f47a68",
"status": "processing",
"model": {
"id": "vidi",
"name": "Vidi",
"dataset": "Celeb-DF v2"
},
"created": 1791085773.32
}
curl "https://<your-originai-site>/api/analyses/8c1f3e2a9b7d4f60a5e2c9d1b3f47a68"
{
"id": "2d7a9e41c0b84f3fa6e15c8b9d0f2a73",
"status": "failed",
"model": {
"id": "veni-hq",
"name": "Veni HQ",
"dataset": "FaceForensics++ C23 (HQ)"
},
"reason": "no_face",
"error": "No face was detected in the video."
}
{
"id": "8c1f3e2a9b7d4f60a5e2c9d1b3f47a68",
"status": "completed",
"model": { "id": "vidi", "name": "Vidi", "dataset": "Celeb-DF v2" },
"created": 1791085773.32,
"pred": "DEEPFAKE",
"prob_fake": 0.9059,
"raw_prob_fake": 0.99999,
"threshold": 0.2286,
"video_logit": 11.357,
"aggregation": "mean_clip_logit",
"meta": { "width": 944, "height": 500, "fps": 30.0, "duration": 15.633 },
"coverage": {
"mode": "every_frame_phased",
"duration": 15.633,
"frames_analyzed": 469,
"analyzed_start_s": 0.0,
"analyzed_end_s": 15.6,
"fallback": null
},
"clips": [
{ "index": 0, "start_s": 1.75, "end_s": 3.5, "prob": 0.8797 }
],
"protocol": { "available": true, "prob_fake": 0.8871, "pred": "DEEPFAKE" },
"heatmaps": {
"target": "DEEPFAKE",
"clips": [
{
"clip_index": 4,
"clip_start_s": 7.2,
"clip_end_s": 10.7,
"clip_prob": 0.9695,
"frames": [
{
"t": 7.2,
"frame": "/api/analyses/8c1f3e2a9b7d4f60a5e2c9d1b3f47a68/media/c4_f0_frame.jpg",
"frame_heat": "/api/analyses/8c1f3e2a9b7d4f60a5e2c9d1b3f47a68/media/c4_f0_frame_heat.jpg"
}
]
}
]
},
"timings": { "total": 1.51 }
}
What a result contains
Every answer from GET /api/analyses/{id} has id, status, model and created. The rest depends on the status.
| Field | What it means |
|---|---|
status | processing, then completed or failed. |
stage | While processing: fetching (a link's video is being downloaded) or analyzing. Then completed or failed. |
pred | The result: DEEPFAKE or REAL. |
prob_fake | The calibrated score from 0 to 1. pred is DEEPFAKE when it's at or above threshold. |
threshold | The detector's own line: veni-hq 0.5248, veni-lq 0.4893, vidi 0.2286, vici 0.8322. Compare scores only with the line of the detector that made them. |
raw_prob_fake, video_logit, aggregation | The score before calibration, the combined raw score, and how the clip scores were combined (mean_clip_logit). |
meta | The video's width, height, fps and duration in seconds. |
coverage | What was checked: how many frames and which seconds. fallback is not null (and mode is protocol_fallback) when too few frames showed a usable face and only a few short clips were checked. Such a result is less reliable. |
clips | The timeline: the video in equal parts, each with start_s, end_s and its score prob, averaged over every check that overlaps it. |
heatmaps | The moments with evidence images. Each has its own clip_start_s, clip_end_s and score clip_prob, and frames with image paths. Its clip_index is not an index into clips. |
protocol | The second check: three short clips scored the way the detector was tested. It looks at less of the video, so it can disagree with pred. |
timings | How long each step took on the GPU, in seconds. |
error, reason | When the check failed: a message for people, and a short code such as no_face, insufficient_valid_frames or decode_failed. |
The other requests
The website also uses these. Links work only for public videos from the platforms the server lists.
| Request | What it does |
|---|---|
GET /api/capacity | Whether a new check is accepted right now (accepting, and a detail message and code when it isn't), how long a check may run (scan_timeout_seconds) and the file limits. |
POST /api/analyses/link | Starts a check of a video link. Send JSON {"url": "…", "model": "vidi"}. The answer is 202 like an upload, with "stage": "fetching" and a source naming the platform. |
GET /api/link-sources | Whether links can be checked, the accepted platforms, and the known off_platforms that can't be checked yet. |
POST /api/links/inspect | Looks up a link's title, length and quality without checking it. Send JSON {"url": "…"}. |
GET /api/analyses/{id}/media/{name} | An evidence image named in heatmaps. Kept for 1 hour, like the result. |
Limits and errors
A refused request answers with JSON: a detail message you can show to people, and a code your app can check.
What can be checked
- MP4, MOV, WebM or MKV
- 10 seconds to 2 minutes long
- 360p to 1080p (the shorter side)
- Up to 150 MB
- Results expire after 1 hour
| Status | Codes |
|---|---|
| 400 | unknown_model, bad_format, empty, not_video, too_short, too_long, resolution_low, resolution_high, invalid_request. Link requests can also answer unsupported, private or platform_off. |
| 404 | The check doesn't exist or has expired. |
| 413 | too_large |
| 429 | busy, daily_limit, visitor_busy, visitor_daily_limit. Try again later. |
| 503 | not_configured: the detectors aren't connected right now. |
Showing results to your users
Most of your users won't be experts. A few rules keep the results honest.
Show every state
Show when a check is waiting, running, done or failed, and let people retry without uploading again.
Keep the context
Show the detector, what it learned from and its threshold next to every result.
Don't call it proof
Describe the result as one clue, and point people to ways of checking further.
Want to build with OriginAI?
This test API is here for the thesis demo. If you'd like to use it for something, tell us what you're building first.