# unifont.dev HTTP API

> Every page on unifont.dev is also an HTTP endpoint: search, metadata, CSS, provider comparison and unicode coverage.

Every page on unifont.dev is a thin layer over these endpoints. They are public, they need no authentication, they are cached, and they will not change while they are under `/v1`.

Machine-readable: [`/openapi.json`](/openapi.json). Not for production use: this API is best-effort and may be rate-limited or withdrawn. If a deployment of yours depends on it, run `unifont` yourself.

## GET /api/v1/providers

`listProviders`: Every provider unifont supports, with the number of families each one lists and whether it answered while the current index was built.

- `200` Every provider, in cascade order.

## GET /api/v1/status

`getStatus`: How the merged index is doing right now: family counts, which providers answered, and how old the index is.

- `200` Current index state.

## GET /api/v1/contributors

`listContributors`: Everyone who has landed a commit in `unjs/unifont`, ranked by commit count.

- `200` Contributors, most commits first.

## GET /api/v1/fonts

`searchFonts`: Search the merged catalogue of families. Ranked exact, then prefix, then word, then substring. Never fuzzy, so a typo returns nothing rather than the wrong family.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `q` | query | string | no | What to search for. Omit to list everything. |
| `provider` | query | string | no | Limit results to one provider. `npm` and `adobe` cannot list their families and are rejected. |
| `limit` | query | integer | no | How many families to return. |
| `offset` | query | integer | no | How many matches to skip. |

- `200` Matching families.
- `400` Unknown provider, or a provider that cannot list its families.

## GET /api/v1/fonts/{family}

`getFont`: Everything a provider knows about a family, and the faces a selection resolves to, including `@font-face` CSS for it.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `family` | path | string | yes | Font family name, percent-encoded. Matching ignores case. |
| `provider` | query | string | no | Limit the provider cascade to these providers (comma-separated). |
| `weights` | query | string | no | Weights to resolve (comma-separated). A range such as `200 800` resolves a variable axis. |
| `styles` | query | string | no | Styles to resolve (comma-separated): `normal`, `italic` or `oblique`. |
| `subsets` | query | string | no | Unicode subsets to resolve (comma-separated), e.g. `latin`, `latin-ext`, `cyrillic`. |
| `formats` | query | string | no | Font formats to resolve (comma-separated). Defaults to `woff2`. |

- `200` The resolved family.
- `400` No family was given.
- `404` No provider publishes the family. It may still exist on Adobe Fonts or npm, neither of which can be listed.

## GET /api/v1/fonts/{family}/css

`getFontCss`: `@font-face` CSS for one family, ready to link to or paste. A selection that resolves to nothing returns an empty stylesheet with a comment, not an error.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `family` | path | string | yes | Font family name, percent-encoded. Matching ignores case. |
| `provider` | query | string | no | Limit the provider cascade to these providers (comma-separated). |
| `weights` | query | string | no | Weights to resolve (comma-separated). A range such as `200 800` resolves a variable axis. |
| `styles` | query | string | no | Styles to resolve (comma-separated): `normal`, `italic` or `oblique`. |
| `subsets` | query | string | no | Unicode subsets to resolve (comma-separated), e.g. `latin`, `latin-ext`, `cyrillic`. |
| `as` | query | string | no | Rename the family in the output, so several providers can be loaded side by side. |

- `200` The stylesheet.
- `400` No family was given.

## GET /api/v1/css

`getCatalogueCss`: One stylesheet for up to 40 families, so a grid of specimens costs a single request. A family that cannot be resolved becomes a comment rather than breaking the sheet.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `families` | query | string | yes | Families to resolve (comma-separated, up to 40). |
| `weights` | query | string | no | Weights to resolve (comma-separated). Defaults to the variable range, or the weight nearest 400. |
| `subsets` | query | string | no | Subsets to resolve (comma-separated). Defaults to `latin` where the family publishes it. |

- `200` The stylesheet.
- `400` `families` was missing or empty.

## GET /api/v1/fonts/{family}/compare

`compareFontProviders`: Ask every provider that needs no credentials for the same family, and see how the answers differ: weights, styles, subsets, file counts and transfer size.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `family` | path | string | yes | Font family name, percent-encoded. Matching ignores case. |

- `200` One row per provider.
- `400` No family was given.

## GET /api/v1/fonts/{family}/coverage

`getFontCoverage`: Which characters the resolved faces can actually draw, based on their `unicode-range`. Checks a set of built-in samples, or your own string.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `family` | path | string | yes | Font family name, percent-encoded. Matching ignores case. |
| `provider` | query | string | no | Limit the provider cascade to these providers (comma-separated). |
| `text` | query | string | no | Check this text instead of the built-in samples. |

- `200` Coverage per sample.
- `400` No family was given.
- `404` No provider publishes the family.

## GET /api/v1/fonts/{family}/transfer

`getFontTransferSize`: How much a selection weighs, measured with `HEAD` requests against the provider CDN. No font file is downloaded.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `family` | path | string | yes | Font family name, percent-encoded. Matching ignores case. |
| `provider` | query | string | no | Limit the provider cascade to these providers (comma-separated). |
| `weights` | query | string | no | Weights to resolve (comma-separated). A range such as `200 800` resolves a variable axis. |
| `styles` | query | string | no | Styles to resolve (comma-separated): `normal`, `italic` or `oblique`. |
| `subsets` | query | string | no | Unicode subsets to resolve (comma-separated), e.g. `latin`, `latin-ext`, `cyrillic`. |

- `200` Measured transfer size.
- `400` No family was given.
- `404` No provider publishes the family.

## POST /mcp

`callMcp`: MCP server over Streamable HTTP. Tools: `search_fonts`, `get_font`, `get_font_css`, `compare_providers`, `check_coverage`, `list_providers`. Add it with `claude mcp add --transport http unifont https://unifont.dev/mcp`.

- `200` A JSON-RPC 2.0 response. A notification is answered with `202` and no body.
- `202` A notification was accepted.
- `400` The body was not a JSON-RPC 2.0 request.

## Conventions

- **Family names** are percent-encoded: `/api/v1/fonts/Big%20Shoulders%20Display`. Matching ignores case.
- **Unknown families** return `404` with a message naming the family.
- **Partial answers** name the providers that failed in `unavailable`, rather than quietly returning a short list.
- **Caching**: metadata for an hour, CSS for a day, both with `stale-while-revalidate`. Font files come from the provider CDN; this API never proxies them.

