Recuro Media API

Restore and colorize film from your own program: upload a file, see what the job would cost, try a free preview frame, run restoration, Reconstruct, colorization, frame interpolation and sound restoration on our servers, and download the result - from a media asset manager, a render farm, a script or a plugin of an editing application. You can do most of what the workspace on our website does. These stay on the website: paying (top-ups, orders, invoices), the crop mask (anamorphic and letterbox corrections), video previews, saving your own presets, SFTP delivery servers, the project fields (Project Owner, Project ID) and the account settings.

New to APIs? Read Start here (5 minutes), then make your first call. Experienced? The workflow, the reference and the machine-readable openapi.yaml (OpenAPI 3.1) are what you need.

  • Base address: https://www.recuromedia.com/api/1.0 - HTTPS only. Requests and responses are JSON (application/json; charset=utf-8), except chunks of files, images and results.
  • Versioning: this is version 1.0, at /api/1.0. It only grows: we add fields, endpoints and error codes without notice, so your program must ignore fields it does not know. A change that would break a program gets a new version address; none is planned.
  • Never put an API key into a web page's JavaScript: every visitor could read it. Call the API from your server or a desktop program. (The API answers browser requests from other addresses - CORS, Access-Control-Allow-Origin: *, no cookies - for the desktop plugins' web views, not to put secrets into web pages.)

The API was rebuilt on 6 Oct 2026. Requests in the earlier form (one address with an action field) answer ApiActionRetired.

Start here

This part is for you if you have never used an API before. It explains what an API is, what ours does, what you need, and every word the rest of the page uses.

What an API is

An API (application programming interface) lets one program use another program over the internet: your program sends a request to our server, and our server sends back a response with data your program can read - no person clicks a button. You can try it in a terminal window with one command (your first call) before you write a single line of a program.

What you can do with our API

  • Send us film: upload a video or sound file of up to 50 GiB in pieces that survive a broken connection, or let us fetch it from an https address.
  • Learn about it: we analyse every file - length, frame rate, frame size, black borders, black-and-white or colour, scenes, sound, timecode.
  • Know the price first: a quote tells you what a job costs and whether your balance covers it.
  • Try for free: a preview shows one frame before and after, without charge.
  • Process it: restoration (noise, dust and scratches, flicker, colour, stability, sharpness), Reconstruct, colorization (with your review of the colours), frame interpolation, sound restoration - in MOV, MXF or MP4.
  • Get it back: follow the progress, download the result (resumable), or hand out a download link; let us call your server when a job is done (webhooks).

What you need before you start

  1. An account. Sign up on the website with Sign Up at the top of the page, and confirm your e-mail address. A company account has one administrator: the person who signed up.
  2. API access. Contact us and ask us to switch on API access for your administrator account. Until we have, the next step is not shown.
  3. An API key and secret. Signed in as the administrator, open My Account (top right) > Administrator Tools, find API Key and Secret and press CREATE API SECRET. The key (16 characters) and the secret (32 characters) appear. Copy the secret at once: it is shown only this one time. Keep it in a password manager or your server's secret store - never in your code or in a repository. Lost it? Press DELETE API SECRET..., then CREATE API SECRET again; the old secret stops working at that moment.
  4. Money or seconds on the account. Jobs are paid from a prepaid balance (top it up in the workspace) or, by agreement, by invoice. Previews and quotes are free. See Billing.
  5. A tool to send requests. curl is already on Windows 10 and 11, macOS and Linux. For programs: any language that can send HTTPS requests; our full example is in Python 3.8 or later.

Words you will meet

Each word has a tiny example. Links to these words appear across the page.

