Real photos & placeholders
by URL.

A self-hosted image API. Ask for a keyword and a size, get a real Unsplash photo served from local storage โ€” cached, resized, instant. Falls back to a clean generated placeholder when no match exists.

mountain water forest sunset

โšกQuick start

Two endpoints. No keys. No SDK. Drop a URL into any <img> tag.

Real photo by keyword

Pulls a matching photo, resizes & center-crops it, caches the result.

GET /api/photo/nature/800/600

Generated placeholder

Random gradient or solid color, optional text and rounded corners.

GET /api/400/300?color=6366f1&text=Hello

๐Ÿ–ผ๏ธReal photo examples

All images below are live calls to this server. Keywords come from a curated set of ~24,000 tags. Add ?seed=anything to lock the same photo across requests.

๐Ÿ“กAPI Reference โ€” Photos

Returns JPEG. Falls back to a generated placeholder when no photo is found for the keyword.

GET/api/photo/{keyword}/{width}/{height}
ParamTypeDefaultDescription
keywordpathโ€” Subject of the photo. Examples: nature, mountain, water, animal, sunset, forest.
widthpath ยท intโ€” 1โ€“2000 pixels.
heightpath ยท intโ€” 1โ€“2000 pixels.
seedquery ยท stringrandom Stable photo selection. Same seed โ†’ same photo across requests.
grayscalequery ยท boolfalse Convert to black & white.

๐ŸŽจAPI Reference โ€” Generator

Pure programmatic placeholders โ€” no photo lookup. Useful for layouts, design mocks, fallbacks.

GET/api/{width}/{height}
ParamTypeDefaultDescription
textstringplaceholderCentered text label.
colorstringgradient6-char hex (ff0000) or named color (red, skyblue, orange...).
radiusint0Rounded corner radius in pixels (SSAA-smoothed).
grayscaleboolfalseBlack & white.
hide_sizeboolfalseHide the WxH suffix from text.
no_textboolfalseDisable all text rendering.

โœจGenerator examples

When you don't need a photo โ€” or when one isn't available.

๐Ÿš€Why this exists

Because every project needs placeholders, and most public services rate-limit or disappear.

๐Ÿ“ฆ

Self-hosted

Single static Go binary plus a SQLite file. Air-gap friendly.

โšก

Cached resizes

First request resizes from the master. Every request after is a static file read.

๐ŸŽฏ

Stable picks

Same seed always returns the same photo. Same URL โ†’ same bytes.

๐Ÿ›Ÿ

Graceful fallback

No photo for the keyword? You still get a clean gradient with the keyword as label.