The site is an API
Every page here is a thin layer over these endpoints. They're public, they need no auth, they're cached, and they won't change while they're under /v1. Use them in scripts, editor plugins or CI checks, anywhere you want font metadata without shipping a resolver. There's an MCP server too, if the thing asking is a model.
Machine-readable: openapi.json describes every endpoint below, llms.txt indexes the site for agents, and every prose page here answers in markdown if you ask for text/markdown or append .md to its path.
Not for production use. This API is best-effort, we'll rate-limit it if we have to, and it can change or go away, the same as proxy.unifont.dev. Build it into a script, an editor plugin or a CI check by all means; if a deployment of yours depends on it, run unifont yourself instead, or self-host the proxy.
GET /api/v1/providers
Every provider, with the number of families it will list.
curl https://unifont.dev/api/v1/providersreturns{ providers: [{ name, label, origin, families, requiresOptions, unavailable, note }] }
POST /mcp
MCP server over Streamable HTTP. Point an agent at it and it can search families, read provider metadata, compare CDNs, check unicode coverage, and get CSS it can paste.
claude mcp add --transport http unifont https://unifont.dev/mcpreturnsJSON-RPC 2.0. Tools: search_fonts · get_font · get_font_css · compare_providers · check_coverage · list_providers
GET /api/v1/status
How the merged index is doing right now: family counts, which providers answered, and how old the index is.
curl https://unifont.dev/api/v1/statusreturns{ families, providers, unavailable, indexAge, crossListed }
GET /api/v1/fonts
Search the merged catalogue. Ranked exact → prefix → word → substring. Never fuzzy.
| Parameter | Type | Notes |
|---|---|---|
q | string | What to search for. Leave it out to list everything. |
provider | string | Limit to one provider. |
limit | number | Default 60, capped at 200. |
offset | number | Default 0. |
curl "https://unifont.dev/api/v1/fonts?q=grotesk&limit=5"returns{ total, families: [{ family, providers }], unavailable }
GET /api/v1/fonts/{family}
Everything one provider knows about a family, and the faces it resolves to.
| Parameter | Type | Notes |
|---|---|---|
provider | string | Limit the cascade to these providers (comma-separated). |
weights | string | Comma-separated. Defaults to every weight published. |
styles | string | Comma-separated: normal, italic, oblique. |
subsets | string | Comma-separated. Defaults to everything published. |
curl "https://unifont.dev/api/v1/fonts/Newsreader?weights=400,600&subsets=latin"returns{ family, provider, providers, properties, requested, notes, fonts, fallbacks, css }
GET /api/v1/fonts/{family}/transfer
How much a selection weighs, measured with HEAD requests. Nothing is downloaded.
| Parameter | Type | Notes |
|---|---|---|
weights | string | Comma-separated. Default 400. |
styles | string | Comma-separated. Default normal. |
subsets | string | Comma-separated. Default latin. |
provider | string | Limit the cascade to these providers (comma-separated). |
curl "https://unifont.dev/api/v1/fonts/Fraunces/transfer?subsets=latin,latin-ext"returns{ family, faces, files, measured, bytes }. `measured` counts the files that reported a content-length
GET /api/v1/fonts/{family}/css
@font-face CSS you can serve. Link to it directly, or read it and copy. If a selection resolves to nothing you get an empty stylesheet with a comment, not a 404.
| Parameter | Type | Notes |
|---|---|---|
provider | string | Limit the cascade to these providers (comma-separated). |
weights | string | Comma-separated. Default 400. |
styles | string | Comma-separated. Default normal. |
subsets | string | Comma-separated. Default latin. |
as | string | Rename the family, so you can load several providers at once. |
curl "https://unifont.dev/api/v1/fonts/Switzer/css?weights=300,500"returnstext/css
GET /api/v1/css
One stylesheet for lots of families, so a grid of specimens costs one request.
| Parameter | Type | Notes |
|---|---|---|
families | string | Comma-separated, up to 40. Required. |
weights | string | Comma-separated. Defaults to the variable range, or the weight nearest 400. |
subsets | string | Comma-separated. Defaults to latin where the family publishes it. |
curl "https://unifont.dev/api/v1/css?families=Anton,Erode,Spectral"returnstext/css. A family that can't be resolved becomes a comment, rather than breaking the sheet
GET /api/v1/fonts/{family}/compare
The same family, asked of every provider that doesn't need credentials.
curl https://unifont.dev/api/v1/fonts/Inter/comparereturns{ family, results: [{ provider, available, weights, styles, subsets, faces, files, host, fallbacks }] }
GET /api/v1/fonts/{family}/coverage
Which characters the resolved faces can actually draw, based on their unicode-range.
| Parameter | Type | Notes |
|---|---|---|
text | string | Check your own text instead of the built-in samples. |
provider | string | Limit the cascade to these providers (comma-separated). |
curl "https://unifont.dev/api/v1/fonts/Anton/coverage?text=Zażółć"returns{ family, provider, checks: { [name]: { text, unrestricted, covered, missing, subsets } } }
Conventions
- Family names
- Percent-encode them:
/api/v1/fonts/Big%20Shoulders%20Display. Matching ignores case. - Unknown families
404with a message naming the family. A family may still exist on Adobe Fonts or npm, neither of which can be listed from here.- Partial answers
- If a provider fails, the response says so in
unavailable, rather than quietly handing you a short list. - Caching
- Metadata is cached for an hour and CSS for a day, both with
stale-while-revalidate. Font files come from the provider's own CDN. This API never proxies them.