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}`;

For issues or questions about the API: