Documentation#
One endpoint, one line of HTML: give icons a domain, get its favicon back as a crisp PNG. This page covers usage, the fallback behavior, caching, rate limits, and how verified tenants get higher limits.
Getting started#
The fastest integration is an image tag:
<img src="https://icon0.eu/domain/example.eu" width="32" height="32" alt="">
That is the whole SDK. No account, no API key, no client library. The same URL works from CSS and from code:
.site-icon {
background-image: url("https://icon0.eu/domain/example.eu");
background-size: 16px 16px;
}
const res = await fetch("https://icon0.eu/domain/example.eu")
const blob = await res.blob() // image/png - CORS is open to every origin
Icon endpoints#
| Endpoint | What |
|---|---|
GET /domain/{domain} |
the domain's favicon as a PNG (anonymous rate limits) |
GET /t/{public_id}/domain/{domain} |
the same, on your tenant path (elevated rate limits once the referring domain is verified) |
Both endpoints always answer with Content-Type: image/png and never with a broken image: if a site has no usable favicon, a deterministic colored letter tile is served instead (see Fallback behavior).
How icons are resolved#
For each domain we look for icons in this order and pick the source that renders best at 32 pixels:
- HTML
<link rel="icon">and<link rel="apple-touch-icon">tags - Web app manifest icons
- A probe of
/favicon.ico
Supported source formats: ICO, PNG, GIF, JPEG, BMP, WebP, and SVG (rasterized server-side). The output is always a PNG.
Sizes. Add ?sz= for other sizes: any value from 16 to 256, default 32 (the square form ?sz=64x64 is tolerated). Requests of 80px and larger are rendered from a high-resolution master (up to 256px, sourced from apple-touch icons, web manifests, or SVG) when the site offers one; smaller requests use the site's small-optimized favicon art, which is usually pixel-hinted for exactly those sizes. 256 is the maximum quality supported.
<img src="https://icon0.eu/domain/github.com?sz=64" width="64" height="64">
Fallback behavior#
When no usable favicon exists, the response is still a valid PNG: a colored letter tile derived deterministically from the domain - the same domain always produces the same tile, so your UI stays stable. Fallback responses are marked with the header:
X-Icons-Fallback: 1
Check for it if you want to treat generated tiles differently from real favicons.
Caching#
Responses are built to be cached hard:
Cache-Control: public, max-age=86400, stale-while-revalidate=604800
ETag: "…" (strong)
Access-Control-Allow-Origin: *
Browsers keep an icon for 24 hours and may serve it stale for up to 7 days while revalidating. Conditional requests with If-None-Match get a 304 Not Modified. In practice, most of your users' requests never reach our servers at all.
Rate limits#
Rate limits exist purely to protect the systems - they are not a business model, and the service is free at every tier.
| Per minute | Per day | |
|---|---|---|
| Anonymous (per IP) | 30 | 1,000 |
| Verified tenant (whole tenant) | 3,000 | 100,000 |
| Verified tenant (per-IP slice) | 300 | 10,000 |
Limiting is done with a salted in-memory hash of the caller's IP; the salt rotates daily at 00:00 UTC and nothing is persisted - see the Privacy policy. Requests over the limit answer 429 Too Many Requests.
Verified tenants#
Higher limits require no payment - only proof that you control the domain your pages load icons from:
- Sign in at icon0.eu/console - your organization becomes your tenant, and the console shows your icon base URL (
https://icon0.eu/t/{public_id}/domain/). - Add your domain in the console (or via the API below).
- Publish the TXT record: create a DNS TXT record at
_icons-verify.<host>whose value is theverify_tokenthe console shows. - Verify: click Verify (or
POST /api/v1/domains/{id}/verify). Fresh DNS changes can take a few minutes to propagate.
From then on, requests to your /t/{public_id}/domain/… base whose Referer or Origin matches a verified domain are served under the tenant limits.
<img src="https://icon0.eu/t/{public_id}/domain/example.eu" width="32" height="32" alt="">
Tenant API reference#
The management API lives under https://icon0.eu/api/v1 and authenticates with the OIDC access token the console itself uses (Authorization: Bearer …). All endpoints are JSON; errors use {"error":{"code":"…","message":"…"}}.
| Endpoint | What |
|---|---|
GET /api/v1/config |
public: auth issuer/client, host, and the current limit values |
GET /api/v1/me |
{tenant_id, public_id, plan, roles, icon_base} |
GET /api/v1/domains |
list your domains, each with verify_token, txt_record, verified_at |
POST /api/v1/domains |
add a domain - {"host":"app.example.eu"} (409 host_taken, 400 invalid/reserved) |
POST /api/v1/domains/{id}/verify |
run the DNS TXT check; returns the domain plus txt_ok |
DELETE /api/v1/domains/{id} |
remove the domain (204) |
Privacy in one paragraph#
We do not log IP addresses, user agents, or referrers. The only per-visitor state anywhere in the system is the salted, in-memory rate-limit hash described above, and its salt rotates daily - even we cannot link yesterday's traffic to today's. Icons are fetched from the target sites and cached as public image data; nothing about the people loading them is stored. Everything runs on servers in the European Union. The full picture: Privacy policy and Compliance.
More#
- About us
- Terms of service · Privacy policy · all legal pages are linked in the footer
- Machine-readable overview: /llms.txt; this page raw: /docs.md