Errors

When a request does not work, the response has an HTTP status code of 400 or more and a JSON body in the standard form of RFC 9457 (application/problem+json). Your program should look at the code - it never changes - not at the wording of the sentence:

Response: 402 Payment Required
{
  "type": "https://www.recuromedia.com/menu.developer.php#BalanceInsufficient",
  "title": "Balance too low",
  "status": 402,
  "code": "BalanceInsufficient",
  "detail": "Your balance is too low for this job. Please top up to start it.",
  "retryable": false,
  "request_id": "req_6c1f0b9e2d4a",
  "details": { "account_plan": "Prepaid", "required_eur": "12.40", "balance_eur": "3.10" }
}
The fields of an error
FieldWhat it is
codeThe error code: one word that says what happened (the table below). Test this in your program.
statusThe HTTP status code again.
titleA short title of the code.
detailA sentence about this case that you can show to a person.
retryabletrue: the same request, unchanged, can work later - after Retry-After when the response has that header, or once the step the code's row names is done (for example a new token after TokenExpired, an open upload completed or deleted after TooManyUploads). false: the request itself must change.
request_idThe request's id (also the Recuro-Request-Id header). Quote it when you contact us.
detailsFacts of this case, where useful - for example {"missing": [4]} for UploadIncomplete, {"job_id": "job_3150"} for JobExistsForMedia, {"field": "settings"} for ValidationFailed. Missing when there are none.
typeA link to the code's row in the table below.

A simple way to handle errors: on 429 and 503 with a Retry-After header wait that many seconds and send the same request again - except PreviewLimitReached (it can say up to a day: start the job without a preview, or wait) and TooManyUploads (complete or delete an open upload first); a 503 without Retry-After (FeatureUnavailable, not_enabled or not_configured) means the feature is not offered - do not retry. On 401 TokenExpired get a new token and send it again; on 500 and 502 try again after a few seconds, at most 3 times; on any other error stop and show detail - repeating it unchanged will not help.

HTTP status codes

