HTTP API

Hosted at https://api.navii.dev. No auth, public CORS, fully cacheable. Plain text errors, image responses for everything else.

Compatibility promise

Every endpoint, query param, response format, and response header documented as of v0.24.x stays available on the hosted API. Existing URLs such as https://api.navii.dev/avatar/alice keep working unauthenticated.

Future Pro features are additive: they use Authorization: Bearer <polar_license_key> only when you request a Pro-only capability. Get a license at https://navii.dev/pro. The free API remains anonymous, and hosted free-tier rate limits will not tighten beyond the currently published limits.

Why so strict? Avatar responses use cache-control: public, max-age=31536000, immutable. Once a URL is in customer HTML, browsers and CDNs may hold it for a year. Navii treats those URLs as a public contract.

GET /avatar/:seed[.svg|.png]

Returns a deterministic mascot avatar for the given seed. Same seed → same avatar, byte-for-byte. Append .png to the seed to receive a rasterized PNG instead of SVG.

Path

ParamTypeDescription
:seedstringAny unique identifier. Use a stable user id, UUID, or email. Avoid display names — see the seed rule.

Query

ParamTypeDefaultDescription
sizeint96Output size in px. Clamped to 16–1024.
paletteenumseededForce a color family. See palette catalog.
backgroundenumseedednone · solid · ring.
tileBgcolornoneOpaque circular fill behind avatar. Any CSS color (URL-encoded, e.g. %23ffffff) or auto to use the palette accent.
moodenumseededneutral · happy · serious · sleepy · wink. Overrides seed-derived eyes + mouth with a curated pair. Same seed + mood = byte-identical render.
packscsvnoneComma-separated pack ids — e.g. accra-gallery, command-center. Premium themed bodies, palettes, accessories, and outfits. Unknown ids are silently skipped. Order doesn't affect cached output.
styleenumseededmasc · femme · neutral. Style-hint bias on seeded picks. Only meaningful alongside packs; harmless otherwise.
titlestringnoneAccessible label. Adds role="img" + aria-label to the SVG root.
animated0 / 10Idle motion (float, blink, antenna sway, spark pulse, twinkle). SVG only — ignored for PNG. Honors prefers-reduced-motion.

Examples

https://api.navii.dev/avatar/alice
https://api.navii.dev/avatar/alice?palette=violet&animated=1
https://api.navii.dev/avatar/alice?tileBg=%23ffffff
https://api.navii.dev/avatar/alice?mood=happy
https://api.navii.dev/avatar/alice?packs=accra-gallery
https://api.navii.dev/avatar/alice?packs=command-center&style=neutral
https://api.navii.dev/avatar/alice.png?size=512&tileBg=auto

GET /random[.png]

Returns a fresh avatar inline — same URL, different avatar every refresh. Internally picks a new UUID seed per request and renders directly. No redirect. Point an <img src="/random"> at it and every page refresh swaps the avatar.

Query

All /avatar/:seed params apply (size, palette, background, tileBg, mood, packs, style, title, animated) — same semantics, same clamps, same enums.

