Issue Nº 39 — Sep 23, 2026

A random photo from this API never caches. A seeded one caches for a day

Issue thirty-nine covers Lorem Picsum, a keyless API that serves Unsplash photos as placeholder images. This session queried it live. A plain size request redirects to a random photo and tells caches to skip it. A seed or an ID gets the same kind of redirect, but caches for 24 hours. The image file itself sits behind a signed URL, and the signature is required.

Random and seeded requests redirect the same way, but cache differently

This session sent GET https://picsum.photos/200/300 live, with an Origin header. It returned 302 to https://fastly.picsum.photos/id/737/200/300.jpg?hmac=.... The photo ID changed on each call. The response carried cache-control: private, no-cache, no-store, must-revalidate, so no cache may keep the redirect.

GET https://picsum.photos/seed/abc/200 returned 302 to a fixed photo for that seed. GET https://picsum.photos/id/0/200 did the same. Both carried cache-control: public, max-age=86400, stale-while-revalidate=60, stale-if-error=43200. A seed or an ID makes the redirect cacheable for one day.

The docs add one more tip for random images. To show several same-size images, they tell you to add a random query parameter, such as ?random=1 and ?random=2. That stops the browser from reusing one image for every tag. GET https://picsum.photos/200/300?random=1 still returned the no-store header.

The docs describe /seed/ as a way to get the same image every time. They do not mention the cache header. Use a seed or an ID when you want placeholders that stay the same for a day. Use a plain size, or ?random=N, when you want a new photo each time.

The image URL only works with a signature from the redirect

Each redirect from picsum.photos points to a fastly.picsum.photos URL with an hmac query parameter. This session sent GET to that URL with the hmac removed. It returned 400 with a text/plain body: Invalid parameters.

The same URL with its matching hmac returned 200 and image/jpeg. The response had a picsum-id header. It also had cache-control: public, max-age=2592000, stale-while-revalidate=60, stale-if-error=43200, immutable. So the file itself caches for 30 days.

The hmac must match the exact path. An hmac copied from a 200x200 redirect, sent with a 200x300 path, also returned 400. The picsum.photos redirect sets how long the redirect can cache. The fastly.picsum.photos URL decides whether to serve the file, and it checks the signature first.

A client cannot build its own fastly.picsum.photos URL from a photo ID and a size. It has no way to make a valid hmac, so the request gets a 400. Always start at picsum.photos and follow the redirect. The redirect is where the URL gets its signature.

Errors come back as plain text, and the docs give no size limit

This session sent GET https://picsum.photos/6000/6000 live. It returned 400 with a text/plain body: Invalid size. The docs page names no maximum size. A bisection found the limit: 5000 returned 302, and 5001 returned 400.

GET https://picsum.photos/200?grayscale&blur=11 returned 400, text/plain, Invalid blur amount. The same URL with blur=10 returned a normal 302. The docs give the blur range as 1 to 10, and the probes agree.

The docs do not describe GET https://picsum.photos/0/0. It returned 302 to a photo at its original size, for example 3333/5000.jpg for photo 758. It is not an error. It gives you the full-size file.

The docs say the Link header of /v2/list carries next and previous page links. With limit=100, page 9 had both rel="prev" and rel="next". Page 10 had 93 rows and only rel="prev", so the missing next link marks the last page. Page 11 returned 200 with an empty array. The response also sends access-control-expose-headers: Link, so browser code can read the header. The catalogue holds 993 photos.

Lorem Picsum, by the numbers

Rendered live from the atlas entry
AuthenticationNone required
HTTPSSupported
CORSEnabled
PricingFree
FormatsJPEG, WebP, JSON
lorem-ipsumplaceholder-imagesimage-generationunsplashkeyless

Sources

Facts checked Sep 2026