compress.target takes a media URL and a target, then searches candidate encodes for the smallest file that still clears the bar. Images are scored with SSIMULACRA2, video with VMAF, and every job returns a report: the achieved score, the compression ratio, and what the search did.
Live. Accepts video, image, and audio URLs.
Compress an image for the web
output.file and the report in output.data. Images finish in about 10 seconds, audio in about 1, video takes longer because every search probe is a real encode plus a VMAF pass.
What does the report show?
Every completed job carries the search’s account of itself:status is the honest part. ok means the target was met. infeasible_quality means even the best encode could not reach the score you asked for, and you got the best effort. infeasible_size means the byte cap and the quality floor could not both hold. skipped_no_improvement means no encode beat your original, so the original came back untouched with compressionRatio: 1 and verdict: "passthrough". Set forceReencode: true to get the re-encode anyway. A passthrough result reports achievedScore: null and qualityLevel: null, because the file you got back was never encoded. A dryRun predicts passthrough the same way.
scoredOn says where the score was measured. full means the delivered file. sample means a long video was searched on samples taken across its length, and the score is the samples’ score.
verdict maps the score to a label. For images: 90+ is visually_lossless, 70+ is high, 50+ is medium, below that low. For video on VMAF: 94+, 90+, and 80+ (acceptable).
Should I target a size or a quality?
target takes one of four shapes:
The tier thresholds per medium: images clear SSIMULACRA2 90 / 85 / 78, video clears VMAF 94 / 91 / 86. Video is scored with the VMAF NEG model, which reads one to two points under the standard VMAF scale, so 94 here sits near 96 on the numbers you may know. Audio has no metric loop. Tiers map to a channel-aware Opus bitrate ladder, so a mono voice track gets half the budget of stereo music.
A byte budget on H.264 video runs one two-pass encode at a planned bitrate, with no search. When the budget is too small for the source resolution, the video steps down to a smaller frame, because a sharp small picture beats a blocky large one. The result reports
probes: 1 and qualityLevel: null, and the audio is re-encoded to AAC to fit. A video that already fits the budget comes back untouched. Other codecs and images still search for the best level under the cap.
A JPEG headed for JPEG XL is also repacked with its pixels untouched, and the smaller of that and the lossy search ships. A repacked result reports mode: "lossless" with no score.
Which codecs does it use?
Withcodec: "auto", the pick follows compatibility:
Or pin one:
av1, hevc, h264, vp9, avif, jxl, jpegli, webp, png, opus, aac, mp3, flac, alac. The lossless-only codecs (png, flac, alac) always encode losslessly; combined with a byte budget, the report tells you whether the lossless file fit.
for: "archive" skips the lossy ladder entirely and produces a lossless master: PNG for images, FFV1 in MKV for video, FLAC for audio.
Can I predict the result without encoding?
dryRun: true runs the search and returns the report with a predicted outputBytes, but writes no file and uploads nothing. output.file is null, the same shape as a data job. Use it to quote a compression before committing to it.
Parameters
string
Delivery posture.
"web" resolves the defaults to sRGB + stripped metadata; "archive" resolves to lossless with everything preserved. Any explicit field you set wins over the profile.string | number | object
default:"balanced"
The compression target. One of the four shapes above.
string
default:"broad"
Decode reach
codec: "auto" must guarantee: "modern", "broad", or "legacy".string
default:"auto"
Output codec, or
"auto" to follow the compatibility ladder.string | number | null
default:"null"
Cap the longest edge: a pixel count or a rung like
"1080p" / "720p". Images downscale before encoding; video scales in-encode and the VMAF score is measured against the full-resolution source.string
default:"preserve"
Transparency:
"preserve", "flatten" (onto white), or a "#RRGGBB" fill color.string
default:"colorOnly"
"all" keeps everything, "colorOnly" strips EXIF and GPS (ICC preservation is best-effort), "none" strips all.string
default:"balanced"
"fast", "balanced", or "thorough" — trades encode time for extra compression on every codec that has a speed knob.boolean
default:"false"
Re-encode even when the result would not beat the original.
boolean
default:"false"
Return the predicted report without producing a file.
object
Video-only policies.
audioTracks: "all" (default), "primary", or "none". subtitles: "strip" (default) or "preserve" (best-effort copy; fails when the output container cannot hold the codec).object
Audio-only policies.
loudnessLufs: normalize to an integrated LUFS target such as -14, or null (default) for off. channels: "preserve" (default), "mono", or "stereo".object
Expert knobs.
metric: "auto" (default), "ssimulacra2", or "vmaf" to override the scoring oracle. floor: a hard minimum score the output must never drop below, in any mode.How does the search pick an encode?
The search is a binary search over the encoder’s quality scale, so a job costs about 7 probe encodes at most, each scored against the source. Videos longer than 12 seconds probe on three 2-second samples instead of the full file, and only the final encode runs over everything. A byte-budget search always encodes the full file, because the budget is exact. Compression ratios depend on the input. A camera original shrinks hard; a file that is already well-compressed often comes back asskipped_no_improvement, which is the job telling you the original was already the smallest honest answer.
Errors
A malformed request (an unknown codec name, a removed tuning knob, two target goals at once) returnsVALIDATION_ERROR on POST /jobs before anything is billed. A URL that resolves somewhere Rendobar won’t fetch from returns INPUT_URL_BLOCKED.
After the job starts, failures carry the standard error shape. INPUT_FETCH_FAILED follows the same retryable rules as ffprobe. An infeasible target is not an error: the job completes with status: "infeasible_quality" or "infeasible_size" in the report and the best file the search found.
See also
- Job output: the output shape every job returns
- ffprobe: inspect a file before deciding how to compress it
- FFmpeg: full manual control when you want to write the command yourself
- Compose: render the video you are about to compress
- Upscale an image: enlarge rather than shrink
- Plan limits: file-size caps and timeouts per plan
- Webhooks: receive
job.completedinstead of polling