StatusWhat it means for youWhat to do
200 OKThe request worked; the body holds the answer.Read the body.
201 CreatedSomething new was made (an upload, a job, a sign-up, a link). The Location header, where present, is its address.Keep the id of the body.
202 AcceptedWe took the request and work on it now (an analysis, a preview, a job that runs on). The body shows the state at this moment.Poll the thing until it has finished.
204 No ContentThe request worked and there is nothing to send back (a delete, a sign-out).Nothing.
206 Partial ContentThe part of a file you asked for with Range.Add it to the part you have.
304 Not ModifiedNothing has changed since the ETag you sent in If-None-Match; there is no body.Keep using what you have; poll again after Retry-After.
400 Bad RequestYour request is wrong: a missing or wrong field, broken JSON.Correct it as code and detail say; repeating it unchanged will not help.
401 UnauthorizedWe do not know who you are: no key, a wrong key or secret, an expired or ended token.Check the credentials; get a new token.
402 Payment RequiredThe money or seconds are not there, or an order is not paid.Top up or pay on the website.
403 ForbiddenWe know who you are, but you are not allowed to do this.See the error code; often it needs an API key, another plan or API access.
404 Not FoundThe address or the id does not exist (or is not yours).Check the address and the id.
405 Method Not AllowedThe address exists with other HTTP methods.Use a method of the Allow header.
409 ConflictThe thing is not in the right state for this request yet or any more (still being analysed, already started, not waiting for keyframes).See the error code: wait, or change what you do.
410 GoneIt existed but has expired or been removed.Start again (a new upload, a new sign-in).
413 Content Too LargeA file, chunk or body is too large.Send less.
415 Unsupported Media TypeA file type or a body format we do not take.Use a supported format.
416 Range Not SatisfiableA chunk number or a byte range outside the file.Use a number or range inside it.
422 Unprocessable ContentThe request is well-formed but the values do not go together (settings, frames, keys).Change the values as detail says.
426 Upgrade RequiredOne of our apps is too old.Update the app.
429 Too Many RequestsToo many requests, or a daily or parallel limit is reached.Wait the Retry-After seconds, then try again.
500 Internal Server ErrorA fault on our side.Try again after a few seconds; contact us with the request_id if it repeats.
502 Bad GatewayA cloud server of ours does not answer.Try again after a minute.
503 Service UnavailableA feature or server is not available at the moment, or a server is starting.Wait the Retry-After seconds; without one, the feature is not offered.
507 Insufficient StorageYour storage (or the server's disk) is full.Delete media and jobs you no longer need.

Error codes

Every code the API answers with, its HTTP status, whether a retry can help (Retry: the error's retryable, explained above), what it means for you and what to do.

CodeHTTPRetryWhat it means for youWhat to do
ValidationFailed400noA field of your request is missing, has the wrong type or a value that is not allowed - also source or client_data larger than 4 KB (4,096 bytes) as JSON. details.field names the field.Correct the field named in details.field as detail describes; do not send the same request again unchanged. source and client_data hold at most 4 KB of JSON each.
InvalidJson400noThe body is not a JSON object (a syntax error, a list instead of an object, or a byte order mark before the JSON).Check the JSON (for example with an online JSON checker). On Windows PowerShell write the file with -Encoding ascii (see Windows).
ImportUrlFailed400noWe could not download the import address: it answered with another HTTP status than 200 (details.http_status), it could not be reached, or it redirected more than 5 times.Open the address in a browser that is not signed in anywhere: it must download the file directly. Use a direct download link.
ChunkSizeMismatch400noA chunk does not have the right number of bytes: details.expected says how many it needs (every chunk is chunk_size bytes, the last one the rest of the file).Cut the chunk again at index × chunk_size with chunk_size bytes and send it again.
ChunkDigestMismatch400yesA chunk does not match its Repr-Digest: it was damaged on the way.Send the same chunk again.
Unauthorized401noThe request has no Authorization header, a malformed one, or an access token we do not know (also one replaced by a refresh).Send Authorization: Basic with your key and secret (curl -u KEY:SECRET) or Authorization: Bearer with a valid access token. On Windows PowerShell use curl.exe, not curl.
InvalidCredentials401noThe API key or the secret is wrong (the response never says which), or the e-mail address or password, a refresh token or a PKCE verifier is wrong or expired.Copy the key and the secret again (no spaces, the secret has 32 characters). Lost the secret? Create a new one (Administrator Tools). After 20 failures in 15 minutes your address is blocked for a while (RateLimited).
TokenExpired401yesThe access token is older than an hour.Get a new token: refresh it (apps) or exchange the key again (API keys), then repeat the request.
TokenRevoked401noThe sign-in was ended: signed out, signed out under Connected Apps, a password change or reset, two-factor authentication switched on or off, or the account was deactivated.Sign in again.
RefreshTokenReused401noA refresh token was used again more than 60 seconds after it had been replaced. For safety the app is signed out: someone else must have a copy of the token.Sign in again, and store each new refresh token at once (an old one works only 60 seconds longer).
InvalidCode401noAn e-mailed or authenticator code is wrong or expired. details.expired true means the code cannot be used any more; an e-mailed sign-in code also tells details.tries_left.Ask the person to type the code again; with details.expired true ask for a new code.
BalanceInsufficient402noYour prepaid balance does not cover the job - or, for a keyframe change (Modify), the EUR 0.05 each of its tries needs before it starts (details.feature keyframe_modify). details.required_eur is what the job or change needs, details.balance_eur what you have.Top up your balance on the website (links.top_up of GET /me), then start the job again.
SecondsLeftInsufficient402noThe seconds left of your Trial do not cover the job (details.seconds_left).Upload a shorter part. On a Trial, upgrade to Prepaid in the workspace (links.upgrade of GET /me) and top up.
ApiAccessDisabled403noAPI access is not switched on for this user.Contact us to have API access switched on for this user.
AccountDeactivated403noThe account is deactivated.The company administrator or we have to activate the account again; contact your administrator or us.
AccountNotValidated403noThe account's e-mail address is not confirmed yet. We have e-mailed a new code; details.signup is the id to send it with.Send the e-mailed code with details.signup to POST /auth/signup/verify.
InsufficientScope403noThis kind of sign-in is not allowed to do this. details.required names the scope it lacks (for example webhooks); details.credential api_key means only an API key can do it (URL imports).Use an API key for webhooks and URL imports; an app uploads the file instead.
TrialLimitExceeded403noThe media is longer than the Trial allows for this kind of job (details.max_seconds).Upload a shorter part of the film, or upgrade to Prepaid in the workspace (links.upgrade of GET /me), then top up.
AppLoginDenied403noThe person pressed Deny in the browser sign-in.Stop: the person does not want this app signed in. Offer to start again.
SignupClosed403noThis site does not take new sign-ups at the moment.Sign up on the website later, or contact us.
NotFound404noNo such endpoint, or no such id - also an id that belongs to another user, and anything you have deleted.Check the address and the id. Ids are case-sensitive and include their prefix (med_, job_ ...).
MethodNotAllowed405noThe address exists, but not with this HTTP method. The Allow header lists the methods it takes.Use one of the methods in the Allow header (see the reference for the endpoint).
MediaNotReady409yesThe media is still being analysed (details.status queued or analyzing), or its analysis failed (failed).Wait: poll GET /media/{media_id} until status is ready. A failed media will not become ready: delete it and upload again.
MediaBusy409yesThe same upload is being completed by another request right now.Wait the Retry-After seconds (5) and complete again: you get the same media.
JobExistsForMedia409noThe media has a job already (details.job_id). A media has one job at a time.Use the existing job (details.job_id), or delete it first if you want other settings.
JobNotCancellable409noThe job has ended already (details.status).Nothing to do: the job has ended. Delete it if you do not need it.
JobNotAwaitingKeyframes409noThe job is not waiting for its keyframes: it is not a colorization job, it has not reached the keyframes yet, or they were approved already.Check the job's status: keyframes can be read and changed only while it is needs_action.
ModifyInProgress409yesA keyframe of the job is being changed (details.frame). One change per job runs at a time, and approving waits for it.Wait the Retry-After seconds (1) and poll the keyframe of details.frame until its modify.state is done or failed; then try again.
ModifyLimitReached409noNo longer answered: since 24 Oct 2026 the one limit of Modify is the tries a keyframe keeps at a time (KeyframeTriesFull). Kept in this list for programs that still handle it (it said the plan's tries were used up: details.frame, details.scene, details.limit, details.remaining, details.per).Not answered any more. From an older server: choose one of the keyframe's versions instead (PUT /jobs/{job_id}/keyframes, version) - that stays free.
PreviewsBusy409yesTwo of your previews are being made already (details.active).Wait until one of them is ready or failed (the Retry-After says 20 seconds), or delete one.
PreviewNotReady409yesThe preview's images are not made yet (details.status).Poll GET /previews/{preview_id} until it is ready, then fetch the image.
UploadIncomplete409noChunks are missing: details.missing lists them (at most the first 1,000).Send the chunks in details.missing, then complete again.
UploadNotOpen409noThe upload has been completed; it takes no more chunks.Nothing to send: the file is a media already - media_id of GET /uploads/{upload_id} within a day of the completion; later find it with GET /media.
ResultNotReady409yesThe job has no result yet (details.status: queued, running or needs_action).Poll GET /jobs/{job_id} until status is completed, then download.
NoResult409noThe job failed or was cancelled (details.status): it has no result and will never have one.Read the job's error.message. Delete the job, then start a new one if you want to try again.
AppLoginExpired410noThe browser sign-in has expired (after 15 minutes) or was cancelled.Start a new browser sign-in.
AppLoginAlreadyRedeemed410noThe browser sign-in's tokens have been collected already (they are handed out once).Use the tokens of the first successful poll; to get new ones, sign in again.
UploadExpired410noNo chunk arrived for 7 days: the upload was removed.Open a new upload and send the whole file again.
ResultExpired410noThe result has been deleted: results are kept 60 days after the job finished.Start a new job of the media (delete the old job first), if the media still exists.
ApiActionRetired410noA request in the form of the old API (one address with an action field, before 6 Oct 2026).Change your program to this API (the reference below).
ChunkTooLarge413noA chunk is larger than 32 MiB.Use a chunk_size of at most 33,554,432 bytes.
FileTooLarge413noThe file is larger than 50 GiB.Upload the film in parts of at most 50 GiB.
PayloadTooLarge413noA JSON body is larger than 1 MiB (1,048,576 bytes).Send a smaller body. A file goes in chunks (PUT /uploads/{upload_id}/chunks/{index}), never in a JSON body.
MediaTypeUnsupported415noThe file name does not end in .mov, .mp4, .mxf, .mkv, .mpg, .mpeg, .wav or .mp3.Convert the file into one of these formats (for example MOV) and upload that.
ContentTypeUnsupported415noA body that is not JSON (its Content-Type names another format) where JSON is expected.Send the body as JSON with Content-Type: application/json.
ChunkOutOfRange416noThe chunk number is not one of the upload's: they run from 0 to details.chunk_count - 1.Use chunk numbers from 0 to chunk_count - 1 of the upload.
RangeNotSatisfiable416noThe Range of a download lies outside the file (details.size).Ask for a range inside the file (details.size bytes), or download without Range.
OptionConflict422noSettings that cannot be combined, or that cannot be made for this media; detail says which, details.conflicts lists them where there are several.Change the settings as detail says (for example another output format) and send the request again.
RangeInvalid422noA frame or time outside the media.Use a frame from 0 to frames - 1, or a time from 0 to duration_seconds, of the media.
RangeNotSupported422noA range (a part of a media) was sent: parts are not supported yet.Leave range out and upload only the part you want processed.
SceneNeedsKeyframe422noYour choices would leave a scene without any keyframe (details.scene, counted from 0).Keep at least one keyframe of that scene (excluded false).
IdempotencyKeyReused422noThe Idempotency-Key was used within the last 24 hours for a request with another body.Use a new key for a new request; use the same key only to repeat exactly the same request. In the shell examples: unset and the key variable of the example (UPLOAD_KEY and JOB_KEY of the workflow - its step 0 does it; KEY_UPLOADS, KEY_IMPORTS, KEY_PREVIEWS, KEY_JOBS or KEY_MODIFY of the reference) makes the next run send a new one.
FrameRateChangedAtIngest422noThe media's 3:2 pulldown was removed at the analysis (29.97 fps became 23.976 fps), so output.frame_rate source cannot give 29.97 fps back.Upload the file again with "ingest": {"keep_frame_rate": true}, or use output.frame_rate processed.
PictureTooLarge422noThe picture is larger than we take (never on the Free plan): details.width and details.height (the picture as found or as you declared it), details.max_long_side and details.max_short_side (the limit, either way round). 4K is only made by upscaling (output scaling 2160).Export the film at 2K or HD (at most max_long_side × max_short_side) and upload that. For a 4K result, choose output scaling 2160.
PictureTooLargeForFilter422noReconstruct cannot work on a picture this large on our graphics cards: details.filter is reconstruct (the picture itself) or super_resolution (Reconstruct with output scaling, which doubles a picture up to 1024 × 576), details.width and details.height are the picture the filter would make, details.max_width and details.max_height the largest picture of the same shape our cards take for it and details.max_mpixels its megapixels (all three null when we cannot tell). Checked by POST /quotes and POST /jobs before anything is charged, so a job never fails for it halfway. Not on the Free plan: its job starts, and a task too large for every card fails in our processing.Choose settings without reconstruct (for super_resolution: no output scaling), or export the film smaller - at most max_width × max_height - and upload that.
SignupInvalid422noThe sign-up form has errors: details.errors lists each with its field, a code (FreeEmailAddress - production only, InvalidEmail, WeakPassword, Required, InvalidText - a name or company with an invisible character such as a tab or a zero-width space, or longer than its limit; any other character, åäö, emoji and every punctuation mark included, is fine, CountryNotServed - a country under sanctions, TermsNotAccepted) and a sentence (detail).Show each entry's detail next to its field and let the person correct it.
WeakPassword422noThe new password is too weak: it needs at least 10 characters.Choose a password of at least 10 characters.
ClientVersionUnsupported426noOne of our apps is older than the lowest version still accepted (details.minimum_version).Update the app to details.minimum_version or later (the Downloads page).
RateLimited429yesToo many requests in a minute (details.limit per details.window_seconds), too many failed sign-ins or e-mailed codes, or a request with an Idempotency-Key waited more than 30 seconds for an earlier one of yours.Wait the number of seconds in the Retry-After header, then send the request again. Slow down your requests (see Limits).
SlowDown429yesA browser sign-in was polled faster than its interval.Poll every details.interval seconds from now on.
TooManyParallelChunks429yesMore than 4 chunks of one upload, 16 of one credential or 32 of one user are on their way at the same time.Wait 1 second (Retry-After) and send the chunk again; send fewer chunks at the same time.
TooManyUploads429yesYou have 10 uploads open.Complete or delete one of your open uploads (Retry-After says 60 seconds).
PreviewLimitReached429yesToday's free previews of this kind are used up (details.resets_at says when the next one is free).Wait until details.resets_at (the Retry-After header gives the seconds), or start the job without a preview.
InternalError500yesSomething went wrong on our side.Try again after a few seconds. If it happens again, contact us with the request_id.
CloudServerUnreachable502yesThe cloud server that holds this media or job does not answer.Try again after a minute. If it lasts, contact us with the request_id.
ExchangeRateUnavailable503yesYour prices are in another currency than euros and today's exchange rate is not there yet.Try again after the Retry-After seconds (3600).
CloudServerStarting503yesThe cloud server that holds this media or job is being started.Wait the Retry-After seconds and send the request again.
FeatureUnavailable503yesThe feature is switched off on this site or not available at the moment (GET /health features). A keyframe change says why in details.reason: not_enabled (this site does not offer it; no Retry-After), not_configured (this site's own wording for changes is not set up; no Retry-After) or no_worker (no service can make it now; Retry-After: 60).With a Retry-After: try again after it. Without one (keyframe changes: not_enabled or not_configured): the feature is not offered here - do not retry, hide it in your program. (retryable says true for this code in both cases; go by Retry-After and details.reason.)
InsufficientStorage507noYour storage is full (or the server's disk), counting what your open uploads still need.Delete media and jobs you do not need (download the results first), or open fewer uploads at once.