Overview
All API requests are made to the base URL:
Free account
25
ops/mo · 3 without signup
Seller tier
300
operations / month
Pro tier
1,200
operations / month
Growth tier
5,000
operations / month
Max file size
75 MB
paid plans · 20 MB free
Without an account: 3 ops/month (IP-based). Free account: 25 ops/month. Paid API keys use per-key limits. Each image returned counts as one operation, however many transformations were applied to it. A request for multiple formats or sizes of one file returns multiple images and costs one operation per output.
Authentication
The free tier requires no authentication. API keys are generated from your dashboard and passed as a Bearer token.
Authorization: Bearer mchy_••••••••••••••••API keys are shown once at creation and cannot be retrieved again. Store them securely. You can regenerate a key from your dashboard at any time.
/v1/squish
Compress and/or convert a single image. Send the raw image bytes as the request body. Returns the processed image as a binary blob.
Query Parameters
type Output format.
One of: jpg · png · webp · avif · jxl
width Target width in pixels. Aspect ratio is preserved unless smartCrop is also set. Omit or set to 0 for unconstrained. For SVG input, the vector is drawn at the requested width.
height Target height in pixels. Omit or set to 0 for unconstrained.
quality Output quality (1–100). With smartCompress this is the quality of the subject: smart compression never lowers it. JXL maps linearly, so 70 is about distance 3.0. 100 is the best lossy setting, not lossless: see lossless.
smartCompress Saliency-guided background softening. The subject is left untouched and encoded at quality; the background is lightly low-passed so it costs fewer bytes (roughly 15–40% smaller, depending on format and how much of the frame is background). The background does come out slightly softer. Skipped when there is no clear subject, the subject fills the frame, the background was removed, HDR is being carried, or lossless is set: see X-Mochify-SmartCompress. Accepts 1 or true.
smartCrop alias: crop
Saliency-guided crop, centered on the detected subject. Requires both width and height. Accepts 1 or true.
removeBackground AI background removal. Output is PNG, WebP or AVIF with an alpha channel; JPEG outputs flatten to white. Available on every plan, including Free. Accepts 1 or true.
background Fill behind transparent areas when the output cannot keep them: every jpg output with transparency, such as an SVG. With removeBackground it also replaces the transparent background on png, webp, avif and jxl output. Otherwise those formats keep the alpha channel and ignore it.
One of: black · a six-digit hex color, with # written as %23 (%23F5F5F5) · transparent. Any other value, including color names and three-digit hex, fills white.
rotate Clockwise rotation in degrees.
Supported values: 90 · 180 · 270
stripExif Strip EXIF metadata from the output. Set to false or 0 to preserve metadata.
optimizeForWeb Progressive encoding plus 4:2:0 chroma subsampling for the smallest browser-delivered file. Accepts 1 or true.
brightness Exposure adjustment. Range -100 (darkest) to +100 (brightest). 0 is no change.
clarity Midtone contrast enhancement: makes images look crisper without affecting overall exposure. Accepts 1 or true.
hdr Ultra HDR / ISO 21496-1 gain map handling. 1 or true preserves a gain map the source already has and never invents one, so it is ignored if the source is not HDR. generate does that and synthesizes a gain map when the source is plain SDR.
Only jpg output can carry a gain map (jxl carries HDR by a different route; avif, webp and png cannot). Skipped when combined with brightness, clarity or removeBackground, since those change the base the gain map is a ratio to. Check X-Mochify-HDR for what was actually emitted.
lossless Pixel-exact output. Accepts 1 or true, overrides quality, and disables smartCompress softening.
Only jxl, webp and png can honor it: jpg and avif are rejected with a 400 rather than silently encoded lossy. A source that is already lossy (JPEG, AVIF, HEIC) is re-encoded at the highest lossy setting instead, since nothing can restore what that file already discarded. Expect the output to be larger than the input: lossless preserves pixels, not file size. Check X-Mochify-Lossless for what was actually emitted.
targetBytes Ceiling on the output file size, in bytes. An upper bound, never a size to pad up to: if the requested quality already fits, that encode is returned untouched.
No encoder can predict a compressed size without encoding, and no quality value maps to one: bytes at a fixed quality are a property of the pixels, spanning roughly 3× (JPEG) to 10× (AVIF) between a flat graphic and a detailed photo at identical dimensions. So this measures instead, typically 1–3 encodes, at most 5, extrapolating from each real measurement rather than bisecting. Minimum 1024. Cannot be combined with lossless (a 400): a pixel-exact file is whatever size it is. Applies per output, so a multi-variant request means every variant fits, not that they sum to it. Check X-Mochify-Target for whether the ceiling was actually reached.
targetLever Which knob targetBytes may turn: quality (re-encode lower, never resize), dimensions (downscale, never change quality), or both (quality first, then downscale).
Requires targetBytes. quality is a 400 for png output, which has no quality control at all; use dimensions or both, where scaling is the only lever anyway. The dimensions lever is also withheld when an HDR gain map is being carried (hdr=1 on an Ultra HDR source), because resizing the base would invalidate the map: quality alone is used, and the target may report floor. When the lever moves, X-Mochify-Target-Scale reports how far.
Request Headers
Content-Type MIME type of the uploaded image. For example image/jpeg, image/webp, image/avif, image/png, image/heic, image/jxl.
Response
Raw compressed image bytes. Supported input formats: JPEG, PNG, WebP, AVIF, HEIF, JXL and SVG. Formats are detected from the file's content.
X-Latency-Ms Processing time in milliseconds.
X-Mochify-Optimized true if the output is smaller or the format changed; false if the original was returned unchanged.
X-Mochify-Reason Present when X-Mochify-Optimized: false. Explains why the original was returned, for example "Original was smaller".
X-Mochify-Saliency Subject coverage (0.000–1.000): the mean of the saliency mask. Only present when smartCompress=true.
X-Mochify-SmartCompress Only present when smartCompress=true, and describes the returned bytes. background = the background was softened. skipped:no-subject, skipped:subject-fills-frame, skipped:background-removed, skipped:hdr or skipped:lossless = the pixels were left alone, and why. One value for a multi-variant ZIP.
X-Mochify-Target Present only when targetBytes was set. hit = the returned bytes are at or under the ceiling. floor = the levers ran out first, and this is the smallest output that could honestly be produced: still a real, usable image, not an error. For a multi-variant ZIP, hit means every entry fits. Worth checking: a floor response is a 200, so it is otherwise indistinguishable from success.
X-Mochify-Target-Encodes How many encodes the size search spent, including the first one the pipeline would have performed anyway. Summed across variants for a ZIP.
X-Mochify-Target-Scale Extra downscale the dimensions lever applied, relative to the geometry that was requested (for example 0.710). Omitted when the lever did not move, i.e. the output is exactly the size asked for.
X-Mochify-Quality The quality the returned bytes were encoded at. Present when smartCompress=true or when a targetBytes search ran, in which case it is the value the search settled on.
X-Mochify-BgRemoved true when background removal was applied.
X-Mochify-HDR Only present when hdr was requested. true means the emitted file carries headroom the source captured, generated means headroom Mochify synthesized, false means the output carries none. It describes the bytes actually returned, so a format that cannot hold a gain map reports false even when the source was HDR.
X-Mochify-Lossless Only present when lossless was requested. true means the returned bytes reproduce the submitted pixels exactly; downgraded means they are the best lossy encode instead, because the source was already lossy or because HDR reconstruction replaced the submitted pixels.
Examples
curl -X POST "https://api.mochify.app/v1/squish?type=webp&stripExif=1" \
-H "Content-Type: image/jpeg" \
-H "Authorization: Bearer mchy_your_api_key" \
--data-binary @photo.jpg \
--output photo.webpconst file = document.querySelector('input[type="file"]').files[0];
const response = await fetch(
'https://api.mochify.app/v1/squish?type=avif&stripExif=1',
{
method: 'POST',
headers: {
'Content-Type': file.type,
'Authorization': 'Bearer mchy_your_api_key',
},
body: file,
}
);
const blob = await response.blob();
const latency = response.headers.get('X-Latency-Ms');
// Save the file
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'output.avif';
a.click();import requests
with open('photo.jpg', 'rb') as f:
response = requests.post(
'https://api.mochify.app/v1/squish',
params={'type': 'webp', 'stripExif': '1'},
headers={
'Content-Type': 'image/jpeg',
'Authorization': 'Bearer mchy_your_api_key',
},
data=f,
)
with open('output.webp', 'wb') as out:
out.write(response.content)
print(f"Done in {response.headers.get('X-Latency-Ms')}ms")/v1/pdf
The PDF toolkit. A single endpoint with five operations, selected with ?op=. Four of them take a PDF as the raw request body; op=create takes images and produces one.
Operations
optimize PDF Recompress the images inside a PDF, in place, and return a smaller PDF. Text, fonts, vector art, links and layout are untouched, so the document stays searchable.
extract ZIP Pull the images embedded in a PDF out of it, at the resolution they were stored at. These are the pictures somebody placed into the document, not a render of each page.
rasterize ZIP Render every page to an image, text and all. The default operation when op is omitted.
split ZIP Explode a PDF into one single-page PDF per page. Takes no parameters.
create PDF Build a PDF from images, one page per image in upload order. Send a single raw image body, or a multipart/form-data upload with each file appended as images.
Images inside a PDF are always JPEG. The PDF format has no WebP, AVIF or JXL, so op=optimize re-encodes with jpegli and nothing else is offered. Those formats are available from op=extract, which writes files out rather than back into the document.
Parameters: op=optimize
quality 75 jpegli quality for the re-encode (1–100). Lower is smaller.
maxDpi 150 Target resolution, measured against how large each image is actually drawn on the page rather than how many pixels it stores. Use 96 for screen and email, 150 for general use, 300 to keep print quality. Set 0 to disable and rely on maxDimension alone.
A 4000px photo placed in a two-inch box is 2000 DPI of waste; the same photo full-bleed on A4 is not. Only the drawn size can tell those apart, so it is read from the content stream. An image drawn at several sizes is targeted at its largest, never its average.
maxDimension 2000 Absolute backstop applied alongside maxDpi: no image keeps a side longer than this. It is what limits an image the placement scan never saw drawn. Set 0 to remove the cap.
Resizing is layout-safe: a PDF image is drawn into the unit square, so changing its pixel size cannot move or rescale it on the page.
minSize 64 Leave images smaller than this (on either axis) untouched. Keeps spacers, rules and bullet glyphs out of the work.
It never returns a larger file. If recompression would not help, the original bytes come back and X-Mochify-Saved-Pct is 0. Running it twice changes nothing.
It never drops what it cannot handle. Images in encodings outside the supported set (CMYK, indexed palettes, JPEG 2000, CCITT and JBIG2 scans, stencil masks) are passed through byte-for-byte rather than skipped.
Parameters: op=extract
type original Output format for the extracted files.
One of: original · png · jpg · webp · avif · jxl
original copies each embedded JPEG out byte-for-byte, with no re-encode and no quality loss; images stored in other encodings come out as lossless PNG. Setting maxWidth forces a re-encode.
quality 82 Output quality (1–100). Ignored for original and png.
maxWidth 0 Cap the width of each extracted image, preserving aspect ratio. 0 leaves sizes alone.
minSize 64 Skip images smaller than this on either axis. Exported PDFs are full of 1×1 spacers and hairline rules; without a floor the archive is mostly junk. Set 0 to take everything.
An image repeated across pages, such as a logo or letterhead, is returned once rather than once per page. Files are named page-003-image-002.webp, recording the page each image was first found on.
Parameters: op=rasterize
type png Output image format for each rendered page.
One of: png · jpg · webp · avif · jxl
dpi 150 Render resolution, 36–300. Use 72 for screen previews, 300 for print.
quality 82 Output quality (1–100). Ignored for png.
Parameters: op=create
page fit Page size. fit makes each page exactly the image, with no whitespace.
One of: fit · a4 · letter
quality 82 jpegli quality (1–100) for the JPEG embedded in each page.
dpi 96 Pixels per inch used to size a fit page, 36–600.
maxWidth 0 Downscale images wider than this before embedding. 0 leaves them alone.
combine 1 Set to 0 to get one single-page PDF per image, returned as a ZIP, instead of one combined document.
Saving to your own storage
dest - Write the result to your connected storage instead of returning it. One of bucket or drive. The response becomes a JSON receipt, { "stored": true, ... }, and the bytes go straight from Mochify to your storage rather than back through the browser.
Only the operations that produce a single file accept it: op=optimize, and op=create with combine=1. extract, rasterize, split and combine=0 return an archive and are refused with a 400 rather than filed under a made-up name.
name - Required whenever dest is set. The destination filename, including extension. Without it every file in a batch would derive the same fallback name and overwrite each other.
Requires a signed-in account with that destination connected in the dashboard; an anonymous request gets a 401. If the write fails, the file is returned as a normal download with an X-Mochify-Bucket-Error header rather than erroring, so a storage problem never costs you the work.
Plans and limits
optimize, extract, rasterize and split require a paid plan (Seller, Pro, Growth, or Day Pass). create is available on every plan, including Free.
100 MB maximum.
200 absolute. For the PDF-in operations, Growth is unlimited within that and other paid plans cap at 10 pages.
create Free 3, Seller / Pro / Day Pass 20, Growth 200.
One operation per request, whatever the page or image count.
Response Headers
X-Latency-Ms Processing time in milliseconds. All operations.
X-Mochify-Pages Pages processed. rasterize, split, create.
X-Mochify-Images Images returned in the archive. extract.
X-Mochify-Skipped Images skipped for being too small, too large, or in an encoding that cannot be extracted. extract.
X-Mochify-Duplicates Repeat references to an image already returned once. extract.
X-Mochify-Images-Recompressed Images actually replaced. optimize.
X-Mochify-Images-Kept Images left as they were, either because recompressing them would not have saved enough or because they were outside the supported set. optimize.
X-Mochify-Original-Bytes Size of the submitted PDF. optimize.
X-Mochify-Saved-Pct Percentage saved, as a whole number. 0 means the original was returned unchanged. optimize.
Examples
curl -X POST "https://api.mochify.app/v1/pdf?op=optimize&quality=75&maxDpi=150" \
-H "Content-Type: application/pdf" \
-H "Authorization: Bearer mchy_your_api_key" \
--data-binary @report.pdf \
--output report-compressed.pdf \
--dump-header -
# X-Mochify-Images-Recompressed: 12
# X-Mochify-Saved-Pct: 47curl -X POST "https://api.mochify.app/v1/pdf?op=extract&type=webp&maxWidth=1600" \
-H "Content-Type: application/pdf" \
-H "Authorization: Bearer mchy_your_api_key" \
--data-binary @brochure.pdf \
--output images.zip
# Omit type= to get the embedded images byte-for-byte, with no re-encode.const file = document.querySelector('input[type="file"]').files[0];
const response = await fetch(
'https://api.mochify.app/v1/pdf?op=optimize&quality=75',
{
method: 'POST',
headers: {
'Content-Type': 'application/pdf',
'Authorization': 'Bearer mchy_your_api_key',
},
body: file,
}
);
const blob = await response.blob();
const saved = response.headers.get('X-Mochify-Saved-Pct');
// 0 means the PDF was already well compressed and came back untouched.
console.log(saved === '0' ? 'Already optimal' : `Saved ${saved}%`);import requests, zipfile, io
with open('report.pdf', 'rb') as f:
response = requests.post(
'https://api.mochify.app/v1/pdf',
params={'op': 'rasterize', 'type': 'png', 'dpi': '300'},
headers={
'Content-Type': 'application/pdf',
'Authorization': 'Bearer mchy_your_api_key',
},
data=f,
)
zipfile.ZipFile(io.BytesIO(response.content)).extractall('pages/')
print(f"{response.headers.get('X-Mochify-Pages')} pages rendered")curl -X POST "https://api.mochify.app/v1/pdf?op=create&page=a4" \
-H "Authorization: Bearer mchy_your_api_key" \
-F "images=@page1.jpg" \
-F "images=@page2.jpg" \
-F "images=@page3.jpg" \
--output album.pdf/v1/checkTokens
Returns the number of remaining operations for the current IP or API key. Useful for checking quota before submitting a batch.
Response
remaining number Total remaining operations for the current IP or API key.
available boolean true if at least one operation remains.
curl "https://api.mochify.app/v1/checkTokens" \
-H "Authorization: Bearer mchy_your_api_key"
# Response
# { "remaining": 983, "available": true }Errors
The API uses standard HTTP status codes.
200 Success. Response body is the processed image.
400 Bad request: missing or invalid parameters.
401 Unauthorized: invalid or missing API key.
403 Your plan does not include this operation. The PDF-in operations on /v1/pdf require a paid plan. Upgrade to enable them.
413 Payload too large. The request body, or the output it would produce, exceeds the limit for your plan.
415 Unsupported media type. The body was not a file this operation accepts, for example an image sent to a PDF-in operation.
422 The file was readable but could not be processed as asked. The body explains why, for example a PDF with more pages than your plan allows, a password-protected PDF, or a PDF holding no extractable images.
429 Rate limit exceeded. Free tier resets monthly; paid tiers reset on your billing date. Upgrade for higher limits.
500 Server error: the file could not be processed.
503 At capacity. Retry after the delay in the Retry-After header. Nothing is charged for a request that returns this.
Need more operations?
Upgrade to Pro for 1,200 operations/month and a dedicated API key.