Get the highest resolution favicon for any domain - or look up a service icon by name.
A simple JSON API to fetch a normalized 128×128 PNG favicon for any domain.
One GET request returns a JSON object containing a CDN URL to a normalized 128×128 PNG favicon plus cache metadata. The PNG itself is served separately by /cdn/favicons/{domain}.png and is cached for 7 days.
Send a real request to your own instance from the browser. The response JSON and the resulting CDN-cached PNG are shown below.
(no response yet)
Every call to /api/v1/favicon needs an API key. Only the SHA-256 hash of each key is stored server-side - the raw key is shown exactly once at creation time.
Pass the key in one of two ways:
Keys are generated and managed through the bundled scripts/manage-keys.js CLI:
# Create a key for "customer A" on the pro plan docker compose exec maflplus-favicon-api npm run keys:create -- --label "customer A" --plan pro # List active keys with this month's usage counter docker compose exec maflplus-favicon-api npm run keys:list # Revoke a key by its visible prefix (kept in DB for audit history) docker compose exec maflplus-favicon-api npm run keys:revoke -- --prefix fa_abcdefgh
| Name | Required | Description |
|---|---|---|
url |
Yes | Absolute URL (or bare domain) of the website to look up. The host is extracted, lowercased and validated. |
key |
If keys are required | Optional alternative to the Authorization: Bearer header. Ignored when this instance does not require a key. |
The image bytes are not in the response body. Fetch the PNG from the url field instead - it points to /cdn/favicons/{domain}.png with a long Cache-Control.
| Field | Type | Description |
|---|---|---|
url | string | Public CDN URL of the cached PNG (/cdn/favicons/{domain}.png). |
domain | string | Normalized hostname extracted from the input. |
width / height | number | Always 128 × 128. |
format | string | Always "png". |
sourceType | string | Where the icon came from, in priority order: svg > manifest > apple-touch-icon > png > og-image > catalog fallbacks > external. |
cached | boolean | true if served from the 7-day disk cache, false if just generated. |
cachedAt | string (ISO-8601) | Timestamp of when the PNG was stored. |
All error responses are JSON with the shape { "error": ..., "code": ... }, plus extra context fields where useful (such as plan, limit, used, period on quota errors).
| Status | Code | Meaning |
|---|---|---|
| 400 | missing_urlinvalid_url | The url query parameter is missing or could not be parsed. |
| 401 | missing_api_keyinvalid_api_key | No key was sent, or the provided key is not recognized / has been revoked. |
| 422 | favicon_not_foundfavicon_not_processable | No usable icon could be discovered for the domain, or the bytes could not be decoded. |
| 429 | quota_exceeded | The monthly call quota for this key has been reached. Resets on the 1st of next month (UTC). |
| 500 | internal_error | Unexpected server error. |
A request counts toward the monthly quota only when the API returns 200.
Each API key is assigned a plan at creation time. A monthly limit of 0 means unlimited. Quotas reset on the 1st of every month (UTC).
| Plan | Monthly calls |
|---|---|
free | … |
pro | … |
enterprise | … |
The url field in every successful response points here. It is publicly readable, requires no API key, and returns 404 until /api/v1/favicon has been called at least once for that domain.
image/pngpublic, max-age=604800, immutable (7 days by default)