Summary
Two additions to the V1 conversion API, both synchronous multipart/form-data file conversions gated only by the global plan conversion limit. POST /v1/convert/compress-image compresses PNG, JPEG and WebP files without ever changing their format — lossless-first, with an optional target_size_kb budget met by aspect-ratio-locked downscaling. The three SVG rasterization endpoints (svg-to-png, svg-to-jpeg, svg-to-webp) gain optional width and height parameters that control the output dimensions in pixels; the playground exposes matching size inputs with an aspect-ratio lock derived from the uploaded SVG.
POST /v1/convert/compress-image
Accepted extensions (4): .png .jpg .jpeg .webp — magic-byte checked. The output keeps the input's extension and format; a file whose content does not match its extension is rejected with 400 rather than silently converted. Animated inputs (APNG, animated WebP) are rejected with 400 rather than silently flattened to their first frame.
Request: file (required). target_size_kb (optional; a zero/negative value is a 400 rejected before quota is burned, a non-numeric value is a 422 from request validation). output_filename (default: input basename; the input's extension is preserved). job_id (optional, enables status polling). direct_download (accepted for parity with the other file endpoints; responses currently always take the JSON path below, same as every V1 file conversion).
Behavior — stage 1, lossless (always runs): metadata (EXIF, XMP, PNG text chunks) is stripped; the ICC color profile and the EXIF orientation flag are preserved — orientation is re-emitted as a minimal single-tag EXIF block instead of being baked into pixels, which keeps the JPEG path free of extra quantization loss. PNG re-encodes at zlib level 9 + optimize, plus a palette candidate accepted only when the palette roundtrip is provably pixel-identical. JPEG re-encodes reusing the original quantization tables (quality='keep') with optimized progressive Huffman coding. WebP re-encodes as true lossless VP8L at maximum effort. The smallest of the original bytes and all candidates wins, so the output is never larger than the input.
Behavior — stage 2, dimension reduction (only when target_size_kb is set and stage 1 missed it): LANCZOS downscale with the aspect ratio locked; the scale factor is binary-searched (up to 8 iterations, minimum scale 1%) for the largest dimensions that fit the budget. Downscaled JPEG and lossy-source WebP re-encode at quality 85; PNG and lossless-source WebP stay lossless at the reduced size (lossless vs lossy WebP sources are told apart by walking the RIFF chunk list for VP8L). An unreachable target returns the smallest file achieved, not an error — check file_size / X-File-Size to see what was reached.
Response: 200 with JSON — presigned_url, object_key, filename (timestamped, input extension preserved, e.g. photo_20260717_101530123.png), file_size, conversion_time_seconds, job_id. The same values are mirrored on the X-Object-Key, X-File-Size, X-Conversion-Time, X-Filename headers.
Constraints:
- The decoded canvas is capped at 40,000,000 pixels (e.g. 8000x5000), checked from the image header before any pixels are decoded, so a decompression bomb is rejected with
400 without allocating the full surface.
- Whole-request size is validated against the plan's max file size via
Content-Length (413).
- The shared conversion concurrency gate applies: at capacity the endpoint returns
503 with Retry-After: 10.
- WebP lossless encoding uses maximum effort (
method=6) up to 4 MP and drops to method=4 above it, bounding CPU on large canvases; all requests are still bounded by the 300s gateway timeout (504, then poll with job_id).
- CMYK JPEGs are re-encoded in CMYK (no mode change); 16-bit PNGs skip the palette candidate and only get the plain lossless re-encode.
width / height on svg-to-png, svg-to-jpeg, svg-to-webp
Request: optional width and height integer form fields, 1–10000 each. One dimension alone scales the render proportionally — the other is derived from the SVG's own aspect ratio (CairoSVG native behavior). Both together set the exact canvas size, which may change the aspect ratio. Omitting both keeps the previous behavior (the SVG's width/height/viewBox attributes decide).
Constraints:
- Total output is capped at 25,000,000 pixels (
400). For single-dimension requests the derived dimension is estimated server-side from the root <svg> width/height attributes (absolute units only) or viewBox, so an extreme-ratio SVG cannot request an unbounded render surface. If the ratio cannot be determined from those (e.g. the SVG sizes itself in em/ex/%, which CairoSVG can still resolve into a large canvas), a single-dimension request is rejected with 400 and asked to supply both width and height — the size must then be explicit and self-bounding.
- Validation runs before quota is burned or an activity row is logged; out-of-range dimensions are pure client errors.
- The converters' signatures stay backward-compatible — existing calls without
width/height are byte-for-byte unaffected.
Playground: the three SVG conversions now show width/height inputs whose placeholders display the uploaded SVG's intrinsic size, plus a Lock aspect ratio toggle (on by default). The lock reads the SVG's width/height attributes client-side, falling back to the viewBox, and disables itself when neither yields a usable ratio. Blank fields mean "the SVG's own size". The controls are localized in all five languages.
Docs
Every affected page was updated in all five languages (en, fr, de, es, it): the three SVG endpoint pages now document width/height (parameter table, resolution notes, FAQs), a new compress-image endpoint page follows the standard skeleton, and the image-conversions category page, endpoints overview, parameters reference ("Image Options" section) and endpoint counts (48 → 49) were updated accordingly.