Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:write
Starts a job: a ready media is processed with your settings on our full pipeline (restoration, Reconstruct, colorization with your review of the keyframes, frame interpolation, sound restoration). The price is taken from your balance at this moment and given back once if the job does not deliver.
Parameters
Name
Where
Type
Required
What it is
Idempotency-Key
header
string
no (recommended)
Your own value of 1 to 64 printable characters: a new random one (a UUID) for every new request, and the same one when you send that request again after a lost response. The shell examples keep it in a variable that a second run of the same block does not change (see Idempotency keys). Example: 9b2f0c7e-3d4a-4f61-8a5e-2c1b7d9e0f43.
media_id
body (JSON)
string
yes
The ready media to process. Example: med_4821.
settings
body (JSON)
object
yes
What the job does: either preset_id or filters (not both), optionally output_format and output_scaling.
settings.preset_id
body (JSON)
string
one of the two
A preset of GET /catalogue (presets[].id): its filters, output format and output scaling. Example: pre_90. A preset's upload server (a website setting) is never used by an API job: the result stays here for you to download.
settings.filters
body (JSON)
array of strings
one of the two
Filter keys of GET /catalogue (filters[].key; the full list is in Filters). At most one of light_dust_and_scratches, medium_dust_and_scratches and heavy_dust_and_scratches. An empty list [] runs no filter: the job only re-encodes the media into the output format (and scales it, with output_scaling), at the full price of its tier. Example: ["noise", "medium_dust_and_scratches"].
settings.output_format
body (JSON)
string
no
The result's file type: mov_prores_422_hq, mov_prores_4444, mxf_dnxhr_hqx, mxf_dnxhr_444, mp4_h264 or mp4_hevc. Default: the preset's format, or mov_prores_422_hq with filters.
settings.output_scaling
body (JSON)
string
no
The result's height: source (the same as the media), 1080 (HD) or 2160 (4K). Default: the preset's scaling, or source with filters. A larger output can raise the price (see Billing).
output
body (JSON)
object
no
How the result fits back into an editing timeline. Every part has a default; see Output options.
output.geometry
body (JSON)
string
no
restored or match_source. Default: restored.
output.frame_rate
body (JSON)
string
no
processed or source. Default: processed.
output.audio
body (JSON)
string
no
source (the sound as it is - the preset's sound filters are left out), restored (the sound filters of the settings run) or none (no sound). Default: source.
output.timecode
body (JSON)
string
no
Only source (the media's own timecode). Default: source.
output.reel_name
body (JSON)
string
no
1 to 32 printable ASCII characters (letters, digits, spaces, punctuation), written as the reel name of the result's timecode track. Default: none. Example: A001.
output.format
body (JSON)
string
no
The same as settings.output_format; when both are sent, this one counts.
output.scaling
body (JSON)
string
no
The same as settings.output_scaling; when both are sent, this one counts.
keyframes
body (JSON)
string
no
Colorization only: review (the job waits for you to look at the colorized keyframes - see Keyframes) or auto (they are approved as they are). Default: review.
reference
body (JSON)
string
no
Your own name for the job, up to 100 characters; it comes back unchanged. Example: Shot 12.
client_data
body (JSON)
object
no
Anything your program wants to find again with the job, up to 4 KB as JSON; it comes back unchanged. Example: {"sequence": "Main"}.
notify_url
body (JSON)
string
no
A public https address of yours for webhooks about this job. API keys only.
range
body (JSON)
null
no
Leave it out (parts of a media are not supported yet: upload the part you want).
What you get back: 201 Created with a Job object (statusqueued), a Location header (the job's address) and Retry-After: 1. Then follow it with GET /jobs/{job_id}.
Good to know: A media can have one job at a time: a second start answers JobExistsForMedia with the existing job's id in details.job_id. To run other settings on the same media, delete the first job (after downloading its result). Send an Idempotency-Key: if the response is lost and you send the request again with the same key, you get the same job instead of a second one (and a second charge). These combinations cannot be made and answer OptionConflict with the reason in detail: two dust and scratches levels; an MXF output at a frame rate MXF cannot hold (for example 16 or 18 fps) or smaller than 256×120 pixels; MXF for a sound-only media; output.audionone for a sound-only media; a sound-only media whose sound is silent; match_source with an output scaling or with a stretched or squeezed picture; output.frame_ratesource with natural speed or high frame rate.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:read
Shows one job: its status and stage, its progress, its place in the queue, its price and billing, and - once it is completed - its result. Poll it to follow a job.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
If-None-Match
header
string
no
The last ETag (a free 304 when nothing has changed).
What you get back: 200 OK with a Job object, an ETag and a Retry-After header: 1 while the job is queued or running, 60 otherwise (also while it waits for your keyframes).
Good to know: How long a job takes depends on the length of the media, its frame size, the filters and the queue; progress.eta_seconds gives the estimated seconds left once we can tell.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:read
Lists your jobs, newest first, a page at a time. Use it to find your jobs again after your program restarted - with the reference and client_data you gave them.
Parameters
Name
Where
Type
Required
What it is
status
query
string
no
active (queued, running or waiting for keyframes), finished (completed, failed or cancelled) or all. Default: all.
media_id
query
string
no
Only the jobs of this media. A media that is not yours answers NotFound.
limit
query
integer
no
How many entries one page holds: 1 to 200. Default: 50.
cursor
query
string
no
The next_cursor of the page before, exactly as it came. Leave it out for the first page. See Lists and pages.
What you get back: 200 OK with data (a list of Job objects) and next_cursor (a string for the next page, or null on the last page).
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:write
Stops a job that has not ended. What it was charged is given back (once). The job stays, with statuscancelled, so you keep a record of it.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
What you get back: 202 Accepted with the Job object as it is now (statuscancelled, billing.staterefunded).
Good to know: A cancelled job still counts as the media's job: delete it (DELETE /jobs/{job_id}) before you start another job of the same media. A job that has ended already answers JobNotCancellable.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:read
Downloads the finished file of a completed job. An interrupted download can be resumed where it stopped.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
Range
header
string
no
One byte range, to resume: bytes=1048576- (from byte 1048576 to the end), bytes=0-1023 (the first 1024 bytes) or bytes=-1024 (the last 1024 bytes). curl -C - sends it for you.
If-Range
header
string
no
The result's ETag: the range counts only while the file is still the same; otherwise the whole file comes (200).
If-None-Match
header
string
no
The result's ETag: 304 when you have this file already.
What you get back: 200 OK with the whole file, or 206 Partial Content with the range you asked for (Content-Range says which bytes). The headers: Content-Type (video/quicktime, application/mxf or video/mp4), Content-Length (bytes), ETag, Accept-Ranges: bytes and Content-Disposition with the file name - an ASCII filename= and, for a name with other characters, also filename*=UTF-8''... (RFC 6266); use that one when it is there. Cache-Control: private, no-cache: no shared cache (a company proxy) keeps the file. HEAD gives the headers without the file.
Good to know: With curl --fail an error answer (for example ResultNotReady) is never saved as the file: curl ends with exit code 22, and the example then deletes what it had saved - it is not your result. A broken connection ends with another exit code and keeps the part: run the same line again and -C - continues it. Downloads have no per-minute limit. After the download, compare the file's SHA-256 with result.sha256 of the job (given for jobs started through the API; null for a job started in the workspace). The result is kept 60 days after the job finished (result.expires_at).
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:read
Makes a download address for the result that works without a key - for a colleague's browser, a media asset manager or a transcoder - until the time you choose.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
expires_in
body (JSON)
integer
no
How long the address works, in seconds: 60 to 86400 (24 hours). Default: 3600 (1 hour). The body can be empty.
What you get back: 201 Created with url (the address; anyone who has it can download the file until it expires - treat it like a password) and expires_at (when it stops working, UTC). The address supports Range, and its download has the same file name (Content-Disposition) and Cache-Control: private, no-cache as GET /jobs/{job_id}/result.
Good to know: Available when GET /health says features.signed_result_links true; otherwise FeatureUnavailable. The address is always on www.recuromedia.com, also when your files are on a cloud server: while that server is starting, the address answers 503 Service Unavailable with Retry-After - try again then.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:read
Downloads the same frame before colorization - to show next to the keyframe as a before/after.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
frame
path
integer
yes
The keyframe's frame number (counted from 0), as frame in GET /jobs/{job_id}/keyframes. Example: 12.
What you get back: 200 OK with a JPEG of the same size as the keyframe's thumbnail (480 pixels wide), cropped as the keyframe.
Good to know: The frame numbers are the media's frames as analysed: a 29.97 fps clip with 3:2 pulldown was turned into 23.976 fps at the analysis, so its frames are counted at 23.976 fps. A frame that is not one of the job's keyframes answers NotFound.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope keyframes:write
Saves your choices for the keyframes: the colour strength (saturation) for the whole job and per keyframe, which keyframes to leave out, and which colour version of a keyframe to use. What you do not name stays as it is.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
saturation
body (JSON)
integer
no
The colour strength of the whole job in percent: 50 to 200 (100 = as colorized). It applies to every keyframe without its own saturation.
keyframes
body (JSON)
object
no
Per keyframe: the frame number as a string (the key) and an object with any of the three entries below. Example: {"12": {"saturation": 120}}.
keyframes.<frame>.saturation
body (JSON)
integer
no
This keyframe's colour strength in percent: 50 to 200.
keyframes.<frame>.excluded
body (JSON)
boolean
no
true leaves the keyframe out; false uses it again. Every scene keeps at least one keyframe (SceneNeedsKeyframe).
keyframes.<frame>.version
body (JSON)
integer
no
Which colour version to use: one of the keyframe's versions (1 is the colorization; see Modify). Free, any number of times.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope keyframes:write
Approves the keyframes: the job carries their colours to every frame and runs on to the end. This is the workspace's FINISH JOB. You can send last choices in the same request.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
What you get back: 202 Accepted with the Job object (statusrunning, stagepropagating). Follow it with GET /jobs/{job_id} as before.
Good to know: The active version of every keyframe is used - also a picture our check rejected that you chose (attentionrejected: it is your choice). While a keyframe is being changed (Modify), approving answers ModifyInProgress with Retry-After: 1: wait until the change is done or failed. While a keyframe you use has no picture at all (needs_choice true), approving answers KeyframeNeedsChoice: choose another version, change it or leave it out first.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope keyframes:write
Has one keyframe's colours changed as you describe them in a sentence, for example make all uniforms dark green. The result becomes a new version of the keyframe and the one in use. A keyframe keeps a number of tries at a time by plan (Free, Prepaid and Invoice 5 tries per keyframe at a time, Trial none (Modify is not included)); on the Prepaid plan each try is a small charge from the balance; on the Free and Invoice plans tries are not charged. One change can go to every keyframe of the scene (scene).
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
frame
path
integer
yes
The keyframe's frame number (counted from 0), as frame in GET /jobs/{job_id}/keyframes. Example: 12.
Idempotency-Key
header
string
no (recommended)
Your own value of 1 to 64 printable characters: a new random one (a UUID) for every new request, and the same one when you send that request again after a lost response. The shell examples keep it in a variable that a second run of the same block does not change (see Idempotency keys). Example: 9b2f0c7e-3d4a-4f61-8a5e-2c1b7d9e0f43.
text
body (JSON)
string
yes
What to change, as plain text on one line: 1 to 300 characters after spaces at the start and end are removed; no line breaks or control characters; the colour described in words, not as a code. Example: make all uniforms dark green.
scene
body (JSON)
boolean
no
true: the same change for every keyframe of this keyframe's scene that is not left out - each from its own active version, all started at once; it counts as ONE change of the scene. Default false: this keyframe only.
confirm_warning
body (JSON)
boolean
no
Accepted and ignored (since 22 Oct 2026 a warning of the check never stops a change - it comes back as modify.warning).
What you get back: 202 Accepted with the Keyframe object (modify.staterunning), a Location header (the address to poll) and Retry-After: 1. Poll GET on the same address every second until modify.state is done or failed; in our tests a change took 7 to 10 seconds.
Good to know: Our AI image service edits the keyframe's active version: only what the text asks for changes; the other colours, the light and the picture stay - and every colour keeps the film's own brightness (a light thing stays light, a dark one dark: its colour can change, not how light it is). Before anything is started an AI check reads the text with the keyframe(s): a text that asks for something other than a colour change (adding or removing things, light or darkness, faces, fixing the picture, text, a whole style) or names something the picture does not show is refused with ModifyNotPossible (details.reason, in the language of the text; not counted); a colour much darker or lighter than the thing is in the film only gives a warning - the change is made and modify.warning holds the sentence to show (it comes out lighter or darker); describe the colour in words (for example a soft lavender purple) - that gives the most natural result: a colour code such as #5F3AB1 is not passed on, the check describes it in words (modify.sent_text), and a code alone with nothing named is refused (ModifyNotPossible). Every picture a change gives a keyframe is one try - also one our check rejected (it is in versions, marked); a request that gives no picture (a refusal, a failure, a cancel) is none. A keyframe keeps its plan's tries at a time (Free, Prepaid and Invoice 5 tries per keyframe at a time, Trial none (Modify is not included); modify.limit and modify.kept_max, modify.used and modify.kept the tries it keeps now): a change of a keyframe that keeps them all - also one for the whole scene when any keyframe it would change does - is refused before the check with KeyframeTriesFull; delete one first (DELETE .../versions/{version}) - that makes room. On the Trial plan Modify is not included (modify.included false, modify.available false): the request answers ModifyNotIncluded before anything is checked. Charges: on the Prepaid plan each try is a small charge from the balance; on the Free and Invoice plans tries are not charged (modify.charged; whether a try has been charged is versions[].charge, and each charged try is listed in the order history of the website). A request that gives no picture - a refusal of the check, of the content rules, a failure - is never charged. On the Prepaid plan the balance must hold EUR 0.05 for each keyframe the change goes to, or the request answers BalanceInsufficient before anything is checked. Any keyframe of the job can be changed, also one you left out - only while the job waits for its keyframes, and one change of a job at a time (ModifyInProgress - also while a check runs). A refusal or failure of the image service is not an HTTP error: the keyframe then shows modify.statefailed with the reason. When this site does not offer changes (modify.available false) the request answers FeatureUnavailable with details.reasonnot_enabled (changes are switched off on this site) or not_configured (this site's own wording for changes is not set up) and no Retry-After - do not retry, and do not offer the change in your program then; when no service can make the change at the moment, details.reason is no_worker with Retry-After: 60; when the check could not be made in time (15 seconds), details.reason is precheck_failed with Retry-After: 5 - try again.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope jobs:read
Downloads the image of one colour version of a keyframe - to show the versions side by side before you choose one.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
frame
path
integer
yes
The keyframe's frame number (counted from 0), as frame in GET /jobs/{job_id}/keyframes. Example: 12.
version
path
integer
yes
The version number, 1 or more (one of the keyframe's versions).
ext
path
string
yes
jpg (a JPEG thumbnail, 480 pixels wide) or png (a PNG at full size).
What you get back: 200 OK with the image. A version never changes, so it is cached by your client for up to 3600 seconds (Cache-Control: private, max-age=3600).
Good to know: To use another version, send {"keyframes": {"12": {"version": 1}}} with PUT /jobs/{job_id}/keyframes - free, also on a keyframe that keeps its most tries and on the Trial plan. Approving carries the colours of each keyframe's active version to every frame.
Credential: API key and secret (HTTP Basic) or an access token (Bearer) · scope keyframes:write
Deletes one Modify try of a keyframe. A keyframe keeps its plan's tries at a time (Free, Prepaid and Invoice 5 tries per keyframe at a time, Trial none (Modify is not included)); deleting one makes room for the next change.
Parameters
Name
Where
Type
Required
What it is
job_id
path
string
yes
The job's id, as POST /jobs or GET /jobs gave it. Example: job_3150.
frame
path
integer
yes
The keyframe's frame number (counted from 0), as frame in GET /jobs/{job_id}/keyframes. Example: 12.
version
path
integer
yes
One of the keyframe's versions with deletabletrue (a change's picture, also one our check rejected).
What you get back: 200 OK with the Keyframe object as GET .../keyframes/{frame}/modify shows it, without the version.
Good to know: The version and its pictures are gone for good, and its number is never used again. Its place is free again (modify.used and modify.kept one less, modify.remaining one more). A try on the Prepaid plan was charged when it was made: deleting it gives no money back. When it was the version in use, original_version - the keyframe's own colorization - is used instead. The colorization's own version and its tries cannot be deleted (ValidationFailed); while a change of this keyframe runs or a check is under way the request answers ModifyInProgress with Retry-After: 1.
You opened a sign-in link from your e-mail. Signing in here signs you in on this device (this browser). If you asked for the link in another window, you can type its code there instead.