https://hypothesis.sh/photo/600/400 returns a 600×400 photo. Dimensions go in the path, an image comes back. No account, no key, no request body.
/photo/200 random square photo, 200×200
/photo/600/400 random photo at 600×400
/photo/seed/hero/1200/630 the same photo for "hero", every time
/photo/id/7/400 photo 7 from the library
/photo/gen/hero/1200/630 a pattern generated from "hero", not a photoSizes run from 1 to 1600 on either axis. The explorer at /photo builds these URLs from controls and previews them live, and the docs cover every form.
A seeded URL resolves to the same source photo at any size, in any format, with any filter. Snapshot tests and design fixtures can hardcode it without the layout shuffling between runs.
Unseeded requests pick a photo at random and answer with a 302 to that photo's /photo/id/{n}/… URL. If you want to keep the photo you got, keep the URL you were redirected to.
/photo/gen/{seed}/… skips the library and draws the image instead. Colors, placement and shapes all come from the seed, and what comes back is an SVG: nothing is decoded, cropped or resized, so it is the cheapest thing here to serve and a few hundred bytes on the wire.
/photo/gen/hero/1200/630 soft seeded gradient, the default
/photo/gen/hero/1200/630?style=bauhaus seeded circles, arcs and bars
/photo/gen/card/400/300?style=label a drafting card that states its own size
/photo/gen/avatar-3/96?style=noise a seeded fractal-noise fieldA seed is always required here. Every string is a valid seed, so a shorter form would be ambiguous — /photo/gen/600/400 could mean a random 600×400 pattern or seed 600 at 400 px. If you want an arbitrary pattern, make one up.
Patterns are vector, so they stay sharp at any size. Append .jpg or .webp when something downstream cannot take SVG.
?grayscale desaturates. ?blur=1 through ?blur=10 applies a gaussian blur, and the two combine. Both work on photos and patterns alike. Photos default to JPEG and patterns to SVG; append .webp, .jpg or .svg to the size to say otherwise:
/photo/600/400?grayscale
/photo/seed/hero/1200/630?grayscale&blur=3
/photo/600/400.webp
/photo/gen/hero/600/400.jpgEvery response carries Access-Control-Allow-Origin: * and long-lived cache headers, so the images work in img tags, CSS backgrounds, canvas, and cross-origin fetch(). A Photo-ID response header identifies what a response came from — the library image in photo mode, the style and seed hash in procedural mode — and it's readable from cross-origin JavaScript.
The library is 160 AI-generated photographs: landscapes, architecture, food, textures, interiors, still life. There are no model or property releases to worry about, and nothing in it belongs in production content. Source images are 512×512, so large renders are upscales; a 1600px hero will look soft next to real photography, which is the reason sizes stop there.
Patterns have no source image at all — they are drawn to whatever box you ask for, so the size bound applies to them for consistency rather than for detail.