curl -X POST https://api.fastdrop.io/api/v1/readiness \
-H "Content-Type: application/json" \
-H "X-API-Key: fd_live_your_key_here" \
-d '{
"video_url": "https://example.com/take_04.mp4",
"context": "nle_editing",
"include_data": ["fps", "codec"]
}'
{
"verdict": {
"action": "fix_first",
"context": "nle_editing@v1",
"reasons": [
{
"check": "vfr",
"severity": "high",
"detail": "Variable frame rate: the gaps between frames are uneven, so playback time and frame count disagree.",
"fix": {
"operation": "transcode",
"to": { "frame_rate_mode": "constant", "fps": 29.97 },
"reason": "Constant frame rate keeps audio and video aligned."
},
"evidence": { "vfr_score": 0.34, "avg_fps": 29.97 }
}
],
"user_message": "Fix this before you start. Variable frame rate: the gaps between frames are uneven, so playback time and frame count disagree.",
"checks_passed": ["hevc", "high_bitrate", "min_fps", "no_audio", "no_video_stream", "uhd_4k", "unknown_codec"],
"checks_failed": ["vfr"]
},
"data": {
"avg_fps": 29.97,
"video_codec": "h264",
"audio_codec": "aac",
"container": "mov"
},
"fix_quote": {
"reason_id": "vfr",
"credits": 2,
"resolution_tier": "hd",
"billed_minutes": 1,
"credits_per_minute": 2,
"output": { "width": 1920, "height": 1080 }
},
"credits_charged": 1,
"probe_ms": 412
}
Readiness
Check Readiness
Decide whether footage suits an intended use, before you process it
POST
/
readiness
curl -X POST https://api.fastdrop.io/api/v1/readiness \
-H "Content-Type: application/json" \
-H "X-API-Key: fd_live_your_key_here" \
-d '{
"video_url": "https://example.com/take_04.mp4",
"context": "nle_editing",
"include_data": ["fps", "codec"]
}'
{
"verdict": {
"action": "fix_first",
"context": "nle_editing@v1",
"reasons": [
{
"check": "vfr",
"severity": "high",
"detail": "Variable frame rate: the gaps between frames are uneven, so playback time and frame count disagree.",
"fix": {
"operation": "transcode",
"to": { "frame_rate_mode": "constant", "fps": 29.97 },
"reason": "Constant frame rate keeps audio and video aligned."
},
"evidence": { "vfr_score": 0.34, "avg_fps": 29.97 }
}
],
"user_message": "Fix this before you start. Variable frame rate: the gaps between frames are uneven, so playback time and frame count disagree.",
"checks_passed": ["hevc", "high_bitrate", "min_fps", "no_audio", "no_video_stream", "uhd_4k", "unknown_codec"],
"checks_failed": ["vfr"]
},
"data": {
"avg_fps": 29.97,
"video_codec": "h264",
"audio_codec": "aac",
"container": "mov"
},
"fix_quote": {
"reason_id": "vfr",
"credits": 2,
"resolution_tier": "hd",
"billed_minutes": 1,
"credits_per_minute": 2,
"output": { "width": 1920, "height": 1080 }
},
"credits_charged": 1,
"probe_ms": 412
}
Returns a verdict on whether a video is fit for what you are about to do with it, with machine-actionable instructions for anything that needs fixing.
Unlike every other capability, this answers in the same request rather than returning a job to poll. The point is to decide before committing to expensive work, and a verdict you have to poll for arrives after the decision was needed.
Cost: 1 credit — against the 10 credits a classify-and-transcribe pipeline spends on the same video, or the 19 it costs to run every capability at the premium tier. Checking first costs a tenth of finding out afterwards, and the footage that fails is the footage that would have wasted the whole run.
The distinction between
Present when the verdict is
24fps footage splits the same way — normal delivery for an editor, too slow to associate motion between frames:
Anything rejectable before the video is read costs nothing, and any failure after the charge is refunded.
When to call this
- Gating an upload? Check at ingestion, so a user learns their clip is unusable while they still have the camera in their hand.
- About to dispatch a pipeline or a transcode job? Check first — a
rejecthere saves the entire run. - Handed footage by a third party? Check before you trust it. You did not shoot it and cannot assume it is what it claims to be.
string
required
Public HTTP/HTTPS URL of the video.
string
default:"nle_editing"
What the footage is for. The same clip can pass one context and fail another.
motion_analysis— tracking, optical flow, pose estimation, anything measuring change between framesnle_editing— cutting in Premiere Pro, DaVinci Resolve or Final Cut Prosocial_playback— delivery to a social feed or any other web playback targetarchival— long-term storage: keep the original, record what is notable about it
nle_editing@v1. Without one you get the current version, and the response always reports which was used.See Why check footage before you process it for how the contexts differ on the same clip.object
Relax or tighten a threshold for this call, e.g.
{"min_fps": 24} to accept 24fps footage for motion analysis. A threshold the chosen context does not define is rejected rather than ignored, so a typo surfaces instead of silently doing nothing.Each context declares its own. social_playback accepts max_duration_s, max_file_size_mb, max_bitrate_kbps, allowed_aspect_ratios and allow_hevc; archival declares none at all, because an archive does not refuse footage for being long or slow.Duration, file-size and aspect-ratio limits are off unless you set them. Platform limits differ by an order of magnitude and change without notice, so a default we invented would be wrong more often than yours.array
Raw measurements to return alongside the verdict:
vfr, codec, resolution, fps, duration, bitrate, audio, orientation, provenance.This shapes the response only. Every check the context needs runs either way, and the cost is the same — so a verdict is never based on a measurement that was skipped to save money.Use orientation rather than resolution if you care which way up the video is: phones record vertical footage as a landscape frame plus a rotation flag, so a 9:16 clip is commonly stored 1920x1080. orientation reports aspect_ratio and the display_width/display_height a viewer actually sees.string
Only used if the video falls back to a job. See Very large files.
The three verdicts
action | Meaning |
|---|---|
proceed | Nothing found that blocks this use |
fix_first | Something is wrong, and it can be fixed. A fix says how |
reject | The footage cannot serve this purpose, and no transformation helps |
fix_first and reject is whether a transformation can recover the footage. Variable frame rate can be transcoded away. Frames that were never captured cannot be added, so 12fps footage is rejected for motion analysis rather than being sent off to a transcode that cannot help.
A fix is attached to every reason that has one, and to no reason that does not — so “is there a fix” is answerable without reading the text.
Response (200 OK)
curl -X POST https://api.fastdrop.io/api/v1/readiness \
-H "Content-Type: application/json" \
-H "X-API-Key: fd_live_your_key_here" \
-d '{
"video_url": "https://example.com/take_04.mp4",
"context": "nle_editing",
"include_data": ["fps", "codec"]
}'
{
"verdict": {
"action": "fix_first",
"context": "nle_editing@v1",
"reasons": [
{
"check": "vfr",
"severity": "high",
"detail": "Variable frame rate: the gaps between frames are uneven, so playback time and frame count disagree.",
"fix": {
"operation": "transcode",
"to": { "frame_rate_mode": "constant", "fps": 29.97 },
"reason": "Constant frame rate keeps audio and video aligned."
},
"evidence": { "vfr_score": 0.34, "avg_fps": 29.97 }
}
],
"user_message": "Fix this before you start. Variable frame rate: the gaps between frames are uneven, so playback time and frame count disagree.",
"checks_passed": ["hevc", "high_bitrate", "min_fps", "no_audio", "no_video_stream", "uhd_4k", "unknown_codec"],
"checks_failed": ["vfr"]
},
"data": {
"avg_fps": 29.97,
"video_codec": "h264",
"audio_codec": "aac",
"container": "mov"
},
"fix_quote": {
"reason_id": "vfr",
"credits": 2,
"resolution_tier": "hd",
"billed_minutes": 1,
"credits_per_minute": 2,
"output": { "width": 1920, "height": 1080 }
},
"credits_charged": 1,
"probe_ms": 412
}
fix_quote — what acting on this verdict would cost
Present when the verdict is fix_first and the prescription is one
POST /fix executes. It is the price that endpoint
would charge, computed from measurements already taken — not an estimate.
reason_id says which failure the price is for. A verdict can carry several, and
the one that gets fixed is the most severe, so the number alone would be
ambiguous.
Absent means there is nothing to buy. Either nothing is wrong, or nothing
that is wrong is something we execute — a verdict can prescribe a fix we do not
run yet, and quoting work we would then refuse is worse than saying nothing. It
is never 0; a zero would read as free rather than as unavailable.
You are not billed twice for calling this endpoint first. A fix runs the same
check itself and its quote already covers it, so this call is for when you want
the verdict without committing to the work.
The same clip, three answers
One variable-frame-rate phone clip, asked about three ways. Nothing about the file changes between these calls — only what you said you were going to do with it:# -> fix_first, with a transcode instruction.
# Conformed to a timeline, VFR drifts against the audio.
curl ... -d '{"video_url": "...", "context": "nle_editing"}'
# -> proceed, clean.
# Every player resamples on decode, so the timebase never has to be exact.
curl ... -d '{"video_url": "...", "context": "social_playback"}'
# -> proceed, with a note on the record.
# An archive keeps what it was given and writes down what is notable.
curl ... -d '{"video_url": "...", "context": "archival"}'
# -> proceed
curl ... -d '{"video_url": "...", "context": "nle_editing"}'
# -> reject, min_fps
curl ... -d '{"video_url": "...", "context": "motion_analysis"}'
# -> proceed, with the floor lowered for this call
curl ... -d '{"video_url": "...", "context": "motion_analysis", "overrides": {"min_fps": 24}}'
Notes on a passing verdict
proceed does not always mean nothing was found. A check that matters to a context but does not block it — HEVC for an editor, variable frame rate for an archive — comes back as a low or medium reason with the action still proceed.
Those checks appear in checks_failed, which reads oddly the first time: checks_failed lists every check that found something, and action says whether any of it stops you. Branch on action, not on checks_failed being empty.
{
"action": "proceed",
"context": "archival@v1",
"reasons": [
{
"check": "metadata_incomplete",
"severity": "low",
"detail": "Container metadata is missing device make, device model.",
"fix": null,
"evidence": {
"missing_fields": ["device_make", "device_model"],
"present_fields": ["creation_time"]
}
}
],
"user_message": "Stored as-is. Note for the record: Container metadata is missing device make, device model.",
"checks_failed": ["metadata_incomplete"]
}
Very large files
Reading a video costs bandwidth proportional to its bitrate, not its file size — a five-minute 720p screen recording is cheaper to inspect than a fifteen-second 4K clip. Almost everything fits inside a single request. Video too high-bitrate to assess inline returns202 with a job instead, in the same shape as every other capability. Fetch the same verdict from Get Job Status with ?wait=N.
202
{
"job_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"status": "queued",
"credits_charged": 1,
"estimated_seconds": 60,
"poll_url": "/api/v1/classify/a1b2c3d4-5678-90ab-cdef-1234567890ab",
"context": "nle_editing@v1",
"reason": "This video is too high-bitrate to assess within the inline budget. The job returns the same verdict."
}
The endpoint never returns a verdict based on a check it skipped. If the variable-frame-rate pass cannot run within the inline budget, the request is deferred rather than answered with the check missing — a confident wrong answer being worse than a slower right one.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_url | Not an HTTP/HTTPS URL, or not fetchable |
| 400 | unknown_context | No such context; the response lists the valid ones |
| 400 | invalid_overrides | An override names a threshold this context does not define |
| 400 | invalid_include_data | Unknown field name; the response lists the valid ones |
| 400 | unreadable_video | The URL did not yield a readable video |
| 402 | insufficient_credits | Not enough credits |