A favicon API is a web service you call from code to turn one image into a complete set of website icons, instead of doing it by hand in a browser tool. You send the source image in an HTTP request and get back the finished files: a layered favicon.ico, PNG icons at every size, an apple touch icon, the web app manifest icons, a manifest and the HTML tags that link them. It is the same job as a favicon maker, done by a script, a build step or your own product.
This guide shows how the Faviconize favicon API works end to end: authentication, the request, the JSON response field by field, the download, errors and metering, plus three patterns for wiring it into real work. The response fields and file names below are the ones our endpoint actually returns, taken from the code that serves it.
When a favicon API is worth it
A favicon API is worth it when icons are made often enough, or by enough people, that a manual step becomes a cost or a risk. Typical cases:
- An agency or studio that launches many client sites and wants every one to ship with a correct icon set.
- A site builder or SaaS product where your own customers upload a logo and expect their site, store or status page to have a favicon.
- A build pipeline that should regenerate icons whenever the logo file in the repository changes, so nobody ships a stale icon.
- A design system where the icon is one more artifact produced from a single source of truth.
For one site, the free favicon maker on the home page does the same thing with no account. The API is part of the paid plans, listed on the pricing page.
Favicon API authentication
The favicon API authenticates every request with an API key sent as a bearer token. You create keys on your dashboard. A key is shown once, at creation, and only a hash of it is stored on our side, so a lost key cannot be recovered: you revoke it and create a new one.
Send the key in the Authorization header:
Authorization: Bearer sk_your_key_here
An x-api-key: sk_your_key_here header works as well. A missing, unknown or revoked key gets a 401 response. Keep keys out of your repository: store them in your CI secrets or environment variables and read them at run time.
The request: generate an icon package
To generate an icon package, send a POST to /api/generate with a multipart/form-data body. One call takes one image and returns one package.
| Field | Required | What it does |
|---|---|---|
file |
yes | The source image: PNG, JPG, WebP or SVG |
name |
no | App name written into the manifest |
shortName |
no | Short app name written into the manifest |
themeColor |
no | Theme color for the manifest and the theme-color meta tag, as a hex value |
backgroundColor |
no | Manifest background color, also the fill behind the maskable icon |
basePath |
no | Path prefix for the snippet and manifest, if the icons live in a subfolder |
With curl:
curl -X POST https://faviconize.com/api/generate \
-H "Authorization: Bearer $API_KEY" \
-F "file=@logo.png" \
-F "name=Acme" \
-F "shortName=Acme" \
-F "themeColor=#0b5cff" \
-F "backgroundColor=#ffffff"
Any field you leave out gets a default: "My App" for the two names and white for the two colors. Set them; the names show up in the manifest. The base path is normalized, so /icons and /icons/ both produce paths like /icons/favicon-32.png.
The same image rules apply as in the browser tool. The format is read from the file's contents, not its extension. A non-square source is scaled to fit and padded with transparency, never cropped. An animated GIF or WebP is refused with a message that names the frame count. An SVG needs a viewBox or a width and height.
The response, field by field
The response is JSON. These are the fields it carries:
downloadUrl: a signed link to the ZIP. It needs no API key.downloadName: the file name to save it under,favicons.zip.expiresInSeconds: how long the signed link stays valid. Currently 900, which is 15 minutes.deletedAfterHours: when the package is deleted from storage. Currently 24 hours for API packages.source: what we read from your file:format,width,height, and two booleans,squareandvector.files: every file in the package, each withname,bytes,width,heightand, for the ICO,layers.zipBytes: the size of the ZIP.headSnippet: the paste-ready HTML tags, as a string.warnings: plain sentences about anything we changed that you might not expect, such as padding a non-square source or enlarging a small one.meteredandusage: whether the call counted against your plan, and your standing for the period.
For a square 1024 by 1024 PNG logo, the files list from a package we generated looks like this:
[
{ "name": "favicon.ico", "bytes": 15086, "width": null, "height": null, "layers": 3 },
{ "name": "favicon-16.png", "bytes": 346, "width": 16, "height": 16, "layers": null },
{ "name": "favicon-32.png", "bytes": 606, "width": 32, "height": 32, "layers": null },
{ "name": "apple-touch-icon.png", "bytes": 2733, "width": 180, "height": 180, "layers": null },
{ "name": "icon-192.png", "bytes": 3095, "width": 192, "height": 192, "layers": null },
{ "name": "icon-512.png", "bytes": 8904, "width": 512, "height": 512, "layers": null },
{ "name": "icon-maskable-512.png", "bytes": 7010, "width": 512, "height": 512, "layers": null },
{ "name": "site.webmanifest", "bytes": 475, "width": null, "height": null, "layers": null },
{ "name": "head-snippet.html", "bytes": 346, "width": null, "height": null, "layers": null }
]
An SVG source adds favicon.svg, the original vector unchanged, and the snippet gains a line for it.
The widths and heights are read back from the encoded files after they are made, not echoed from what was requested. That makes the response a measurement you can assert on: a test that checks icon-512.png reports 512 by 512 is checking the real file. The ICO reports its layer count the same way, by reading its directory. The favicon size guide explains what each file is for.
Downloading the package
To download the package, send a plain GET to the downloadUrl from the response. The signed token in the link is the credential, so the request needs no API key, which means you can hand the link to a browser, a build step or a customer without exposing your key.
curl -L -o favicons.zip "$DOWNLOAD_URL"
The link is bound to one package and expires after 15 minutes, and the package itself is deleted 24 hours after it is made, under our retention policy. An expired link, or one whose package has already been deleted, answers 410. A token that does not verify answers 403. Download promptly, or call the API again.
Files are never public: the only way to fetch a package is the signed link, and we never use uploaded files for training. The full policy is on the privacy page.
Errors and what to do about them
Every error is JSON with an error field holding a sentence you can show a person as it is. The status codes:
| Status | Meaning | What to do |
|---|---|---|
400 |
Not multipart, or no file field |
Send multipart/form-data with a file part |
401 |
No key, unknown key or revoked key | Check the header and the key |
403 |
The account is suspended | Contact support |
413 |
The file is over your plan's per-file size limit | Send a smaller file |
422 |
The image could not be read as a supported image | Show the message; it says why |
429 |
You have used your plan's allowance for the period | Wait for the next period, or move up a plan |
A 422 is specific on purpose. A corrupt file, an animated image, an unsupported format and an SVG without dimensions each get their own sentence, so the person who uploaded the file knows what to fix. A GET to /api/generate answers 405 with a sentence explaining that the endpoint takes a POST.
How metering works
Metering on the favicon API is simple: one image is one package is one unit. The plan check runs before any pixels are read, so a request over your allowance is refused up front and does no work. When a call fails because the image cannot be read, the unit it reserved is handed back, so a failed call is not billed. Calling again with the same key and the exact same image bytes in the same period counts as the same image, so a retry after a dropped connection is not billed twice.
Your plan's allowance and per-file limit are shown on the pricing page and on your dashboard, which read them from the same configuration the server enforces.
Pattern 1: regenerate icons in a build
The first pattern regenerates the icon set whenever the logo changes, as part of your build. A minimal Node script, run before the site build:
import { readFile, writeFile } from "node:fs/promises";
const body = new FormData();
body.set("file", new Blob([await readFile("brand/logo.svg")]), "logo.svg");
body.set("name", "Acme");
body.set("shortName", "Acme");
body.set("themeColor", "#0b5cff");
const res = await fetch("https://faviconize.com/api/generate", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.FAVICONIZE_API_KEY}` },
body,
});
const pkg = await res.json();
if (!res.ok) throw new Error(pkg.error);
for (const w of pkg.warnings) console.warn(w);
const zip = await fetch(pkg.downloadUrl);
await writeFile("public/favicons.zip", Buffer.from(await zip.arrayBuffer()));
await writeFile("src/head-icons.html", pkg.headSnippet);
Unzip into your public folder and include head-icons.html in your layout. Printing the warnings means a logo that is too small or not square shows up in the build log instead of on the live site. Run the step only when the logo file changes and you make one call per logo change, not one per deploy.
Pattern 2: icons for your customers' logos
The second pattern gives every customer of your product an icon set made from the logo they upload. Your server receives the upload, forwards it to the favicon API with the customer's site name, and stores the files it gets back on your own storage. The API response's warnings array is written for people, so you can show it to the customer next to their upload: "The source is 800x400, which is not square. It was scaled to fit and padded with transparency so nothing was cropped."
Two details matter here. First, download the ZIP promptly, because the link is short-lived and the package is deleted after 24 hours; your copy is the durable one. Second, keep the API key on your server. The signed downloadUrl is safe to pass around; the key is not.
Pattern 3: check icons in a test
The third pattern uses the API's measured response in a test. Because every file reports its real dimensions, a test can assert the contract rather than trust it: the ICO has three layers, apple-touch-icon.png is 180 by 180, both manifest icons are the sizes the manifest says. If the response for your logo ever comes back with a warning, the test can fail and tell you why. That turns "the favicon looks fine on my screen" into a check that runs on every change.
What the API does not do
The favicon API does one job, and it is honest about the edges of it:
- It takes one image per call. Send several calls for several logos.
- It does not draw, edit or crop. There is no editor and no AI generation; it works from the image you give it.
- It does not turn a raster image into a vector.
favicon.svgappears only when you upload an SVG. - It does not accept HEIC or PDF sources.
If you are still deciding what your icon should look like, the free favicon packs are a quick way to see complete sets at every size.
The short answer
The favicon API turns one uploaded image into a complete icon package from code: POST /api/generate with a bearer key and a file, read the JSON for measured file details, warnings and the head snippet, then GET the signed downloadUrl within 15 minutes. It suits build pipelines, agencies and products that make icons for their own customers. The API is on the paid plans on the pricing page. To install what it returns, follow our guide on how to add a favicon to HTML, and for what is inside the ICO, see PNG to ICO.