Response headers

  • x-navii-seed — the seed that was chosen. Read it from a fetch() response if you want to persist the avatar (e.g. save to user profile so it's stable on next visit).
  • cache-control: no-store — never cached by the browser or CDN. Refresh = new avatar.
  • access-control-allow-origin: * + access-control-expose-headers: x-navii-seed — embed anywhere; cross-origin JS can read the seed header.

Examples

https://api.navii.dev/random
https://api.navii.dev/random?palette=mint&size=128
https://api.navii.dev/random.png?size=256

Persisting the chosen seed — useful for onboarding flows where the user gets an avatar without picking one, then keeps it forever:

const res = await fetch('https://api.navii.dev/random');
const seed = res.headers.get('x-navii-seed');
const svg = await res.text();
await db.users.update(user.id, { naviiSeed: seed });

GET /group

Renders multiple seeded avatars as a single horizontally-stacked SVG with optional overlap and a +N counter tile for overflow.

Query

ParamTypeDefaultDescription
seedscsvComma-separated seeds (up to 50). Required.
sizeint64Per-tile size in px. Clamped to 16–256.
overlapfloat0.3Fraction each tile overlaps the previous. 0 = no overlap, 0.7 = heavy stack.
maxintallMax tiles to render. Extra seeds collapse into a +N tile.
ringcolor#ffffffBorder color around each tile.
tileBgcolor#ffffffOpaque fill behind each avatar (prevents overlap show-through).
animated0 / 10Per-avatar animation.

SDK-only: counterFill and counterInk (the +N tile's colors) are settable via GroupOptions in @usenavii/core but not yet wired to query params.

Utility endpoints

PathDescription
GET /Landing page with live playground.
GET /apiService metadata as JSON. Returns { "name": "navii", "version": "...", "endpoints": {...} }.
GET /healthzLiveness probe. Returns { "ok": true, "pngCacheSize": N }.
GET /galleryHTML grid of N seeded avatars (visual debug).
GET /favicon.svgBrand favicon. SVG.
GET /apple-touch-icon.png180×180 dark-tile icon for iOS home-screen.
GET /og.png1200×630 Open Graph image. No params.
GET /robots.txt, /sitemap.xmlSEO essentials.

Response headers

All image responses set:

  • cache-control: public, max-age=31536000, immutable — safe to cache forever (seed + params fully determine bytes).
  • access-control-allow-origin: * — embed anywhere, no preflight for GET.
  • content-type: image/svg+xml; charset=utf-8 for SVG, image/png for PNG, application/json for /api + /healthz.

Rate-limited routes additionally emit:

  • x-ratelimit-limit — max requests in the current window.
  • x-ratelimit-remaining — remaining requests.
  • x-ratelimit-reset — Unix epoch seconds when the window resets.
  • retry-after — only on 429 responses.

The avatar route emits a warning when the seed shape suggests PII:

  • x-navii-warning: plaintext-email-seed; hash with seedFromEmail() — set when the seed matches an email pattern. Avatar still renders; hash the email client-side with seedFromEmail() to drop the warning.

Errors

Plain-text bodies. Status codes:

StatusMeaningBody
400Bad requestseed required · seeds required (comma-separated)
429Rate limitedRate limit exceeded
501Not implementedPNG rasterization unavailable: ... — server missing @resvg/resvg-js. SVG endpoint still works.

Rate limits

/avatar/* is limited to 600 req/min/IP. Other routes are unlimited. Exceeded → HTTP 429 + Retry-After.

Full table, cache rationale, and self-hosting tuning live on the dedicated Rate limits page.

Seeds and URL encoding

Anything you can put in a URL path can be a seed. Seeds with @, ., or other URL-special chars work — just URL-encode them on the client (most browsers do this automatically inside <img src>).

raw:     alice@example.com
encoded: alice%40example.com

The server decodes back to the raw seed before hashing, so both URLs produce the same SVG. Empty seeds → 400.

Versioning and stability

The deterministic contract is scoped to a single release of the engine. A given seed + a given engine version → byte-identical SVG, forever. We won't silently change that.

What we promise to keep stable in patch + minor releases:

  • Existing seeds' part selections don't shift when new variants are added (new parts append to the PRNG stream, never insert).
  • Endpoint URLs, query params, response formats, and response headers stay backwards-compatible; Pro-only features are additive and gated only when requested.
  • SVG markup may change in tiny non-visible ways (formatting, attribute order) — treat as text-content stable, not byte-stable across upgrades.

What can change in a major release:

  • Cast rebases (existing seeds get new combinations). Documented in the changelog.
  • Default option values.

If you need absolute byte-stability across engine upgrades, mirror the SVG bytes locally — see the caching recipe.

Authentication

None for the documented free API. CORS is wide open (access-control-allow-origin: *) — embed from anywhere, no token, no signup. Future Pro-only capabilities may accept Authorization: Bearer <polar_license_key>, but that auth requirement applies only to the Pro-only capability being requested. Get a license at https://navii.dev/pro. If you need to lock down a self-hosted deployment, put it behind your own auth layer (Cloudflare Access, BasicAuth via Caddy, etc.).