Skip to content

Endpoint reference

POST /v1/image

The one chainable pipeline endpoint. Send an image, describe what to do to it, get the processed image back.

POST https://api.purlo.dev/v1/image
Authorization: Bearer <api_key>
Content-Type: multipart/form-data

Local dev: http://localhost:3000/v1/image.

Input

Provide the image one of two ways:

Form How
Upload multipart/form-data field named image (the file).
URL A url field (form field or JSON body) pointing at the image to fetch.

Either way, also send an operations field: a JSON object (as a string, in the multipart body) describing the pipeline. If you provide neither image nor url, the request fails with 400 no_image.

Only http and https URLs are fetched, and the fetch is SSRF-guarded — see Errors & limits for what gets rejected and why.

operations

{
  "resize":  { "w": 800, "h": null, "fit": "inside" },
  "format":  "webp",
  "quality": 80,
  "strip":   true
}

All four keys are optional. Unknown top-level keys (or unknown keys inside resize) are rejected with 400 invalid_operations.

Key Type Default Notes
resize object { w, h, fit } omitted = no resize See below.
format string: jpeg | png | webp | avif keeps the input's format Output container/codec.
quality integer 1–100 80 Passed to the format encoder (mozjpeg for jpeg, standard quality/quantisation knobs for png/webp/avif).
strip boolean true Removes EXIF/GPS metadata. The ICC colour profile is always kept, even when stripping, so colours don't shift. Set false to keep all original metadata.

resize

Field Type Default Notes
w positive integer or null null Target width in pixels.
h positive integer or null null Target height in pixels.
fit string "inside" One of inside, outside, cover, contain, fill.
  • At least one of w or h is required if resize is present.
  • Both are capped at a maximum dimension (10,000px by default — see Errors & limits); requesting larger fails validation.
  • Resize never enlarges. If your requested size is bigger than the source image, Purlo downscales-or-keeps but never scales up past the original. You can't use resize to blow a small image up into a large one.

Order of operations

Operations always apply in this fixed order, regardless of key order in your JSON:

  1. resize
  2. format + quality (convert and compress together)
  3. strip (metadata handling)

This is a documented, stable contract — chain confidently.

Response

Success (200): the processed image, as raw bytes, with:

Content-Type: image/webp            (matches the output format)
X-Purlo-Output-Format: webp
X-Purlo-Output-Bytes: 42110
X-Purlo-Operations: resize,format=webp,quality=80,strip
X-Purlo-Quota-Limit: 1000
X-Purlo-Quota-Remaining: 999
Header Meaning
X-Purlo-Output-Format The format actually written (jpeg/png/webp/avif).
X-Purlo-Output-Bytes Size of the response body in bytes.
X-Purlo-Operations Summary of which operations ran, e.g. resize,format=webp,quality=80,strip.
X-Purlo-Quota-Limit Your plan's monthly image quota.
X-Purlo-Quota-Remaining Images remaining this month, after this request.

Error: JSON, see Errors & limits.

See also