mochify-app Image API
Compress, convert, resize, crop, and transform a single image
Compress, convert, resize, crop, and transform a single image
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/mochify-app:mochify-app-image-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Mochify Image API
version: 1.0.0
description: Mochify's REST API for zero-retention image compression, PDF page extraction/splitting, and AI image generation. Images and PDFs are streamed into RAM at api.mochify.app, processed, and returned; the original bytes are never written to disk. Authenticate with an x-api-key header (dashboard-issued key) for quota tracking, or omit auth entirely for anonymous, IP-metered use on the free tier.
contact:
url: https://mochify.app/docs
servers:
- url: https://api.mochify.app
description: Production
security:
- bearerAuth: []
- apiKeyAuth: []
- {}
tags:
- name: Image
description: Compress, convert, resize, crop, and transform a single image
paths:
/v1/squish:
post:
operationId: squishImage
tags:
- Image
summary: Compress and/or convert an image
description: 'Accepts raw image bytes as the request body (no multipart wrapper) and returns the transformed image, or a ZIP when multiple format/size variants are requested via `types`/`sizes`. Every request is metered against the caller''s quota (25/mo free, 300/mo Seller, 1200/mo Pro, anonymous callers get 3 per session/batch, IP-based). File size ceilings: 20 MB on the free tier, 75 MB on paid plans.'
parameters:
- name: type
in: query
schema:
type: string
enum:
- jpg
- png
- webp
- avif
- jxl
default: jpg
description: Output format. Ignored if `types` is also present.
- name: width
in: query
schema:
type: integer
minimum: 0
description: Target width in pixels. 0 or omitted preserves aspect ratio from height, or leaves the original size if both are omitted.
- name: height
in: query
schema:
type: integer
minimum: 0
description: Target height in pixels. Same rules as width.
- name: types
in: query
schema:
type: string
description: CSV of output formats for a multi-variant response, e.g. `webp,avif`. Combines with `sizes` to produce the full formats × sizes matrix, returned as a ZIP.
- name: sizes
in: query
schema:
type: string
description: CSV of target widths for a multi-variant response, e.g. `500,1200`. See `types`.
- name: quality
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 65
description: Output quality. Under `smartCompress` it is the quality of the SUBJECT — smartCompress saves bytes by softening the background, not by lowering this. `quality=100` is the best LOSSY setting, not lossless — use `lossless` for that.
- name: lossless
in: query
schema:
type: boolean
default: false
description: 'Pixel-exact output. Only `jxl`, `webp` and `png` can honour it; any other format (including a `types` list containing one) is a 400. Overrides `quality`, and disables `smartCompress` softening. A source that is already lossy (JPEG, AVIF, HEIC) is downgraded to the best lossy encode instead of rejected — see the X-Mochify-Lossless response header. A `png` output always reports true: pngsave is pixel-exact regardless. Expect output LARGER than the input: lossless preserves pixels, not file size.'
example: true
- name: quantize
in: query
schema:
type: boolean
default: false
description: 'Lossy PNG: reduce to a dithered palette of at most 256 colors and emit an indexed PNG — typically 15-25% of the lossless size. Applies to `png` outputs only (a request with no png output is a 400) and cannot be combined with `lossless` (400). With no `quality` the palette is the full 256 colors; an explicit `quality` picks fewer (100 = 256, 80 ≈ 108, 65 ≈ 56, 50 ≈ 28, 20 = 8). Without this flag a png is always pixel-exact — `quality` alone never makes it lossy.'
example: true
- name: targetBytes
in: query
schema:
type: integer
minimum: 1024
description: 'Ceiling on the emitted file size, in bytes. Nothing can predict an encoded size without encoding — libvips has no estimator and no saver takes a byte target — so this runs a short measured search (typically 1–3 encodes, at most 5) rather than a single guess. A quality guessed up front cannot work: bytes at a fixed quality are a property of the PIXELS, and span ~3x (JPEG) to ~10x (AVIF) between a flat graphic and a noisy photo at the same dimensions. Treated as an upper bound, never a quota to fill — if the requested (or default) quality already fits, that encode is the answer and the quality is left alone. Cannot be combined with `lossless` (400). Minimum 1024. See the X-Mochify-Target response header for whether the ceiling was actually reached.'
example: 1000000
- name: targetLever
in: query
schema:
type: string
enum:
- quality
- dimensions
- both
default: both
description: 'Which knob `targetBytes` may turn. `quality` re-encodes lower but never resizes ("under 1MB but do not resize"); `dimensions` downscales but leaves quality alone ("1MB, don''t change the quality"); `both` lowers quality first and only then downscales. Requires `targetBytes` (a lever alone is a 400). For `png` the quality lever is palette quantization: the lossless encode is tried first and the file is quantized only if that misses the ceiling (see `quantize` and the X-Mochify-Quantize response header); `dimensions` keeps a png lossless. The `dimensions` lever is 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`.'
- name: smartCompress
in: query
schema:
type: boolean
default: false
description: 'Saliency-guided background softening (ONNX U²-Net-p subject mask): the background is lightly low-passed before the encode so it costs fewer bytes, while the subject is left pixel-identical and encoded at `quality` (or the default). 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 the X-Mochify-SmartCompress response header.'
- name: smartCrop
in: query
schema:
type: boolean
default: false
description: Saliency/face-anchored crop centred on the subject; requires `width` and `height`. Alias of `crop` — either flag triggers the same behavior.
- name: crop
in: query
schema:
type: boolean
default: false
description: Alias of `smartCrop`.
- name: region
in: query
schema:
type: string
example: 0.02,0.35,0.74,0.46
description: Crop to a rectangle given as four comma-separated fractions of the image, `x,y,w,h` from the top-left corner, each 0 to 1. Measured on the upright image (after EXIF orientation) and applied before `width`/`height`, `smartCrop` and `rotate`. A box past an edge is trimmed; any value above 1 is rejected with 400. The pixel rectangle actually cut is returned in `X-Mochify-Region`.
- name: redact
in: query
schema:
type: string
example: faces;0.155,0.54,0.205,0.075
description: 'Hide areas of the image: a `;`-separated list of boxes in the same `x,y,w,h` fraction convention as `region`, and/or `faces` to find and hide faces with Mochify''s own detector (best-effort; it can miss small or side-on faces). Boxes are fractions of the whole upright image even when `region` is set; redaction runs before the crop. Up to 64 areas. A redacted image always has its metadata stripped, even with `stripExif=false`, and never carries an HDR gain map. See `X-Mochify-Redacted` and `X-Mochify-Redacted-Faces`.'
- name: redactStyle
in: query
schema:
type: string
enum:
- fill
- blur
default: fill
description: 'How hidden areas look: `fill` (solid black, for anything that must stay secret) or `blur` (cosmetic; blurred text and faces can sometimes be partly recovered). Requires `redact`.'
- name: erase
in: query
schema:
type: string
example: 0.62,0.55,0.12,0.2
description: 'Paint objects out with plausible background (inpainting): a `;`-separated list of up to 8 boxes in the same `x,y,w,h` fraction convention as `region`, measured on the whole upright image. Draw each box around the whole object with a margin; any part of the object left outside the box can be regrown. Applied before `redact` and `region`. Erase invents content, so it is not a way to hide anything sensitive; use `redact`. Requires a paid plan (403 otherwise). The output always has its metadata stripped and carries no HDR gain map. See `X-Mochify-Erased`.'
- name: recolor
in: query
schema:
type: string
example: 0.4,0.5,0.3,0.2:#2563eb
description: 'Change the color of objects YOU locate, keeping their shading and texture: a `;`-separated list of up to 8 `x,y,w,h:#rrggbb` entries (box as fractions of the upright image, same convention as `region`; target color as hex). The object''s outline is found automatically inside each box and only the object is recolored; a box with no clear object is skipped and reported, never recolored as a rectangle. Box the part to recolor (e.g. the jacket, not the whole person), since everything in the outline takes the color. Not generative. Requires a paid plan (403 otherwise); 503 when the outline model is unavailable. Output metadata is stripped and HDR dropped. See `X-Mochify-Recolored` and `X-Mochify-Recolor-Skipped`.'
- name: removeBackground
in: query
schema:
type: boolean
default: false
description: AI background removal (ONNX BiRefNet-lite). Available on every plan, including anonymous/Free — not gated to a paid tier. Output retains alpha for PNG/WebP; JPEG flattens to `background` (default white).
- name: generate
in: query
schema:
type: string
enum:
- ''
- shadow
- background
description: 'AI scene composition after subject isolation: `shadow` adds a drop shadow under the subject; `background` composites a described/generated scene. Empty/omitted skips this step. Distinct from `POST /v1/generate` (text-to-image from scratch).'
- name: background
in: query
schema:
type: string
description: Flatten/composite background color (used when flattening alpha to JPEG, or as the base under `generate`).
- name: rotate
in: query
schema:
type: integer
enum:
- 0
- 90
- 180
- 270
default: 0
description: Clockwise rotation in degrees.
- name: stripExif
in: query
schema:
type: boolean
default: true
description: 'Strip EXIF/metadata (GPS, device identifiers, timestamps). Defaults to true at the API level — pass `false` or `0` to preserve metadata. Note: the mochify.app browser UI does not uniformly default this toggle the same way across its upload components, so surface-level defaults can differ from this API default.'
- name: optimizeForWeb
in: query
schema:
type: boolean
default: false
description: Progressive encoding + 4:2:0 chroma subsampling for the smallest browser-delivered file.
- name: brightness
in: query
schema:
type: integer
minimum: -100
maximum: 100
default: 0
description: Exposure adjustment.
- name: clarity
in: query
schema:
type: boolean
default: false
description: Midtone contrast enhancement.
- name: hdr
in: query
schema:
type: boolean
default: false
description: Preserve an HDR gain map as Ultra HDR / ISO 21496-1 JPEG. Silently ignored if the source has no HDR gain map.
- name: dpi
in: query
schema:
type: integer
minimum: 8
maximum: 4800
default: 96
description: Output DPI metadata.
requestBody:
required: true
content:
image/jpeg:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
image/avif:
schema:
type: string
format: binary
image/heic:
schema:
type: string
format: binary
application/octet-stream:
schema:
type: string
format: binary
description: Raw bytes; format is detected from magic bytes, not the Content-Type header.
responses:
'200':
description: Transformed image (single variant) or a ZIP archive (multi-variant, when `types`/`sizes` resolve to more than one output).
headers:
X-Latency-Ms:
schema:
type: integer
description: Server-side processing time in milliseconds.
X-Mochify-Optimized:
schema:
type: boolean
description: Whether the output is smaller than the input.
X-Mochify-Saliency:
schema:
type: number
description: Subject coverage 0.000–1.000 (mean of the saliency mask). Present only when smartCompress was used.
X-Mochify-SmartCompress:
schema:
type: string
enum:
- background
- skipped:no-subject
- skipped:subject-fills-frame
- skipped:background-removed
- skipped:hdr
- skipped:lossless
description: 'Present only when smartCompress was used. Describes the emitted bytes: `background` = the background was softened; `skipped:<reason>` = the pixels were left alone and why. One value for a multi-variant ZIP.'
X-Mochify-Quality:
schema:
type: integer
description: The quality the returned bytes were encoded at. Present when `smartCompress` was used, or when a `targetBytes` search did — in which case it is the value the search settled on, not the one that was requested.
X-Mochify-BgRemoved:
schema:
type: boolean
description: Present and true only when background removal ran.
X-Mochify-HDR:
schema:
type: boolean
description: Present when the output format supports HDR gain maps; true if one was carried through.
X-Mochify-Quantize:
schema:
type: string
enum:
- 'true'
- 'false'
description: 'Present for a png output when `quantize` or `targetBytes` was requested. Describes the emitted bytes: `true` = a palette (lossy) png, `false` = pixel-exact. With only `targetBytes`, `false` means the lossless encode already fit. For a multi-variant ZIP, `true` if any png entry was quantized.'
X-Mochify-Lossless:
schema:
type: string
enum:
- 'true'
- downgraded
description: Present only when `lossless` was requested. `true` = the bytes are pixel-exact. `downgraded` = they are the best lossy encode instead, because the source was already lossy or because HDR reconstruction replaced the submitted pixels.
X-Mochify-Target:
schema:
type: string
enum:
- hit
- floor
description: 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 the best available file, not an error. For a multi-variant ZIP, `hit` means every entry fits.
X-Mochify-Target-Encodes:
schema:
type: integer
description: Encodes the target search spent, including the first one the pipeline would have performed anyway. Summed across variants for a ZIP.
X-Mochify-Target-Scale:
schema:
type: number
description: Extra downscale the dimensions lever applied, relative to the geometry that was requested (e.g. 0.710). Omitted when the lever did not move, i.e. the output is exactly the size asked for.
X-Mochify-Region:
schema:
type: string
description: Present only when `region` was set. The pixel rectangle actually cut, as `x,y,w,h` in the upright source; fractions round and an over-the-edge box is trimmed, so it may differ from the box sent.
X-Mochify-Redacted:
schema:
type: integer
description: 'Present only when `redact` was set. How many areas were hidden: the boxes sent plus any faces found.'
X-Mochify-Redacted-Faces:
schema:
type: integer
description: Present only when `redact` includes `faces`. How many faces the detector found. 0 is a successful response that hid no faces, so check it rather than assuming.
X-Mochify-Erased:
schema:
type: integer
description: Present only when `erase` was set. How many boxes were painted out.
X-Mochify-Erase-Outlined:
schema:
type: integer
description: Present only when `erase` was set. Of the erased boxes, how many were repainted along the object's detected outline rather than the whole box (fewer invented pixels). 0 means every box was repainted whole.
X-Mochify-Erase-Coverage:
schema:
type: number
description: 'Present only when `erase` was set. Approximate share of the frame repainted, 0-1 (e.g. 0.112). Large removals are softer: the model paints at a fixed 512x512, so a hole covering much of a large image is filled at reduced detail.'
X-Mochify-Recolored:
schema:
type: integer
description: Present only when `recolor` was set. How many boxes had their object recolored.
X-Mochify-Recolor-Skipped:
schema:
type: integer
description: Present only when `recolor` was set. How many boxes were left alone because no clear object outline was found in them.
content:
image/jpeg: {}
image/png: {}
image/webp: {}
image/avif: {}
image/jxl: {}
application/zip:
schema:
type: string
format: binary
description: Returned instead of a single image when the request resolves to multiple format/size variants.
'400':
description: Empty body, or an invalid types/sizes/type/width/height combination.
content:
text/plain: {}
'401':
description: x-api-key was present but not resolvable.
content:
text/plain: {}
'413':
description: File exceeds the plan's size ceiling (20 MB free, 75 MB paid), or the image's pixel dimensions / megapixels exceed the decompression-bomb ceiling.
content:
text/plain: {}
'415':
description: Unrecognized image format (magic-byte check failed).
content:
text/plain: {}
'422':
description: 'Image could not be processed: corrupt or truncated (unreadable header), or dimensions below the minimum. X-Mochify-Reject carries the taxonomy slug (e.g. corrupt-image, image-too-small).'
content:
text/plain: {}
'429':
description: Monthly quota exhausted.
content:
text/plain: {}
'503':
description: Worker pool at capacity; retry after the Retry-After header (1s).
headers:
Retry-After:
schema:
type: integer
content:
text/plain: {}
/v1/checkTokens:
get:
operationId: checkTokens
tags:
- Image
summary: Check remaining quota
description: Read-only — does not consume a token. Works anonymously (IP-based) or with an x-api-key.
responses:
'200':
description: Quota status.
content:
application/json:
schema:
type: object
properties:
available:
type: boolean
remaining:
type: integer
description: Omitted on a cache miss (unseeded bucket, i.e. a caller who hasn't made a request yet).
quota:
type: integer
plan:
type: string
error:
type: string
description: Present only when x-api-key was supplied but invalid ("invalid_api_key"); available is then false.
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'Dashboard-issued API key sent as `Authorization: Bearer <key>`. This is the header documented on /docs and in the dashboard, and the one to prefer. A signed-in session token is accepted here too. Note: a bearer token that cannot be resolved does not fail the request, it falls through to anonymous IP-based free-tier limits, so a wrong key behaves exactly like sending no key at all. Send the key as x-api-key instead if you want an unusable key reported back to you.'
apiKeyAuth:
type: apiKey
in: header
name: x-api-key
description: The same dashboard-issued API key, sent in the x-api-key header. Used by /v1/squish, /v1/pdf, /v1/generate, /v1/checkTokens, /v1/upload/*. Unlike the bearer header, a key that cannot be resolved is reported as invalid rather than silently downgraded. If no credential is sent at all, these endpoints fall back to anonymous IP-based rate limiting on the free tier rather than rejecting the request.