realpng
Docs

Errors

Every error the realpng API returns, with its status, its code, what it means and what to do about it.

Every failed call answers with an HTTP status and a JSON body whose code is stable. Branch on code, never on message, which is written for people and can change.

Error shape

Check an image on a private network
curl -X POST "https://realpng.com/api/v1/audit" \
  -H "X-API-Key: $REALPNG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "http://192.168.1.20/photo.png"
  }'
400 Bad Request
{
  "code": "invalid_url",
  "message": "Use a public http or https URL on its default port."
}
FieldTypeDescription
codestringAlways present. A stable snake_case name for the error.
messagestringAlways present. One sentence for a person. Some messages include a link, such as where to upgrade.
detailobjectOnly on some errors. See Error details.

Some 429 and 503 errors also send a Retry-After header with the number of seconds to wait. Run many images shows a loop that waits for it.

Errors by status

Each table lists every code that status carries. GET /optimize/{id}/file and GET /optimize/{id}/image need no key, so they never answer 401, 402 or 429.

The PUT to an upload_url goes to storage, not to realpng, so its refusals are not in this shape. Handle errors covers them.

400 Bad request

CodeWhat it meansWhat to do
invalid_bodyThe body is not a JSON object. On POST /uploads, size is missing or not a positive whole number, or content_type is missing.Send a JSON object. For an upload, send the file's exact size in bytes and its content_type.
invalid_imageThe body sets none or more than one of image_url, image_base64, image_upload_id, video_url, video_upload_id, audio_url and audio_upload_id. Also sent when an upload id is not a UUID or the image is empty.Send exactly one file field. An upload id is the id from POST /uploads.
invalid_urlimage_url, video_url or audio_url is not http or https, is on a port other than the default, includes a user name or password, or points at a private, loopback or link local address, by IP, through DNS or through a redirect. Also a callback_url that is not https, is on a port other than the default, includes a user name or password, or is a private IP address.Use a URL anyone on the internet can open, or upload the file.
image_unreachableThe image URL answered with a status other than 200, took too long to answer, or could not be fetched.Check that the URL opens without a login or cookies. If the host blocks automated requests, upload the file instead.
video_unreachableThe video URL answered with a status other than 200, took too long to send the whole file, or could not be fetched.Check that the URL opens without a login or cookies. A large video goes faster as an upload.
audio_unreachableThe voice clip's URL answered with a status other than 200, took too long to send the whole file, or could not be fetched.Check that the URL opens without a login or cookies, or upload the file.
invalid_callbackPOST /optimize only. callback_url is not a string, or its host resolves to no address or to a private one.Pass a public https URL.
batch_unsupportedMCP only. The request body is a JSON-RPC batch, an array of messages.Send one JSON-RPC message per request.
invalid_parameterseconds is not a positive number. Otherwise the engine refused our request to it, which points to a fault on our side, not in your call.Send a video's length in seconds, or leave seconds out. For any other cause, retry once. If it keeps failing, email hello@realpng.com.

401 Unauthorized

CodeWhat it meansWhat to do
invalid_keyThe API key is missing, malformed, unknown or revoked.Send a valid key in the X-API-Key header or as Authorization: Bearer. See Authentication, or create a new key at /account#keys.

402 Payment required

CodeWhat it meansWhat to do
plan_requiredYour plan has no API access. The message names the plan.Upgrade your plan. See plans without API access.
payment_failedThe last payment for your plan failed.Update your card at /account#billing. API access resumes once the payment goes through.
quota_exceededThe credits left this period do not cover this call. Only on POST /audit and POST /optimize. detail has the numbers.Wait until detail.resets_at, or upgrade your plan. The refused call costs nothing.

404 Not found

CodeWhat it meansWhat to do
not_foundGET /optimize/{id} found no optimization with that ID on your account, or it has expired. GET /audit/{id} answers the same for a video check. GET /optimize/{id}/file and GET /optimize/{id}/image answer the same for an unknown or expired optimization, and until the optimization has succeeded.Check the ID. Poll until its status is succeeded before you download.
unknown_jobThe optimization or video check has expired. It means the same as not_found, so treat both as gone.Start a new one. See how long an optimization lasts.
upload_not_foundPOST /audit or POST /optimize with image_upload_id, video_upload_id or audio_upload_id. Nothing was uploaded to that ID, the upload was already used, or it expired.Request a new upload and PUT the file again. See Use each upload once.

413 Content too large

CodeWhat it meansWhat to do
image_too_largeThe image is over the size limit for how you sent it. image_base64 has a lower limit than image_url and uploads. Also sent when the size you give POST /uploads is over the limit, or when a JSON request body is too large, including over MCP.Send a large file as an upload. See Image limits.
too_many_pixelsThe image has more pixels, width times height, than the call accepts. POST /optimize refuses it at once, never as a failed optimization. detail has the numbers.Resize the image to fit the pixel limit and send it again. An uploaded file needs a new upload.
video_too_largeThe video is over the size limit, sent by URL or as the size you give POST /uploads.Shorten or compress the video. See Video limits.
audio_too_largeThe voice clip is over the size limit, sent by URL.Compress the clip, or send it as an MP3. See Video limits.
video_too_longseconds is over the length limit. A video that runs past seconds, the length limit or the frame limit fails its job with the same code. detail has the numbers.Send a shorter video, or the right seconds. See Video limits.

