{
  "baseUrl": "https://demo.imgapis.com",
  "caching": {
    "header": "x-img-apis-cache: HIT|MISS",
    "images": "Image responses (placeholder, transform, grid) cached immutably for 1 year via Cloudflare Cache API. Pass `?v=` to bust all caches for a request.",
    "info": "/info* responses cached for 24 hours; the four /info paths share a single R2 cache entry keyed on `src` (plus `v` when present)."
  },
  "description": "Image metadata + prominent color extraction + transformation API, served as a Cloudflare Worker.",
  "endpoints": [
    {
      "cacheControl": "public, max-age=86400",
      "examples": [
        {
          "description": "Full payload — metadata, blurhash, colors, palette",
          "url": "https://demo.imgapis.com//info?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        },
        {
          "description": "Path-sugar form — same as ?src=",
          "url": "https://demo.imgapis.com//info/https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        },
        {
          "description": "Subset — only colors and metadata",
          "url": "https://demo.imgapis.com//info?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&select=colors,metadata"
        }
      ],
      "method": "GET",
      "notes": [
        "Shares cache with /info/blurhash, /info/colors, /info/metadata. Once any of them warms the cache, the others serve from it.",
        "Accepts `?src=` or the path-sugar form `/info/[source]/[path]`.",
        "`x-img-apis-cache: HIT|MISS` header reports cache state."
      ],
      "params": [
        {
          "description": "URL-encoded source image URL.",
          "name": "src",
          "required": true,
          "type": "string"
        },
        {
          "default": null,
          "description": "Comma-separated subset of top-level fields to return. Unknown fields reject with 400. Omit for full payload.",
          "enumValues": [
            "blurhash",
            "colors",
            "metadata",
            "palette"
          ],
          "name": "select",
          "required": false,
          "type": "csv"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/info",
      "responseContentType": "application/json",
      "summary": "Full image analysis: metadata, BlurHash, dominant colors, palette."
    },
    {
      "cacheControl": "public, max-age=86400",
      "examples": [
        {
          "description": "Get only the BlurHash for an image",
          "url": "https://demo.imgapis.com//info/blurhash?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        },
        {
          "description": "Path-sugar form — same as ?src=",
          "url": "https://demo.imgapis.com//info/blurhash/https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        }
      ],
      "method": "GET",
      "notes": [
        "Shortcut for /info?src=...&select=blurhash. Shares /info cache.",
        "Accepts `?src=` or the path-sugar form `/info/blurhash/[source]/[path]`."
      ],
      "params": [
        {
          "description": "URL-encoded source image URL.",
          "name": "src",
          "required": true,
          "type": "string"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/info/blurhash",
      "responseContentType": "application/json",
      "summary": "BlurHash-only shortcut. Returns `{ blurhash: string }`."
    },
    {
      "cacheControl": "public, max-age=86400",
      "examples": [
        {
          "description": "Get colors and palette for an image",
          "url": "https://demo.imgapis.com//info/colors?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        },
        {
          "description": "Path-sugar form — same as ?src=",
          "url": "https://demo.imgapis.com//info/colors/https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        }
      ],
      "method": "GET",
      "notes": [
        "Shortcut for /info?src=...&select=colors,palette. Shares /info cache.",
        "Accepts `?src=` or the path-sugar form `/info/colors/[source]/[path]`."
      ],
      "params": [
        {
          "description": "URL-encoded source image URL.",
          "name": "src",
          "required": true,
          "type": "string"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/info/colors",
      "responseContentType": "application/json",
      "summary": "Colors + palette shortcut. Returns `{ colors, palette }` — the two fields typically consumed together."
    },
    {
      "cacheControl": "public, max-age=86400",
      "examples": [
        {
          "description": "Get only file metadata",
          "url": "https://demo.imgapis.com//info/metadata?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        },
        {
          "description": "Path-sugar form — same as ?src=",
          "url": "https://demo.imgapis.com//info/metadata/https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800"
        }
      ],
      "method": "GET",
      "notes": [
        "Shortcut for /info?src=...&select=metadata. Shares /info cache.",
        "Accepts `?src=` or the path-sugar form `/info/metadata/[source]/[path]`."
      ],
      "params": [
        {
          "description": "URL-encoded source image URL.",
          "name": "src",
          "required": true,
          "type": "string"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/info/metadata",
      "responseContentType": "application/json",
      "summary": "Metadata-only shortcut. Returns `{ metadata: { bytes, contentType, format, height, width } }`."
    },
    {
      "cacheControl": "public, max-age=31536000, immutable",
      "examples": [
        {
          "description": "Decode a BlurHash string directly to PNG",
          "url": "https://demo.imgapis.com//placeholder/blurhash?blurhash=L9TI%3Aj%7CcfQ%7Cc%7Cco1fQo1fQfQfQfQ&w=32&h=32"
        },
        {
          "description": "Derive BlurHash from an image URL and render it (w/h optional)",
          "url": "https://demo.imgapis.com//placeholder/blurhash?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&w=64&h=64"
        },
        {
          "description": "Path-sugar form — same as ?src=",
          "url": "https://demo.imgapis.com//placeholder/blurhash/https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800?w=64&h=64"
        }
      ],
      "method": "GET",
      "notes": [
        "Oversize dimensions are silently clamped to the max while preserving aspect ratio.",
        "Format is negotiated from the `Accept` header when `format` is omitted.",
        "With `src`, dimensions default to `contain`. Pass `fit=cover` for exact `w × h`.",
        "With `src`, accepts `?src=` or the path-sugar form `/placeholder/blurhash/[source]/[path]`."
      ],
      "params": [
        {
          "description": "A BlurHash string to decode.",
          "name": "blurhash",
          "required": "one of `blurhash` / `src`",
          "type": "string"
        },
        {
          "description": "Image URL. Server derives the BlurHash from it.",
          "name": "src",
          "required": "one of `blurhash` / `src`",
          "type": "string"
        },
        {
          "description": "Output width in pixels.",
          "max": 2048,
          "min": 1,
          "name": "w",
          "required": "required with `blurhash`, optional with `src` (falls back to intrinsic width)",
          "type": "integer"
        },
        {
          "description": "Output height in pixels.",
          "max": 2048,
          "min": 1,
          "name": "h",
          "required": "required with `blurhash`, optional with `src` (falls back to intrinsic height)",
          "type": "integer"
        },
        {
          "default": "contain",
          "description": "With `src`, defaults to `contain`. Pass `cover` to force exact `w × h`.",
          "enumValues": [
            "contain",
            "cover"
          ],
          "name": "fit",
          "required": false,
          "type": "enum"
        },
        {
          "default": 1,
          "description": "Device-pixel-ratio multiplier applied to `w`/`h`. Final dimensions clamped uniformly to ≤ 4096 per axis.",
          "max": 2,
          "min": 0,
          "name": "dpr",
          "required": false,
          "type": "number"
        },
        {
          "default": 1,
          "description": "BlurHash decoder contrast boost.",
          "name": "punch",
          "required": false,
          "type": "number"
        },
        {
          "default": 80,
          "description": "Encoder quality. Applies to lossy formats.",
          "max": 100,
          "min": 1,
          "name": "q",
          "required": false,
          "type": "integer"
        },
        {
          "default": null,
          "description": "Output format. When omitted, `Accept` decides (`image/avif` > `image/webp` > PNG fallback). `nocompress` mirrors the source content-type.",
          "enumValues": [
            "avif",
            "jpeg",
            "jpg",
            "nocompress",
            "png",
            "webp"
          ],
          "name": "format",
          "required": false,
          "type": "enum"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/placeholder/blurhash",
      "responseContentType": "image/* (negotiated; defaults to image/png)",
      "summary": "Decode a BlurHash to a placeholder image. Provide either `blurhash` directly or a `src` URL to derive it from."
    },
    {
      "cacheControl": "public, max-age=31536000, immutable",
      "examples": [
        {
          "description": "Solid hex color → PNG",
          "url": "https://demo.imgapis.com//placeholder/color?color=ff0000&w=64&h=64"
        },
        {
          "description": "Extract dominant color from URL → PNG",
          "url": "https://demo.imgapis.com//placeholder/color?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&w=64&h=64"
        },
        {
          "description": "Path-sugar form — same as ?src=",
          "url": "https://demo.imgapis.com//placeholder/color/https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800?w=64&h=64"
        }
      ],
      "method": "GET",
      "notes": [
        "Same dimension resolution and clamping rules as /placeholder/blurhash.",
        "With `src`, dimensions default to `contain`. Pass `fit=cover` for exact `w × h`.",
        "With `src`, accepts `?src=` or the path-sugar form `/placeholder/color/[source]/[path]`."
      ],
      "params": [
        {
          "description": "Hex color. Accepts `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`. Leading `#` is optional.",
          "name": "color",
          "required": "one of `color` / `src`",
          "type": "string"
        },
        {
          "description": "Image URL. Server extracts the dominant color from it.",
          "name": "src",
          "required": "one of `color` / `src`",
          "type": "string"
        },
        {
          "description": "Output width in pixels.",
          "max": 2048,
          "min": 1,
          "name": "w",
          "required": "required with `color`, optional with `src` (falls back to intrinsic width)",
          "type": "integer"
        },
        {
          "description": "Output height in pixels.",
          "max": 2048,
          "min": 1,
          "name": "h",
          "required": "required with `color`, optional with `src` (falls back to intrinsic height)",
          "type": "integer"
        },
        {
          "default": "contain",
          "description": "With `src`, defaults to `contain`. Pass `cover` to force exact `w × h`.",
          "enumValues": [
            "contain",
            "cover"
          ],
          "name": "fit",
          "required": false,
          "type": "enum"
        },
        {
          "default": 1,
          "description": "Device-pixel-ratio multiplier. Final dimensions clamped uniformly to ≤ 4096 per axis.",
          "max": 2,
          "min": 0,
          "name": "dpr",
          "required": false,
          "type": "number"
        },
        {
          "default": 80,
          "description": "Encoder quality. Applies to lossy formats.",
          "max": 100,
          "min": 1,
          "name": "q",
          "required": false,
          "type": "integer"
        },
        {
          "default": null,
          "description": "Output format. When omitted, `Accept` decides (`image/avif` > `image/webp` > PNG fallback). `nocompress` mirrors the source content-type.",
          "enumValues": [
            "avif",
            "jpeg",
            "jpg",
            "nocompress",
            "png",
            "webp"
          ],
          "name": "format",
          "required": false,
          "type": "enum"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/placeholder/color",
      "responseContentType": "image/* (negotiated; defaults to image/png)",
      "summary": "Render a solid-color placeholder image. Provide either `color` (hex) directly or a `src` URL to extract the dominant color from."
    },
    {
      "cacheControl": "public, max-age=31536000, immutable",
      "examples": [
        {
          "description": "Canonical: ?src= at namespace root, resize to 400-wide",
          "url": "https://demo.imgapis.com//?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&w=400"
        },
        {
          "description": "Sugar: pathname becomes the src value (no query needed)",
          "url": "https://demo.imgapis.com//my-cdn/photos/cat.jpg?w=400"
        },
        {
          "description": "Force 16:9 aspect ratio without specifying sizes (longest axis = 2048)",
          "url": "https://demo.imgapis.com//?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&ar=16:9"
        },
        {
          "description": "Add a mark at bottom-right with 50% opacity",
          "url": "https://demo.imgapis.com//?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&w=800&mark=https%3A%2F%2Fexample.com%2Fmark.png&mark-w=100&mark-align=bottom,right&mark-pad=10&mark-opacity=0.5"
        }
      ],
      "method": "GET",
      "notes": [
        "Cached immutably for 1 year via Cloudflare Cache API.",
        "`?format=nocompress` mirrors the source content-type, except WebP / AVIF sources which are re-encoded to PNG.",
        "`src` accepts a full URL (gated by account.externalSources) or `<source>/<path>` for named sources registered in the dashboard.",
        "Path-style URLs `/[hash]/[source]/[path]` are sugar — equivalent to `/[hash]?src=[source]/[path]`.",
        "Supports the shared mark overlay params (see end of params list)."
      ],
      "params": [
        {
          "description": "Image URL to transform. Exactly one `src` — use /grid for multi-image composition.",
          "name": "src",
          "required": true,
          "type": "string"
        },
        {
          "description": "Output width in pixels.",
          "max": 2048,
          "min": 1,
          "name": "w",
          "required": false,
          "type": "integer"
        },
        {
          "description": "Output height in pixels.",
          "max": 2048,
          "min": 1,
          "name": "h",
          "required": false,
          "type": "integer"
        },
        {
          "description": "Shortcut that sets both `w` and `h` to the same value. Explicit `w`/`h` override.",
          "max": 2048,
          "min": 1,
          "name": "size",
          "required": false,
          "type": "integer"
        },
        {
          "description": "Output aspect ratio in `W:H` form (e.g. `16:9`) or a positive decimal (`1.7777`). With no `w`/`h`/`size`, longest axis = 2048. With one of `w`/`h`, that axis drives. With both, `max(w, h)` becomes the longest axis. Forces `fit=cover` so the source fills the AR box (overrides any explicit `fit`).",
          "name": "ar",
          "required": false,
          "type": "string"
        },
        {
          "default": 1,
          "description": "Device-pixel-ratio multiplier. Resulting dimensions are uniformly downscaled to stay within 4096 px per axis.",
          "max": 2,
          "min": 0,
          "name": "dpr",
          "required": false,
          "type": "number"
        },
        {
          "description": "Image fit mode.",
          "enumValues": [
            "contain",
            "cover",
            "crop",
            "pad",
            "scale-down",
            "squeeze"
          ],
          "name": "fit",
          "required": false,
          "type": "enum"
        },
        {
          "description": "Focal point preserved when `fit=cover` / `fit=crop` trims the image. `center` (default) keeps the geometric center, `auto` lets Cloudflare pick the most salient region (entropy-based), `face` runs face detection and centers on the detected face — useful for portrait thumbnails. `face` falls back to `auto` when no face is detected and has no effect under `contain` / `pad` / `scale-down` since those modes do not crop.",
          "enumValues": [
            "auto",
            "center",
            "face"
          ],
          "name": "gravity",
          "required": false,
          "type": "enum"
        },
        {
          "default": null,
          "description": "Output format. When omitted, `Accept` decides (`image/avif` > `image/webp` > source passthrough). `nocompress` disables Accept-based negotiation and mirrors the source content-type, except WebP / AVIF sources which are re-encoded to PNG.",
          "enumValues": [
            "avif",
            "jpeg",
            "jpg",
            "nocompress",
            "png",
            "webp"
          ],
          "name": "format",
          "required": false,
          "type": "enum"
        },
        {
          "default": false,
          "description": "Serve the original source untouched — bypasses all transforms, watermarking, and SVG rasterization. Any other transform parameters are ignored.",
          "name": "raw",
          "required": false,
          "type": "boolean"
        },
        {
          "default": 95,
          "description": "Encoder quality. Applies to lossy formats.",
          "max": 100,
          "min": 1,
          "name": "q",
          "required": false,
          "type": "integer"
        },
        {
          "default": 0,
          "description": "Gaussian blur radius.",
          "min": 0,
          "name": "blur",
          "required": false,
          "type": "number"
        },
        {
          "description": "Background color for `pad` fit or transparent sources.",
          "name": "bg",
          "required": false,
          "type": "string"
        },
        {
          "default": false,
          "description": "`true` extracts the foreground (removes the background).",
          "name": "bg-remove",
          "required": false,
          "type": "boolean"
        },
        {
          "default": "interpolate",
          "description": "`generate` uses AI upscaling (ESRGAN) when enlarging; `interpolate` (default) is bicubic. `generate` costs 5 credits per call — AI-backed.",
          "enumValues": [
            "generate",
            "interpolate"
          ],
          "name": "upscale",
          "required": false,
          "type": "enum"
        },
        {
          "description": "Mark image URL. Triggers overlay when present.",
          "name": "mark",
          "required": false,
          "type": "string"
        },
        {
          "default": "15%",
          "description": "Mark width. Pixels (e.g. `100`) or percentage of source width (e.g. `50%`).",
          "name": "mark-w",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "default": null,
          "description": "Mark height. Pixels or percentage of source height. No default — aspect ratio is preserved from `mark-w` when omitted.",
          "name": "mark-h",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "description": "Shortcut that sets both `mark-w` and `mark-h` to the same value. Pixels or percentage. Explicit `mark-w`/`mark-h` override.",
          "name": "mark-size",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "default": "contain",
          "description": "How the mark fits into `mark-w` × `mark-h`.",
          "enumValues": [
            "contain",
            "cover",
            "crop",
            "pad",
            "scale-down",
            "squeeze"
          ],
          "name": "mark-fit",
          "required": false,
          "type": "enum"
        },
        {
          "default": "top,left",
          "description": "Comma-separated anchor: one horizontal token (`left`/`right`) plus one vertical token (`top`/`bottom`). E.g. `bottom,right`.",
          "enumValues": [
            "bottom",
            "left",
            "right",
            "top"
          ],
          "name": "mark-align",
          "required": false,
          "type": "csv"
        },
        {
          "default": "2%",
          "description": "Offset from the aligned edge(s). Pixels or percentage of the smaller source dimension.",
          "name": "mark-pad",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "default": 1,
          "description": "Mark opacity (0 = transparent, 1 = opaque).",
          "max": 1,
          "min": 0,
          "name": "mark-opacity",
          "required": false,
          "type": "number"
        },
        {
          "description": "Tile the mark across the canvas. `true` = both axes, `x` = horizontal only, `y` = vertical only.",
          "enumValues": [
            "true",
            "x",
            "y"
          ],
          "name": "mark-repeat",
          "required": false,
          "type": "enum"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/",
      "responseContentType": "image/* (negotiated)",
      "summary": "Resize and re-encode a single source image through Cloudflare Images. Use `?src=` (URL or `<source>/<path>`) at the namespace root, or the path-sugar form `/[source]/[path]`. Supports format negotiation, blur, padding, background removal, AI upscaling, and mark overlay."
    },
    {
      "cacheControl": "public, max-age=31536000, immutable",
      "examples": [
        {
          "description": "2-image grid at 800×600",
          "url": "https://demo.imgapis.com//grid?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&w=800&h=600"
        },
        {
          "description": "4-image classic grid with 8px gap",
          "url": "https://demo.imgapis.com//grid?src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&src=https%3A%2F%2Fpicsum.photos%2Fseed%2Fimg-apis%2F800&w=800&h=800&type=classic&cols=2&gap=8"
        }
      ],
      "method": "GET",
      "notes": [
        "Defaults to image/png when neither `format` nor a modern `Accept` header is present.",
        "Invalid or unreachable sources are dropped silently — supply 1–5 working URLs. Extra sources beyond 5 are ignored, not rejected.",
        "Supports the shared mark overlay params (see end of params list)."
      ],
      "params": [
        {
          "description": "Image URLs to compose. Repeat the param 1–5 times; a single source renders centered as a fallback. Extra sources beyond 5 are ignored, and invalid / unreachable sources are dropped silently.",
          "name": "src",
          "required": "1–5 occurrences",
          "type": "string"
        },
        {
          "description": "Output width in pixels.",
          "max": 2048,
          "min": 1,
          "name": "w",
          "required": true,
          "type": "integer"
        },
        {
          "description": "Output height in pixels.",
          "max": 2048,
          "min": 1,
          "name": "h",
          "required": true,
          "type": "integer"
        },
        {
          "description": "Shortcut that sets both `w` and `h` to the same value. Explicit `w`/`h` override.",
          "max": 2048,
          "min": 1,
          "name": "size",
          "required": false,
          "type": "integer"
        },
        {
          "default": 1,
          "description": "Device-pixel-ratio multiplier.",
          "max": 2,
          "min": 0,
          "name": "dpr",
          "required": false,
          "type": "number"
        },
        {
          "default": "auto",
          "description": "Layout algorithm.",
          "enumValues": [
            "auto",
            "classic",
            "column",
            "row"
          ],
          "name": "type",
          "required": false,
          "type": "enum"
        },
        {
          "default": 2,
          "description": "Column count for `classic` / `column` layouts.",
          "min": 1,
          "name": "cols",
          "required": false,
          "type": "integer"
        },
        {
          "default": 0,
          "description": "Gap in pixels between cells.",
          "min": 0,
          "name": "gap",
          "required": false,
          "type": "integer"
        },
        {
          "default": null,
          "description": "Output format. When omitted, `Accept` decides (`image/avif` > `image/webp` > PNG fallback). `nocompress` disables negotiation and falls back to PNG.",
          "enumValues": [
            "avif",
            "jpeg",
            "jpg",
            "nocompress",
            "png",
            "webp"
          ],
          "name": "format",
          "required": false,
          "type": "enum"
        },
        {
          "description": "Mark image URL. Triggers overlay when present.",
          "name": "mark",
          "required": false,
          "type": "string"
        },
        {
          "default": "15%",
          "description": "Mark width. Pixels (e.g. `100`) or percentage of source width (e.g. `50%`).",
          "name": "mark-w",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "default": null,
          "description": "Mark height. Pixels or percentage of source height. No default — aspect ratio is preserved from `mark-w` when omitted.",
          "name": "mark-h",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "description": "Shortcut that sets both `mark-w` and `mark-h` to the same value. Pixels or percentage. Explicit `mark-w`/`mark-h` override.",
          "name": "mark-size",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "default": "contain",
          "description": "How the mark fits into `mark-w` × `mark-h`.",
          "enumValues": [
            "contain",
            "cover",
            "crop",
            "pad",
            "scale-down",
            "squeeze"
          ],
          "name": "mark-fit",
          "required": false,
          "type": "enum"
        },
        {
          "default": "top,left",
          "description": "Comma-separated anchor: one horizontal token (`left`/`right`) plus one vertical token (`top`/`bottom`). E.g. `bottom,right`.",
          "enumValues": [
            "bottom",
            "left",
            "right",
            "top"
          ],
          "name": "mark-align",
          "required": false,
          "type": "csv"
        },
        {
          "default": "2%",
          "description": "Offset from the aligned edge(s). Pixels or percentage of the smaller source dimension.",
          "name": "mark-pad",
          "required": false,
          "type": "string-or-percent"
        },
        {
          "default": 1,
          "description": "Mark opacity (0 = transparent, 1 = opaque).",
          "max": 1,
          "min": 0,
          "name": "mark-opacity",
          "required": false,
          "type": "number"
        },
        {
          "description": "Tile the mark across the canvas. `true` = both axes, `x` = horizontal only, `y` = vertical only.",
          "enumValues": [
            "true",
            "x",
            "y"
          ],
          "name": "mark-repeat",
          "required": false,
          "type": "enum"
        },
        {
          "default": null,
          "description": "Cache-bust token. Forces a fresh fetch of remote sources, a new browser screenshot capture, and recomputation of the cached response. Omit to use the normal cache.",
          "name": "v",
          "required": false,
          "type": "string"
        }
      ],
      "path": "/grid",
      "responseContentType": "image/* (negotiated; defaults to image/png)",
      "summary": "Compose 1–5 source images into a single grid image. A single source renders centered (fallback); 6+ are capped to 5 rather than rejected. Supports auto / classic / column / row layouts, gaps, and mark overlay."
    }
  ],
  "name": "@feliperohdee/img-apis",
  "notes": [
    "All endpoints accept GET only. OPTIONS requests return 204 with CORS headers.",
    "CORS: `access-control-allow-origin: *`, methods `GET, OPTIONS`, headers `content-type`.",
    "Supported source formats: PNG, JPG, WebP, GIF, AVIF, HEIF, BMP, TIFF, SVG. SVG sources are rasterized to a raster image automatically — they work across transform, info (colors + blurhash), and grid. Rasterization happens whenever the source is an SVG and the `raw` parameter is absent. Note: `<text>` in SVG requires embedded fonts and is not rendered (vector paths only).",
    "Errors are returned as JSON `{ message, status, ... }` with the matching HTTP status (via `use-http-error`)."
  ],
  "repo": "https://github.com/feliperohdee/img-apis"
}