Skip to content
avatar

Avatar API — v1 — stable

Avatars & identicons

Pass any string — a user id, an email hash, a commit sha — and get a stable, unique mark back. Nothing is stored; the same seed always renders the same image.

Request
curl "https://avatar.waldrand.dev/ada.svg?size=256&style=ridge"
The avatar rendered for the seed ada

Seeds & determinism

The seed is everything between the leading slash and the file extension. It is hashed with SHA-256, and the digest is the only source of randomness a style may draw on — so the mark for a seed is fixed, on every machine, in every process, for as long as the style stands.

There is no database and no account. Two callers who send the same seed get the same bytes without ever having met.

  • Any UTF-8 string up to 256 characters. Percent-encode anything a URL would otherwise read as structure — /, ?, #.
  • Seeds are case-sensitive: Ada and ada are two different people.
  • Dots are fine. Only a trailing .svg, .png or .webp is read as a format, so ada.lovelace.png is the seed ada.lovelace.
  • The style is part of the hash, so ?style=grid is a different drawing, not the same one recoloured.

Hash the identifier before sending it if it is personal. An email address in a URL is an email address in every log and cache between you and here; its SHA-256 renders just as well.

Formats

The extension picks the encoder. SVG is the default and the cheapest — it is a few hundred bytes of geometry that scales to any size, which is why size does nothing for it.

PathContent-TypeNotes
/{seed}.svgimage/svg+xmlThe default. Resolution-independent, around 400 bytes.
/{seed}.pngimage/pngRasterised at size. Transparent where bg=none.
/{seed}.webpimage/webpSame pixels, roughly a third of the bytes.

Styles

Four, and no plans for a fifth. Each one is a different way of turning the same digest into a shape; the accent appears exactly once in every mark, which is what keeps a wall of them looking like one set.

ridge

The house mark, redrawn per seed: two ridgelines and the threshold. The default.

grid

The classic identicon — a 5×5 field mirrored down the middle.

initials

One or two letters off the seed on a tinted tile. The only readable style.

rings

Concentric arcs, turned and broken. Holds together at 16px, where grid turns to static.

GET /{seed}.svg

Returns image/svg+xml. The markup carries a <title> and an aria-label holding the seed, so a screen reader announces something better than “image”. There is no script and no external reference in it.

# a 64px mark for a user id, rounded to a circle
curl "https://avatar.waldrand.dev/u_18f2c9.svg?radius=50"

# drop it straight into HTML — no build step, no proxy
<img src="https://avatar.waldrand.dev/u_18f2c9.svg?radius=50" width="64" height="64" alt="">

GET /{seed}.png

Returns image/png, rasterised at size. Use it where SVG is not welcome — email, OpenGraph cards, anything that ends up in a native image view. Swap the extension for .webp to send a third of the bytes.

# 512px, transparent ground, saved to disk
curl "https://avatar.waldrand.dev/ada.png?size=512&bg=none" -o ada.png

# same seed, same pixels, smaller file
curl "https://avatar.waldrand.dev/ada.webp?size=512" -o ada.webp

Parameters

NameTypeDefaultNotes
seedstringrequiredIn the path. Any UTF-8 string up to 256 characters.
size16–1024256Pixel edge length. Ignored for SVG.
styleenumridgeridge · grid · initials · rings
radius0–500Corner rounding in percent. 50 = circle.
bghexautoBackground fill, or none for transparent.

Rate limits

Rate limit — per IP

60req / min

The bucket holds four minutes’ worth, so a page of 200 avatars loads in one go; it refills at one a second. Only fresh renders count: a 304 or an avatar already in the server’s cache is free, and browser-cached images never reach us at all.

Response headers

X-RateLimit-Limit:
60
X-RateLimit-Remaining:
237
X-RateLimit-Reset:
1738 (epoch s)
Retry-After:
on 429 only

Every rendered avatar comes back with a strong ETag and Cache-Control: public, max-age=2592000, immutable. A conditional request that matches is answered with a 304 and costs you nothing to render.

Errors

The happy path is an image; everything else is JSON, because a caller holding an error would rather parse it than read it.

StatusCodeWhen
400bad_seedEmpty, over 256 characters, or not valid percent-encoding.
400bad_paramA parameter is out of range or not a known value. The body names which.
400unknown_formatAn extension other than .svg, .png or .webp.
405method_not_allowedAnything but GET, HEAD or OPTIONS.
429rate_limitedOver the ceiling. Retry-After says how long.
{
  "error": {
    "status": 400,
    "code": "bad_param",
    "param": "size",
    "message": "size must be between 16 and 1024, got 4096."
  },
  "docs": "https://avatar.waldrand.dev/"
}