API (application programming interface)
A way for one program to use another program over the internet. Your program sends a request to our server and gets a response back - no person clicks anything. Example: Your script asks "what does it cost?" and gets the price as data.
Request
One message from your program to our server: an HTTP method, an address, headers and sometimes a body. Example: GET https://www.recuromedia.com/api/1.0/me
Response
The message our server sends back to one request: a status code, headers and - except for 204 and 304 - a body, which is JSON unless you downloaded a file or an image. Example: 200 OK with {"user": {...}}
Base address
The beginning that every address of this API shares. You add the path of an endpoint to it. Example: https://www.recuromedia.com/api/1.0 + /me
Endpoint
One HTTP method together with one path: one thing the API can do. The reference lists all of them. Example: POST /jobs starts a job.
Path parameter
A part of the path that you fill in, written in curly brackets in this documentation. Example: /jobs/{job_id} becomes /jobs/job_3150.
Query parameter
An option added to the address after a question mark; several are joined with &. Example: /media?status=ready&limit=2
HTTP method
The kind of request: GET reads, POST creates or does something, PUT replaces or saves, DELETE removes, HEAD reads only the headers. Example: DELETE /jobs/job_3150 deletes a job.
Status code
A three-digit number at the start of every response that says how the request went: 2xx worked, 3xx nothing new, 4xx your request has a problem, 5xx our side has a problem. All of them are explained under Errors. Example: 201 Created, 404 Not Found
Header
A name and a value sent before the body of a request or a response, for information about it (who you are, the body's format, when to ask again). Example: Content-Type: application/json
Body
The content of a request or response after the headers. In this API it is JSON, except when you send a chunk of a file or download an image or a result. Example: {"file_name": "A001_C003.mov", "size": 35181362}
JSON (JavaScript Object Notation)
A text format for data: objects in curly brackets with "name": value pairs, lists in square brackets, text in double quotes, numbers, true, false and null (no value). Example: {"status": "ready", "warnings": []}
API key and API secret
Your program's user name (the key, 16 characters) and password (the secret, 32 characters) for this API. You send both with every request. Keep the secret secret, like a password. Example: curl -u "YOUR_API_KEY:YOUR_API_SECRET" ...
Access token
A temporary password (it starts with rma_) that works for 1 hour. Apps get one when a person signs in; partners can swap their key and secret for one. Example: Authorization: Bearer rma_4QyH0m1Zr8...
Refresh token
A longer-lived token of an app (it starts with rmr_, works 90 days) used only to get new access tokens. Example: POST /auth/token with grant_type refresh_token
Scope
A permission of a key or token: which endpoints it is allowed to use. Each endpoint names its scope in the reference. Example: jobs:write allows starting and deleting jobs.
Credential
Whatever proves who you are in a request: your API key and secret, or an access token. Example: Limits are counted per credential.
Upload
The transfer of one file from your computer to us. You open it, send the file in chunks and complete it; then the file is a media. Example: upl_5f0c2a9e41b7d3c8e6a1f094
Chunk
One piece of the file in an upload, 8 MiB unless you choose another size. Chunks are numbered from 0. Example: A 35 MB file is sent as 5 chunks: 0 to 4.
Media
A file we have received (by upload or import) and analysed. Jobs and previews are always made from a media. One media, many media. Example: med_4821
Job
One processing run of a media with your settings (for example restoration or colorization). It costs money and ends with a result. Example: job_3150
Result
The finished file a completed job delivers, which you download. Example: A001_C003 - Custom - job_3150.mov
Preview
A free before/after image of one frame with the settings you consider. Example: prv_3171
Keyframe
In a colorization job: a frame of a scene that is colorized first. After your approval its colours are carried to the other frames of the scene. Example: Frame 12 of scene 0.
Preset
A saved set of filters with an output format and size, by us or by you in the workspace. Example: pre_90 "Restore without Grain"
Filter
One processing step you can choose, for example noise removal or colorization. Example: medium_dust_and_scratches
Polling
Asking again and again, a few seconds apart, whether something has finished. Never faster than once a second; the Retry-After header tells you when to ask next. Example: Ask GET /jobs/job_3150 every second until status is completed.
ETag
A short fingerprint of a response, sent in the ETag header. Send it back in If-None-Match: if nothing has changed you get a short 304 instead of the whole answer, and a 304 never counts against your limits. Example: ETag: W/"4fba7bfc885cb47a48c0a537fd8ba09e"
Idempotency key
A random value (a UUID) you send with a request that creates something. If the response gets lost and you send the same request with the same key again, you get the first response again instead of a second job. Example: Idempotency-Key: 9b2f0c7e-3d4a-4f61-8a5e-2c1b7d9e0f43
Rate limit
The most requests you can send in a time, for example 120 a minute. Above it you get 429 Too Many Requests and wait Retry-After seconds. Example: RateLimit-Remaining: 118
Retry-After
A response header with a number of seconds: how long to wait before you ask again. Example: Retry-After: 1
Webhook
Instead of you polling us, our server sends a request to an address of yours when something happens (a job has completed). Example: A POST to https://your-server.example.com/recuro-webhook
Page and cursor
Long lists come in pages. Each page ends with next_cursor; send it as cursor to get the next page, until it is null. Example: GET /jobs?cursor=eyJiZWZvcmUiOjM1ODF9
UUID (universally unique identifier)
A random 36-character value that is practically never the same twice. Python: uuid.uuid4(); PowerShell: [guid]::NewGuid(); shell: openssl rand -hex 16 (32 characters, as good). Example: 9b2f0c7e-3d4a-4f61-8a5e-2c1b7d9e0f43
SHA-256
A 64-character fingerprint of a file's content. If one byte changes, the fingerprint changes. Used to check that a file arrived whole. Example: sha256sum file.mov (Linux), shasum -a 256 file.mov (macOS), (Get-FileHash file.mov).Hash.ToLower() (PowerShell: Get-FileHash alone prints upper case - the case does not matter)
curl
A command-line program that sends HTTP requests. It comes with macOS, Linux and Windows 10/11 (there it is curl.exe). The -u option sends a key and secret, -H a header, -d a body, -o saves the answer as a file; -sS hides the progress meter but still shows curl's own error messages; --fail saves no error answer as a file (curl ends with exit code 22). Example: curl -sS https://www.recuromedia.com/api/1.0/health

Abbreviations

ShortMeaning
AIartificial intelligence
ASCIIthe basic set of 95 printable characters: English letters, digits, space and punctuation (no accented letters)
JPEG, PNGimage file formats: JPEG is smaller, PNG keeps every detail
NTSCthe American television standard of 29.97 frames per second
URLuniform resource locator: a web address such as https://www.recuromedia.com/
UTF-8the standard way to store text in any language
YAMLa text format for structured data, easier to read than JSON (used by openapi.yaml)
2Khere: a picture that is not HD and whose long side is at most 2048 and short side at most 1556 pixels (DCI 2K 2048 × 1080, 2K scope 2048 × 858, full-aperture film scans 2048 × 1556): our larger price tier and the largest picture we take
4Ka picture 2160 pixels high (3840 × 2160): we make it only by upscaling (output scaling 2160); 4K files cannot be uploaded
HDhigh definition - here: a picture whose long side (the larger of width and height) is at most 1920 and short side at most 1080 pixels, and that is not SD
SDstandard definition - here: a picture whose long side is at most 1280 and short side at most 720 pixels; priced as HD
CORScross-origin resource sharing: rules that let a program in a web view call another address
EUReuro
ETAestimated time of arrival: here, the estimated seconds until a job has finished
fpsframes per second
GiB, MiBbinary sizes: 1 MiB = 1,048,576 bytes, 1 GiB = 1,073,741,824 bytes. 1 KB here means 1,024 bytes; 1 MB (only in rounded sizes such as "a 35 MB file") 1,000,000 bytes
HMAChash-based message authentication code: a signature made with a secret key (used for webhooks)
HTTP, HTTPSthe protocol of the web; HTTPS is HTTP encrypted. This API takes HTTPS only
ISO 8601, ISO 3166standards of the International Organization for Standardization (ISO): 8601 for a date and time, here in UTC (2026-10-06T09:04:18Z); 3166 for two-letter country codes (SE)
MAMmedia asset manager: a system that stores and organises video files
MXF, MOV, MP4video file formats; ProRes, DNxHR, H.264 and HEVC are ways the picture is stored in them
NLEnon-linear editor: an editing application such as Adobe Premiere Pro, DaVinci Resolve or Avid Media Composer
PKCEproof key for code exchange: a one-time secret pair that proves the app that started a browser sign-in is the one that finishes it; S256 means the challenge is the SHA-256 of the verifier
RFCrequest for comments: the published standards of the internet, for example RFC 9457 for error responses
UTCcoordinated universal time: the time zone of every time in this API (London winter time)

How to read this page

  • One word per thing: a file is on your computer; after the upload it is a media on ours; a job processes a media; its finished file is the result. A request gets a response.
  • "Must" means the API refuses the request otherwise. "Can" means you are allowed to, but do not have to. "We recommend" means the API works without it, but your program is safer with it.
  • Placeholders are in capitals: replace YOUR_API_KEY, YOUR_API_SECRET and YOUR_FILE_SHA256 with your own. Every other id, token, code, hash, file name and size in an example is an example value too: use the ids (med_4821, job_3150, upl_...) and tokens (rma_..., rmr_...) you get back, the 6-digit codes of your e-mails, and your file's name, size and SHA-256.
  • Shell examples work as they are in bash (Linux, Git Bash on Windows) and in zsh (Terminal on macOS). They carry no # comments: zsh does not take comments in pasted lines. (Pasting your own comments into zsh? Run setopt interactivecomments first.)
  • Units: times in seconds, sizes in bytes, frames counted from 0, money in the currency it names (euros: EUR), dates and times in UTC.
  • In the reference, {job_id} in a path is a path parameter: write the id there without the curly brackets.

Your first call in 5 minutes

Three requests: one that needs no key (is the service there?), one with your key (who am I?), and one with a JSON body (what would a job cost?). None of them costs anything. The 5 minutes start once you have your API key and secret: we switch API access on first (What you need, steps 2 and 3).

On Windows (PowerShell)

Open PowerShell (press the Windows key, type powershell, press Enter). Type curl.exe, not curl: in Windows PowerShell 5.1, curl is another command (Invoke-WebRequest) that does not understand -u. curl.exe is part of Windows 10 (version 1803 and later) and Windows 11. Paste these lines one block at a time and press Enter:

PowerShell: health and who am I
# 1. Is the service there? (no key needed)
curl.exe -sS https://www.recuromedia.com/api/1.0/health

# 2. Your key and secret (replace both placeholders, keep the quotes)
$Key = "YOUR_API_KEY"
$Secret = "YOUR_API_SECRET"

# 3. Who am I? -u sends the key and secret (HTTP Basic). ${Key} needs its curly brackets
#    in front of the colon - "$Key:$Secret" would not work in PowerShell.
curl.exe -sS -u "${Key}:${Secret}" https://www.recuromedia.com/api/1.0/me

A JSON body in PowerShell: write it into a file and let curl send the file. Typing the JSON on the command line does not work in Windows PowerShell 5.1 - it removes the double quotes inside it, and the API answers InvalidJson. Write the file with Set-Content -Encoding ascii, as below. Do not write it with >, Out-File or -Encoding utf8: they put invisible bytes before the text (UTF-16, or a byte order mark), which the API also refuses as InvalidJson.

PowerShell: a quote
# 4. What would a 60-second HD restoration cost? (a quote: free, nothing is started)
Set-Content -Path quote.json -Encoding ascii -Value '{"planned": {"duration_seconds": 60, "width": 1920, "height": 1080}, "settings": {"filters": ["noise", "medium_dust_and_scratches"]}}'
curl.exe -sS -u "${Key}:${Secret}" -H "Content-Type: application/json" --data "@quote.json" https://www.recuromedia.com/api/1.0/quotes

Prefer PowerShell's own commands? Invoke-RestMethod turns the JSON response into an object you can read field by field. On a 4xx or 5xx response it stops with only "The remote server returned an error"; our code and detail are in $_.ErrorDetails.Message, which the catch below prints:

PowerShell: Invoke-RestMethod
$Basic = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${Key}:${Secret}"))
try {
    $Me = Invoke-RestMethod -Uri https://www.recuromedia.com/api/1.0/me -Headers @{ Authorization = "Basic $Basic" }
    $Me.plan.name          # Prepaid
    $Me.balance.amount     # 412.50
} catch {
    $_.ErrorDetails.Message
}

In the old Command Prompt (cmd.exe) write curl (it is curl.exe there), use the key and secret directly (-u "KEY:SECRET") and write every double quote inside a JSON body as \".

On macOS and Linux

Open Terminal (macOS: Applications > Utilities > Terminal). curl is already installed. Paste one block at a time and press Enter. First your settings for this terminal window - replace both placeholders and keep the quotes:

Terminal: your settings
API=https://www.recuromedia.com/api/1.0
AUTH="YOUR_API_KEY:YOUR_API_SECRET"

1. Is the service there? No key is needed:

Terminal: 1. Is the service there?
curl -sS "$API/health"; echo

2. Who am I? -u sends the key and secret (HTTP Basic):

Terminal: 2. Who am I?
curl -sS -u "$AUTH" "$API/me"; echo

3. What would a 60-second HD restoration cost? A quote: free, nothing is started:

Terminal: 3. A quote
curl -sS -u "$AUTH" "$API/quotes" -H "Content-Type: application/json" \
     -d '{"planned": {"duration_seconds": 60, "width": 1920, "height": 1080}, "settings": {"filters": ["noise", "medium_dust_and_scratches"]}}'; echo

The single quotes around the JSON keep its double quotes intact. ; echo only adds a line break after the response. -sS hides curl's progress meter but still shows curl's own error messages (for example a mistyped address). To read a long response more easily, add | python3 -m json.tool before ; echo (Python 3 must be installed).

What you should see

Request 1 answers {"status":"ok","api_version":"1.0", ...}. Request 2 answers your account as one long line of JSON - shortened and spread out here:

Response of who am I (shortened)
{
  "user": { "id": "usr_1042", "first_name": "Jane", "last_name": "Smith", "email": "jane@example.com", "...": "..." },
  "company": { "id": "cmp_311", "name": "Example Films Ltd", "account_type": "client" },
  "plan": { "code": "prepaid", "name": "Prepaid", "watermark": false, "billing": "balance" },
  "balance": { "amount": "412.50", "currency": "EUR" },
  "...": "..."
}

Check: user.email is your address, plan.name your plan and balance.amount what you can spend in euros. Every field is explained in the Me object. Request 3 answers a quote: "price": {"amount": "25.00", "currency": "EUR"} (60 seconds at €25 per minute) and "covers": true when your balance pays for it. That is all it takes: you have used the API. Next, the whole workflow.

If you see an error instead

Every error of the API is JSON with a code - the word to look up in the error table - and a sentence (detail). The ones you are most likely to meet now:

What you seeWhat it meansWhat to do
Parameter cannot be processed because the parameter name 'u' is ambiguous (a red PowerShell error)You typed curl in Windows PowerShell 5.1, which runs another command (Invoke-WebRequest).Type curl.exe.
"code":"Unauthorized" (401)The request had no key: -u is missing, or - in Windows PowerShell - $Key and $Secret are empty (a new window forgets them).Set $Key and $Secret again and repeat with -u.
curl stops and asks Enter host password for user '':macOS and Linux: AUTH is empty - a new terminal window forgets the settings.Press Ctrl+C, paste the settings block (API= and AUTH=) again, then the request.
"code":"InvalidCredentials" (401)The key or the secret is wrong - often a space or a missing character from copying.Copy both again. Secret lost: create a new one (What you need, step 3). After 20 wrong tries in 15 minutes your address is blocked for a while: wait.
"code":"ApiAccessDisabled" (403)API access is not switched on for your user.Contact us (What you need, step 2).
"code":"InvalidJson" (400)The body of request 3 is not valid JSON - on Windows the quotes were removed, or the file starts with a byte order mark.Follow the Windows steps exactly: the JSON in a file, -Encoding ascii, --data "@quote.json".
curl: (3) URL rejected: No host part in the URLAPI is empty: a new terminal window forgets the settings.Paste the settings block again, then the request.
curl: (6) Could not resolve host or curl: (7) Failed to connectYour computer cannot reach our server: a typing error in the address, no internet, or a company firewall or proxy.Check the address; try it in a browser (/api/1.0/health shows the JSON); ask your IT department about a proxy (curl option -x).
Nothing at all is printedAn example copied with -s instead of -sS hides curl's own error messages.Run it again with -sS (or -v, which shows every step) to see what went wrong.

The core workflow

Every job takes the same way through the API: upload, job, download. The workflow page shows every request of it with its response, in the shell and as a complete Python program.

  1. Upload the file: open an upload, send the file in chunks and complete it; we analyse the media (steps 2 to 4).
  2. See the price, then start the job with the filters you choose (step 5).
  3. Follow the job until it is done (step 6) - a colorization waits for your review of its keyframes first (step 7).
  4. Download the result (step 8) and clean up (step 9).

Questions and answers

Do I need to be a programmer?
No, to try it: the first call and the workflow are commands you paste into a terminal. To connect your own systems for good you, or someone in your team, will write a small program; restore.py is a complete one to start from.
Does trying the API cost money?
No. GET requests, quotes, uploads, analyses and previews are free. Only POST /jobs takes money, and it gives it back if the job fails or is cancelled. A quote shows the price first.
Is there a test system?
No. Use short clips (a few seconds) while you build: a 2-second HD restoration costs €0.80.
How do I know when a job is done?
Poll GET /jobs/{job_id} (at most once a second, as Retry-After says) until status is completed, or let us call your server (webhooks, API keys only).
My program crashed during an upload. Do I have to start again?
No: ask GET /uploads/{upload_id} which chunks are missing and send only those (Resuming an upload), within 7 days.
I sent POST /jobs and got no response. Did it start?
Send the same request with the same Idempotency-Key again: you get the job if it started, and it starts once if it did not. Without a key, look in GET /jobs before you send it again.
Which picture sizes can I upload?
Up to 2K: 2048 × 1556 pixels, either way round (HD, DCI 2K and full-aperture 2K scans). A 4K file is refused (PictureTooLarge) - export it at 2K or HD first. You can still get a 4K result: output scaling 2160 upscales the film. Read the limit from limits.picture of GET /health rather than writing the numbers into your program.
Can I process only a part of a long film?
Not yet: cut the part you want (with a second before and after) and upload that part.
Can I run the same media with other settings?
Yes, one job at a time: download the first result, delete that job (DELETE /jobs/{job_id}), then start the new one.
Why is my key refused right after I created it?
Check that you copied the secret completely (32 characters) and that you send key:secret with a colon between them. A secret is shown once; if you are not sure, delete it and create a new one.
Which programming languages can I use?
Any that can send HTTPS requests: Python, JavaScript (Node.js), PHP, C#, Java, Go, and others. openapi.yaml can generate a client for many languages (for example with OpenAPI Generator).
Whom do I ask?
Contact us, with the request_id of the request in question.

Where to go next

Getting Access
API keys for your own program, signing in inside an app or in the browser, tokens and scopes.
The Workflow
Every request from the upload to the download, step by step, in the shell and in Python.
Concepts and Conventions
Uploads, filters, previews, jobs, keyframes and billing; headers, formats, polling, idempotency and the limits.
Reference
Every endpoint with its parameters, its response, an example and the error codes it can answer.
Objects
Every object of the responses, field by field.
Errors
The HTTP statuses and every error code, with what your program should do.
Webhooks
Let us call your server when a media is ready or a job is done, and check the signature.
openapi.yaml
The whole API in machine-readable form (OpenAPI 3.1).