Skill for unavatar-api
Resolve user avatars from 25+ platforms via a single API endpoint.
User intent
- display profile pictures from GitHub
- X/Twitter
- Apple Music
- other platforms
- look up avatars by email/username/domain/phone number
Installation
npx skills add https://github.com/microlinkhq/skills --skill unavatar-api
# unavatar API unavatar.io resolves user avatars from 25+ platforms via a single endpoint. Supports lookup by email, username, domain, or phone number. ## Endpoint `https://unavatar.io` ## Quick Start All lookups use the `/:provider/:key` format: | Input | Pattern | Example | | -------- | ---------------------------- | ------------------------------------------------ | | email | `/provider/[email protected]` | `https://unavatar.io/gravatar/[email protected]` | | username | `/provider/username` | `https://unavatar.io/github/kikobeats` | | domain | `/provider/domain.com` | `https://unavatar.io/google/reddit.com` | | phone | `/provider/phonenumber` | `https://unavatar.io/whatsapp/34612345678` | ## Authentication - **Free**: No auth required, 50 requests/day per IP - **Pro**: Pass `x-api-key` header, usage-based billing at $0.001/token ```bash curl -H "x-api-key: YOUR_API_KEY" https://unavatar.io/instagram/kikobeats ``` ### Proxy Tiers & Token Cost | Proxy tier | Tokens | Cost | | ----------- | :----: | :----: | | Origin | 1 | $0.001 | | Datacenter | +2 | $0.003 | | Residential | +4 | $0.007 | Response headers: `x-proxy-tier`, `x-unavatar-cost`, `x-pricing-tier`. ### Key Management - **Usage**: `curl -H "x-api-key: KEY" https://unavatar.io/key/usage` - **Rotate**: `curl -H "x-api-key: KEY" https://unavatar.io/key/rotate` ## Query Parameters ### ttl - Type: `number` | `string` - Default: `'24h'` - Range: `'1h'` to `'28d'` Cache duration for the resolved avatar. The avatar is considered fresh until TTL expiration, then recomputed on next request. e.g., `https://unavatar.io/kikobeats?ttl=1h` ### fallback - Type: `string` | `boolean` Custom fallback image when avatar can't be resolved. Accepts: - URL to external avatar service (boringavatars.com, avatar.vercel.sh) - URL to static image - Base64 encoded image (e.g., transparent 1x1 pixel GIF) - `false` to disable fallback (returns 404 instead) ``` https://unavatar.io/github/37t?fallback=https://avatar.vercel.sh/37t?size=400 https://unavatar.io/github/37t?fallback=false https://unavatar.io/github/37t?fallback=data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw== ``` ### json - Type: `boolean` Returns JSON payload instead of image content. e.g., `https://unavatar.io/kikobeats?json` ## Response Format When `json=true` is passed, responses follow a predictable shape: | Field | Type | Present in | Description | | ------- | ------------ | ------------------------- | ----------------------------------------------- | | status | string | all JSON responses | One of: `success`, `fail`, `error`. | | message | string | all JSON responses | Human-readable summary for display/logging. | | data | object | success | Response payload for successful requests. | | code | string | fail, error | Stable machine-readable error code. | | more | string (URL) | most fail/error responses | Documentation URL with troubleshooting details. | | report | string | some error responses | Support contact channel (e.g., mailto:). | ## Response Headers | Header | Purpose | | ---------------------- | ------------------------------------------------------- | | x-pricing-tier | `free` or `pro` — the plan used for this request | | x-timestamp | Server timestamp when request was received | | x-unavatar-cost | Token cost of the request (avatar routes only) | | x-proxy-tier | Proxy tier used: `origin`, `datacenter`, or `residential` | | x-rate-limit-limit | Maximum requests allowed per window (free tier only) | | x-rate-limit-remaining | Remaining requests in current window (free tier only) | | x-rate-limit-reset | UTC epoch seconds when window resets (free tier only) | | retry-after | Seconds until rate limit resets (only on 429 responses) | ## Response Errors Client-side issues return `status: "fail"` (HTTP 4xx). Service-side issues return `status: "error"` (HTTP 5xx). | HTTP | Code | Typical trigger | | ---- | ------------------ | ----------------------------------------- | | 400 | ESESSIONID | Missing session_id in /checkout/success | | 400 | ESESSION | Checkout session not paid or not found | | 400 | ESIGNATURE | Missing stripe-signature header | | 400 | EWEBHOOK | Invalid/failed Stripe webhook processing | | 400 | EAPIKEYVALUE | Missing apiKey query parameter | | 400 | EAPIKEYLABEL | Missing label query parameter | | 401 | EEMAIL | Invalid or missing authenticated email | | 401 | EUSERUNAUTHORIZED | Missing/invalid auth for protected routes | | 401 | EAPIKEY | Invalid x-api-key | | 403 | ETTL | Custom ttl requested without pro plan | | 403 | EPRO | Provider restricted to pro plan | | 404 | ENOTFOUND | Route not found | | 404 | EAPIKEYNOTFOUND | API key not found | | 409 | EAPIKEYEXISTS | Custom API key already exists | | 409 | EAPIKEYLABELEXISTS | API key label already exists | | 409 | EAPIKEYMIN | Attempt to remove last remaining key | | 429 | ERATE | Free-tier daily rate limit exceeded | | 500 | ECHECKOUT | Stripe checkout session creation failed | | 500 | EAPIKEYFAILED | API key retrieval after checkout failed | | 500 | EINTERNAL | Unexpected internal server failure | ## Providers Providers are grouped by input type. | Provider | email | username | domain | phone | | ------------- | :---: | :------: | :----: | :---: | | Apple Music | | ✓ | | | | Bluesky | | ✓ | | | | DeviantArt | | ✓ | | | | Dribbble | | ✓ | | | | DuckDuckGo | | | ✓ | | | GitHub | | ✓ | | | | GitLab | | ✓ | | | | Google | | | ✓ | | | Gravatar | ✓ | | | | | Instagram | | ✓ | | | | Microlink | | | ✓ | | | OnlyFans | | ✓ | | | | OpenStreetMap | | ✓ | | | | Patreon | | ✓ | | | | Reddit | | ✓ | | | | SoundCloud | | ✓ | | | | Spotify | | ✓ | | | | Substack | | ✓ | | | | Telegram | | ✓ | | | | TikTok | | ✓ | | | | Twitch | | ✓ | | | | Vimeo | | ✓ | | | | WhatsApp | | | | ✓ | | X/Twitter | | ✓ | | | | YouTube | | ✓ | | | ### URI Format Providers **Apple Music** supports `type:id` format. Without explicit type, it searches `artist` then `song`: - by name: `/apple-music/daft%20punk` - `artist`: `/apple-music/artist:daft%20punk` or `/apple-music/artist:5468295` - `album`: `/apple-music/album:discovery` or `/apple-music/album:78691923` - `song`: `/apple-music/song:harder%20better%20faster%20stronger` or `/apple-music/song:697195787` **Spotify** supports `type:id` format (default type: `user`): - `user`: `/spotify/kikobeats` - `artist`: `/spotify/artist:6sFIWsNpZYqbRiDnNOkZCA` - `playlist`: `/spotify/playlist:37i9dQZF1DXcBWIGoYBM5M` - `album`: `/spotify/album:4aawyAB9vmqN3uQ7FjRGTy` - `show`: `/spotify/show:6UCtBYL29hRg064d4i5W2i` - `episode`: `/spotify/episode:512ojhOuo1ktJprKbVcKyQ` - `track`: `/spotify/track:11dFghVXANMlKmJXsNCbNl` **WhatsApp** supports `type:id` format (default type: `phone`): - `phone`: `/whatsapp/34612345678` - `channel`: `/whatsapp/channel:0029VaABC1234abcDEF56789` - `chat`: `/whatsapp/chat:ABC1234DEFghi` - `group`: `/whatsapp/group:ABC1234DEFghi` **YouTube** accepts handle, legacy username, or channel ID. Input starting with `UC` and 24 characters long is treated as a channel ID: - handle: `/youtube/casey` or `/youtube/@casey` - channel ID: `/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw` For individual provider examples, see [providers.md](providers.md).