DocsGuides
AI upscale
Raise a video to 1080p, 1440p or 4K with AI, see what it reserves first, and read how it was enhanced.
POST /v1/ai-upscale raises one video to 1080p, 1440p or 4k with AI. It can also double a
frame rate of 30 fps or less for smoother motion. The input must be at most
1920 by 1080 and smaller than the target. Only purchased credits pay for it, and each
plan has its own targets and longest clip.
The request#
curl "$VIDEXO_API_URL/v1/ai-upscale" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "https://example.com/talk.mp4",
"resolution": "1080p",
"trim": {
"start": 0,
"duration": 5
},
"filename": "talk-1080p.mp4"
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/ai-upscale`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"input": "https://example.com/talk.mp4",
"resolution": "1080p",
"trim": {
"start": 0,
"duration": 5
},
"filename": "talk-1080p.mp4"
}),
});
const job = await response.json();Copy failed. The text is selected; copy it with your keyboard.
| Field | What it does |
|---|---|
input | The video: a public https URL or a videxo://uploads/<id> upload. |
resolution | The target: 1080p, 1440p or 4k. |
frame_rate | source (default) keeps the input's rate, at most 60 fps: a faster input comes out at 60. smooth doubles a rate of 30 fps or less, so 24 fps becomes 48 and 30 becomes 60; a faster input keeps its rate, at most 60 fps. |
trim | The part to upscale: start in seconds (0 by default) and duration, at least 1 second and at most your plan's longest clip. Without duration the job runs to the end of the input, up to that limit. |
audio | keep (default) puts the input's own sound back in sync; remove leaves the output silent. |
filename | The stored file name, .mp4 only; upscaled.mp4 by default. |
webhook_url, metadata | As on every job. |
The same upscale can be one output of POST /v1/jobs: "type": "ai_upscale" with the fields
above and input naming one of the job's inputs. An AI upscale job has that one output only.
The answer is the Job, queued, as for any job. It has no ffmpeg command: its
command, input_files, output_files and ffmpeg_command are null, and steps is empty.
Estimate first#
POST /v1/ai-upscale/estimate takes the same body and answers what the job would reserve under your
plan, and how long its processing is expected to take. It makes no job, fetches nothing and uses no
credits.
curl "$VIDEXO_API_URL/v1/ai-upscale/estimate" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "https://videxo.io/samples/talk.mp4",
"resolution": "1080p",
"trim": {
"start": 0,
"duration": 8
}
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/ai-upscale/estimate`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"input": "https://videxo.io/samples/talk.mp4",
"resolution": "1080p",
"trim": {
"start": 0,
"duration": 8
}
}),
});
const estimate = await response.json();Copy failed. The text is selected; copy it with your keyboard.
On Pay as you go:
{
"operation": "ai_upscale",
"max_seconds": 8,
"credits_base": 75,
"credits_per_second": 64,
"credits_max": 587,
"expected_wait_seconds": {
"low": 146,
"high": 446
},
"frame_rate_bands": {
"up_to_30_fps": {
"credits_per_second": 32,
"credits_max": 331,
"expected_wait_seconds": {
"low": 73,
"high": 373
}
},
"above_30_fps": {
"credits_per_second": 64,
"credits_max": 587,
"expected_wait_seconds": {
"low": 146,
"high": 446
}
}
}
}Copy failed. The text is selected; copy it with your keyboard.
The input is not read, so the clip is trim.duration, or your plan's longest clip without one, and
both frame-rate bands are given. credits_max is the higher band: 587
credits for this 8 second clip, against 331
if it turns out to be 30 fps or less. expected_wait_seconds is the time the AI processing is
expected to take, a usual case (low) and a slow one (high); the slow one includes the time to
start a GPU.
How the input's size decides#
The upscale always runs the AI model, then corrects its colour and keeps blur the input meant to
have. How much of the model's own detail stays depends on how small the input is next to the frame
the result fills: the target's frame fitted to the input's shape, turned for a portrait input. A
16:9 input fills the whole frame; a 4:3 input at 1080p fills 1440 by 1080.
full: an input under half that frame on both sides. The model adds detail the input does not have.faithful: an input of half that frame or more on either side. The result is a faithful enhancement, close to a sharp resize: the model's detail is kept only above the input's own resolution.
resolution | Target frame | A 16:9 input is full when under |
|---|---|---|
1080p | 1920 by 1080 | 960 by 540 |
1440p | 2560 by 1440 | 1280 by 720 |
4k | 3840 by 2160 | 1920 by 1080 |
So 960 by 540 to 1080p is faithful, 958 by 538 to 1080p is full, and a 1280 by 720 input
is faithful at 1080p and 1440p and full at 4k. Each job's output shows which way it ran in
its enhancement, once the job has read the input. The estimate cannot say, since it does not read
the input.
What it costs#
An upscale is billed per second of output, by its target and its output's frame rate, plus 75 credits a job and the data used, as for every job:
| Target | Up to 30 fps | Above 30 fps |
|---|---|---|
| 1080p | 32 credits per output second | 64 credits per output second |
| 1440p | 55 credits per output second | 110 credits per output second |
| 4K | 119 credits per output second | 238 credits per output second |
- The rate follows the output's frame rate.
smoothdoubles a rate of 30 fps or less, up to 60: a 24 or 30 fps input made smooth is billed above 30 fps, a 15 fps one (30 fps out) up to it. - Seconds are the output's length, rounded up to the whole second as for every job, and at least 1.
- Only purchased credits pay for it, from a plan or a top-up. Free credits never do: a job they would
have to pay for is refused with
402 out_of_credits(purchased_credits_only) before it is made. - Creating the job reserves the estimate's
credits_max. When it succeeds, it pays the rate of the clip it made and the 75 credits. A job you cancel while it is being processed pays for the seconds already processed and the 75 credits. A job that fails pays at most the data it fetched.
The pricing page has the same table in dollars.
Limits per plan#
The longest clip each plan upscales, after trim:
| Plan | 1080p | 1440p | 4K | AI jobs at once |
|---|---|---|---|---|
| Pay as you go | 10 s | 10 s | not included | 1 |
| Starter | 10 s | 10 s | not included | 1 |
| Pro | 20 s | 20 s | 10 s | 3 |
| Scale | 20 s | 20 s | 20 s | 5 |
A target your plan does not include is refused with 413 limit_exceeded (media_resolution), and a
trim.duration over its longest clip with 413 limit_exceeded (media_duration), both before any
job is made. AI jobs also count toward your plan's jobs at once.
The wait#
expected_wait_seconds is a planning figure, not a promise. When there is no free GPU capacity, the
job waits for it. If processing cannot start in time, which can happen up to about
90 minutes after the job took one of your plan's jobs at once (time queued
for a free one comes on top), the job ends failed with service_unavailable (temporary) and
nothing is charged. Send it again later.
The job#
Once the job has read the input, it shows what the clip will cost and how long it should take:
{
"ai": {
"seconds": 5,
"credits": 235,
"expected_wait_seconds": { "low": 46, "high": 346 }
},
"outputs": [
{
"name": "video",
"filename": "talk-1080p.mp4",
"enhancement": "faithful"
}
]
}Copy failed. The text is selected; copy it with your keyboard.
ai.seconds: the clip's billed seconds.ai.credits: what a success costs at the clip's frame rate, with the 75 credits, before the data used.ai.expected_wait_seconds: the planning wait of this clip.outputs[].enhancement:fullorfaithful, as above. It is null on every other output.
ai is null until the input is read, and on every job that is not AI. The job's progress and its
webhook work as for every job. The output is an H.264 MP4 that fills the frame
above.
It says in its metadata that it was enhanced with AI, and it carries our signed C2PA manifest.
Errors#
| Answer | When | Charged |
|---|---|---|
400 invalid_request | A field is missing, unknown or out of range, with errors naming it. | Nothing: no job. |
402 out_of_credits (purchased_credits_only) | Only free credits are left to pay for it. | Nothing: no job. |
413 limit_exceeded (media_resolution, media_duration) | Your plan does not include the target, or trim.duration is over its longest clip. | Nothing: no job. |
503 service_unavailable (temporary) | AI upscale is not available right now. | Nothing: no job. Send it again later. |
Job failed, limit_exceeded (media_resolution) | The input is larger than 1920 by 1080, or not smaller than the target. | The data fetched. |
Job failed, limit_exceeded (media_duration) | The clip after trim is shorter than 1 second, or longer than allowed. | The data fetched. |
Job failed, processing_failed (media_unsupported) | The input could not be read as a video. | The data fetched. |
Job failed, service_unavailable (temporary, processing_timeout) | No capacity in time, or the processing did not finish in time. | Nothing. |
Job failed, internal_error | Our side failed. | Nothing. |
Every code is on the errors page.
With MCP#
An AI assistant connected to the MCP server has the same two calls as the tools
estimate_ai_upscale and ai_upscale, with the same body.