415 Unsupported media type

CodeWhat it meansWhat to do
unsupported_mediaOn POST /uploads, content_type is not a supported image, video or audio type. On a check or optimization, the file is not a supported type, is animated, or cannot be decoded. A check also answers it for a video or voice clip sent in an image field, and POST /optimize for a voice clip.Convert the file to a supported type and send it again. Send a video as video_url or video_upload_id. An uploaded file needs a new upload.
audio_unsupportedThe voice clip has no audio that can be decoded. It fails the clip's job.Export the clip again as an MP3, WAV or M4A, then send it again.
video_unsupportedThe video has no video stream that can be decoded, or no usable frame rate. It fails the video's job.Export the video again as an MP4 with H.264, then send it again.

429 Too many requests

CodeWhat it meansWhat to do
rate_limitedYour account is over its plan's requests a minute.Wait the seconds in Retry-After, then retry.
too_many_jobsYour account already runs as many checks and optimizations as its plan's jobs at once allows. Only on POST /audit and POST /optimize.Wait the seconds in Retry-After, then retry. A slot frees up when a running check or optimization finishes.

500 Internal server error

CodeWhat it meansWhat to do
internal_errorSomething failed on our side.Retry with a short backoff. A failed check or optimization gives its credits back.
engine_errorThe engine failed without naming the error. It usually comes with status 500 but can carry another.Retry. If it keeps failing, email hello@realpng.com.

503 Service unavailable

CodeWhat it meansWhat to do
engine_unavailableThe engine could not be reached or did not answer in time, usually because it is starting up. Sends Retry-After.Wait the seconds in Retry-After, then retry.
model_unavailableThe detection models are not ready yet. No Retry-After.Retry after a few seconds. This refusal does not use up an upload.
optimizations_disabledOptimizing is paused. Checks still work. Only on POST /optimize. Sends Retry-After.Wait the seconds in Retry-After, then retry. The image is not read, so an upload stays usable.
tracking_failedWe could not record the optimization, so it was not charged.Start the optimization again. If you sent image_upload_id, upload the file again first.
uploads_unavailableImage storage is unavailable. This affects POST /uploads and images sent as image_url or image_base64, which are stored the same way.Retry later.
engine_credentials_missing, engine_region_missingA configuration fault on our side.Retry later. If it keeps failing, email hello@realpng.com.

Error details

Only these three detail shapes are stable. Other errors may include a detail object whose fields can change, so do not code against them.

quota_exceeded

FieldTypeDescription
requiredintegerCredits the call needs.
availableintegerCredits left this period, after the checks and optimizations in flight.
resets_atstring, ISO 8601 date-timeWhen credits reset, in UTC.

too_many_pixels

FieldTypeDescription
widthintegerThe image width in pixels.
heightintegerThe image height in pixels.
limitintegerThe most pixels, width times height, this call accepts. A pixel count, not megapixels.

video_too_long

FieldTypeDescription
secondsnumberThe video's length in seconds.
max_secondsnumberThe longest video this call accepts.

Errors in a failed optimization

An optimization that fails while it runs is not an HTTP error. GET /optimize/{id} and the webhook carry its error, with the same code and message fields, as When an optimization fails shows. A video or voice check's GET /audit/{id} carries its error the same way.

CodeWhat it meansWhat to do
job_timeoutThe optimization did not finish in time.Start a new optimization.
stage1_failedThe re-render step failed.Start a new optimization.
model_unavailableThe models could not load for this optimization.Start a new optimization.
engine_errorThe engine failed without naming the error.Start a new optimization.
job_failedAny other failure.Start a new optimization.
video_too_longThe video runs past seconds, the length limit or the frame limit.Send a shorter video, or the right seconds.
video_unsupportedThe video could not be decoded.Export it again as an MP4 with H.264.
audio_too_longThe voice clip runs past the length limit.Send a shorter clip.
audio_unsupportedThe voice clip could not be decoded.Export it again as an MP3, WAV or M4A.

A code this table does not list means the same thing: the optimization failed. Its credits are given back. A new optimization of an uploaded file needs a new upload.

Errors over MCP

  • Refusals before any tool runs answer as HTTP errors in the shape above: 401 invalid_key, 402 plan_required and payment_failed, 429 rate_limited, 400 batch_unsupported, and 413 image_too_large for an oversized request body.
  • A request the MCP transport itself cannot read gets a JSON-RPC error object instead of that shape. That covers a missing MCP Accept header (406), a Content-Type other than JSON (415) and a body that is not a JSON-RPC message (400).
  • Every error inside a tool call comes back as a tool result with isError: true and the error's message as its text. The code is not included.

Read tool errors shows what an agent sees.