Skip to content

Avatune API

The Avatune API provides a REST endpoint for generating avatar SVGs on-demand, deployed on Cloudflare Workers at https://api.avatune.dev.

Try the API right here:

import { createClient } from "@avatune/api-client";
const client = createClient();
const svg = await client.getAvatar({
theme: "yanliu",
seed: "user-123",
size: 200,
});
https://api.avatune.dev/
Terminal window
GET https://api.avatune.dev/?theme=nevmstas&hair=short

The API returns an SVG image that can be used directly in <img> tags or embedded in your HTML:

<img src="https://api.avatune.dev/?theme=yanliu&seed=user123" alt="Avatar" />
ParameterDescriptionExample
themeTheme nameyanliu, nevmstas, miniavs, micah, kyute, fatin-verse, pacovqzz
ParameterDescriptionDefault
sizeAvatar size in pixelsTheme default
seedRandom seed for generationRandom
backgroundColorBackground color (hex)Theme default

The API enforces rate limits per IP address + User-Agent to ensure fair usage:

  • 100 requests per hour
  • 1,000 requests per day

Limits reset automatically via KV TTL expiration.

When rate limits are exceeded, the API returns:

  • Status Code: 429 Too Many Requests
  • Header: Retry-After: 3600 (seconds until reset)
  • Response Body:
{
"error": "Rate limit exceeded",
"retryAfter": 3600
}

All successful responses include cache headers for optimal performance:

Cache-Control: public, max-age=31536000, immutable

This means:

  • Responses are cached for 1 year
  • Content is immutable (won’t change)
  • Can be cached by browsers and CDNs

The API supports Cross-Origin Resource Sharing (CORS), allowing requests from any origin:

Access-Control-Allow-Origin: *
{
"error": "Invalid theme. Available themes: yanliu, nevmstas, miniavs, micah, kyute, fatin-verse, pacovqzz"
}

Status Code: 400 Bad Request

{
"error": "Rate limit exceeded",
"retryAfter": 3600
}

Status Code: 429 Too Many Requests

For type-safe API access, use the official @avatune/api-client package:

Terminal window
npm install @avatune/api-client
import { createClient } from "@avatune/api-client";
const client = createClient();
// Get avatar SVG with full type safety
const svg = await client.getAvatar({
theme: "yanliu",
seed: "user-123",
hair: "braids", // TypeScript autocompletes available options
hairColor: "#8B4513",
body: "sweaterVest",
});
// Get URL for <img> src (no network request)
const url = client.getAvatarUrl({
theme: "nevmstas",
seed: "user-456",
size: 300,
});

Each theme has its own typed interface with autocomplete for available parts:

import {
createClient,
type YanliuParams,
type NevmstasParams,
} from "@avatune/api-client";
const client = createClient();
// Yanliu theme with typed parameters
const yanliuAvatar = await client.getAvatar({
theme: "yanliu",
hair: "braids", // 'braids' | 'hijab' | 'medium' | 'puff' | ...
body: "blouse", // 'blouse' | 'sweaterVest' | 'teeBasic' | ...
glasses: "glass", // optional
});
// Nevmstas theme with different options
const nevmstasAvatar = await client.getAvatar({
theme: "nevmstas",
hair: "short", // 'short' | 'long' | 'mohawk' | 'pixie' | ...
eyes: "boring", // 'boring' | 'dots' | 'round' | ...
});
import { createClient, AvatuneApiError } from "@avatune/api-client";
const client = createClient();
try {
const svg = await client.getAvatar({ theme: "yanliu", seed: "test" });
} catch (error) {
if (error instanceof AvatuneApiError) {
if (error.isRateLimited) {
console.log(`Rate limited. Retry after ${error.retryAfter} seconds`);
} else {
console.log(`API error: ${error.data.error}`);
}
}
}

For self-hosted instances:

const client = createClient({
baseUrl: "https://your-api.example.com",
timeout: 5000,
});
import { AvatuneClient } from "@avatune/api-client";
console.log(AvatuneClient.themes);
// ['yanliu', 'nevmstas', 'miniavs', 'micah', 'kyute', 'fatin-verse', 'pacovqzz']
<img
src="https://api.avatune.dev/?theme=yanliu&seed=user-123"
alt="User Avatar"
width="200"
height="200"
/>
const avatarUrl = `https://api.avatune.dev/?theme=nevmstas&seed=${userId}&size=300`;
const response = await fetch(avatarUrl);
const svgText = await response.text();
function UserAvatar({ userId, theme = "yanliu", size = 200 }) {
const avatarUrl = `https://api.avatune.dev/?theme=${theme}&seed=${userId}&size=${size}`;
return <img src={avatarUrl} alt="Avatar" width={size} height={size} />;
}
<template>
<img :src="avatarUrl" alt="Avatar" :width="size" :height="size" />
</template>
<script setup>
import { computed } from "vue";
const props = defineProps({
userId: String,
theme: { type: String, default: "yanliu" },
size: { type: Number, default: 200 },
});
const avatarUrl = computed(
() =>
`https://api.avatune.dev/?theme=${props.theme}&seed=${props.userId}&size=${props.size}`
);
</script>
.avatar {
width: 200px;
height: 200px;
background-image: url("https://api.avatune.dev/?theme=miniavs&seed=user-456");
background-size: cover;
border-radius: 50%;
}

The Avatune API is built with:

  • Cloudflare Workers - Edge computing for low latency
  • Cloudflare KV - Distributed key-value storage for rate limiting
  • Global CDN - Deployed to 300+ locations worldwide

Want to run your own instance? Check out the source code:

Terminal window
cd apps/cloudflare-worker
bun install
bun run dev

For deployment instructions, see the GitHub repository.

  1. Use Seeds for Consistency: Always use the same seed parameter for a specific user to ensure their avatar remains consistent
  2. Cache Responses: Take advantage of the long cache headers to minimize API calls
  3. Respect Rate Limits: Implement proper error handling for 429 responses
  4. URL Encode Parameters: Always URL encode color values and other special characters
const params = new URLSearchParams({
theme: "yanliu",
seed: "user-123",
hairColor: "#FF5733", // Will be properly encoded
backgroundColor: "#3498DB",
});
const url = `https://api.avatune.dev/?${params}`;