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 librarySizes 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.
?grayscale desaturates. ?blur=1 through ?blur=10 applies a gaussian blur, and the two combine. JPEG is the default; append .webp to the size for WebP:
/photo/600/400?grayscale
/photo/seed/hero/1200/630?grayscale&blur=3
/photo/600/400.webpEvery 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 names the photo a response came from, and it's readable from cross-origin JavaScript.
The library is AI-generated photography: landscapes, architecture, food, textures. 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.