API
API publique en lecture seule : récupérez le CSS de repli et les métriques calibrées de chaque police de la base, en JSON ou en CSS.
Vue d'ensemble
L'API expose, en lecture seule, chaque police de la base avec ses métriques calibrées et le CSS de repli correspondant. Elle est conçue pour être appelée directement depuis un site tiers, un script de build ou un plugin de CMS.
- URL de base :
https://nmfs.agencewebperformance.fr/api/v1 - Aucune authentification, aucune clé, aucun quota déclaré. Les réponses sont mises en cache en amont (voir Cache), ce qui absorbe les appels répétés.
- Destinée aux projets en développement, pas à la production. Récupérez le CSS au moment du build ou côté serveur, et intégrez-le dans votre propre feuille de styles. Ne référencez jamais une URL de l'API depuis un site en ligne : ce n'est pas un CDN, et rien ne garantit sa disponibilité ni sa latence pour vos visiteurs.
- Pas de CORS. Le navigateur d'un visiteur d'un site tiers ne peut pas lire ces réponses. Les usages prévus — script de build, plugin de CMS côté serveur,
curl— n'en ont pas besoin. - Seules les polices publiées et publiques sont exposées. Les polices privées et les soumissions en attente renvoient un 404 indiscernable d'un identifiant inexistant — impossible d'en déduire l'existence.
- Aucune donnée interne ne sort : ni chemin de fichier, ni adresse e-mail, ni le fichier de police lui-même.
Points d'entrée
| Méthode et chemin | Renvoie | Type |
|---|---|---|
GET /api/v1/fonts | Liste paginée des polices | application/json |
GET /api/v1/fonts/{id} | Métadonnées, métriques et CSS d'une police | application/json |
GET /api/v1/fonts/{id}/css | Le CSS seul, à copier dans votre projet | text/plain |
Seuls GET et HEAD sont acceptés. Une autre méthode sur un chemin connu renvoie 405 ; un chemin inconnu sous /api renvoie 404, toujours en JSON.
Lister les polices
GET https://nmfs.agencewebperformance.fr/api/v1/fonts?stack=serif&per_page=25
| Paramètre | Valeurs | Effet |
|---|---|---|
q | texte libre | Filtre les polices dont le nom contient ce texte. |
stack | sans ou serif | Ne garde que ce stack de repli. Toute autre valeur est ignorée. |
slug | slug exact, ex. poppins | Correspondance exacte. Un slug n'étant pas garanti unique, la réponse reste une liste (0 à n éléments) et n'est pas paginée. |
page | entier ≥ 1 | Page demandée. Défaut 1. |
per_page | 1 à 100 | Taille de page. Défaut 50, plafonnée à 100. |
Les polices sont triées par nom, sans tenir compte de la casse. Chaque élément porte les liens vers sa fiche JSON, son CSS et sa page dans l'outil :
{
"data": [
{
"id": 1,
"name": "Poppins",
"slug": "poppins",
"stack": "sans",
"updated_at": "2026-05-12T15:33:02+00:00",
"links": {
"self": "https://nmfs.agencewebperformance.fr/api/v1/fonts/1",
"css": "https://nmfs.agencewebperformance.fr/api/v1/fonts/1/css",
"html": "https://nmfs.agencewebperformance.fr/font/1"
}
}
],
"meta": { "page": 1, "per_page": 50, "total": 81, "total_pages": 2 }
}
Une police en détail
GET https://nmfs.agencewebperformance.fr/api/v1/fonts/1
La fiche regroupe tout ce qu'il faut pour intégrer la police sans repasser par l'outil. Les métriques sont données par cible — la police système que chaque repli vise réellement — plutôt que par numéro d'emplacement :
{
"id": 1,
"name": "Poppins",
"slug": "poppins",
"stack": "sans",
"contributor": "Eroan",
"created_at": "2026-04-30T09:17:41+00:00",
"updated_at": "2026-05-12T15:33:02+00:00",
"fallbacks": {
"android": {
"font": "Roboto",
"size_adjust": 112.54,
"ascent_override": 97.24,
"descent_override": 35.78,
"line_gap_override": null
},
"desktop": {
"font": "Arial",
"size_adjust": 113,
"ascent_override": 96,
"descent_override": 34.2,
"line_gap_override": null
}
},
"font_family": "'Poppins', 'Poppins-fallback', sans-serif",
"css": "/* Android fallback (Roboto) — declared FIRST */\n@font-face { … }\n\nbody { font-family: … }",
"links": { "self": "…", "css": "…", "html": "…" }
}
| Champ | Contenu |
|---|---|
stack | sans (Arial + Roboto) ou serif (Times New Roman + Noto Serif). |
fallbacks.desktop | Repli pour Windows, macOS et iOS. C'est la face déclarée en dernier dans le CSS, donc prioritaire quand les deux polices système sont présentes. |
fallbacks.android | Repli pour Android, où la police desktop n'existe pas. Déclarée en premier. |
*.size_adjust | Pourcentage, toujours renseigné (100 = inchangé). |
*.ascent_override, descent_override, line_gap_override | Pourcentage, ou null quand le descripteur vaut normal et n'est pas émis dans le CSS. |
font_family | La pile font-family seule, pour l'appliquer au sélecteur de votre choix. |
css | L'exemple d'intégration complet, identique à celui de la page de la police : les deux @font-face (une famille de repli, deux faces) et la déclaration sur body. |
contributor | Nom d'affichage du dernier contributeur, ou null. |
created_at, updated_at | ISO 8601, avec décalage horaire du serveur. |
Le CSS seul
GET https://nmfs.agencewebperformance.fr/api/v1/fonts/1/css
Renvoie les règles @font-face précédées d'un en-tête de provenance, pour les récupérer sans analyser de JSON — typiquement depuis un script de build :
curl -s https://nmfs.agencewebperformance.fr/api/v1/fonts/1/css >> src/styles/fonts.css
Ce point d'entrée n'est pas une feuille de style, et ne peut pas en être une. Il est servi en text/plain avec X-Content-Type-Options: nosniff : aucun navigateur moderne ne l'applique via <link rel="stylesheet">. Mieux, quand le navigateur annonce lui-même une destination style (Sec-Fetch-Dest: style), la requête est refusée en 403 avec un message explicite. L'API existe pour alimenter vos projets, pas pour que vos visiteurs en dépendent à chaque chargement de page.
Le CSS est identique à celui affiché sur la page de la police : les deux @font-face, puis une déclaration body { font-family: … }. Cette dernière est un exemple d'intégration — adaptez le sélecteur à votre projet (html, .site, un composant…) ; la valeur seule est aussi fournie dans le champ font_family de la fiche JSON :
body { font-family: 'Poppins', 'Poppins-fallback', sans-serif; }
Le CSS reproduit la pile de la police telle qu'elle a été calibrée dans l'outil. Si vous réorganisez ces règles, conservez l'ordre : la face desktop doit rester déclarée en dernier.
Cache et requêtes conditionnelles
Toutes les réponses portent Cache-Control: public, max-age=3600 et un ETag qui change dès que la fiche est ré-enregistrée. Tout cache intermédiaire ou client HTTP qui respecte ces en-têtes peut donc les conserver une heure sans nous solliciter.
Pour revalider sans re-télécharger, renvoyez l'ETag reçu : la réponse est un 304 Not Modified sans corps si rien n'a changé.
GET /api/v1/fonts/1/css If-None-Match: "c8cf8315612e57dc9057159925c729d7" HTTP/1.1 304 Not Modified
Aucun cookie n'est posé par l'API — c'est ce qui rend le cache partagé possible.
Erreurs
Les erreurs sont toujours en JSON, y compris sur le point d'entrée CSS :
HTTP/1.1 404 Not Found
{ "error": "not_found" }
| Statut | Cas |
|---|---|
404 | Identifiant inexistant, police privée, soumission en attente de validation, ou chemin inconnu. Les trois premiers cas sont volontairement indiscernables. |
403 | Tentative de chargement du point d'entrée CSS comme feuille de style (Sec-Fetch-Dest: style) — { "error": "stylesheet_hotlink_forbidden" }. |
405 | Méthode autre que GET / HEAD sur un chemin connu. |
500 | Erreur interne — { "error": "internal_error" }. Le détail n'est jamais exposé. |
Un paramètre de filtre invalide n'est pas une erreur : il est ignoré (stack) ou ramené dans les bornes (per_page).
Stabilité
Le préfixe /v1 est un engagement : les champs documentés ici ne seront ni renommés ni supprimés dans cette version. Des champs pourront être ajoutés — écrivez vos clients pour qu'ils ignorent ce qu'ils ne connaissent pas. Un changement incompatible passera par un /v2, en laissant /v1 en service.
Les identifiants numériques sont stables dans le temps. Les slugs sont dérivés du nom de la police et peuvent changer si celle-ci est renommée ; préférez l'identifiant pour référencer une police durablement.
Skill pour Claude et IA génératives
Ce bloc est un mode d'emploi condensé de l'API, rédigé pour un assistant. Copiez-le dans les instructions d'un projet Claude, un fichier SKILL.md, un CLAUDE.md ou le prompt système de l'outil de votre choix : l'assistant saura alors chercher une police dans la base, récupérer son CSS et l'intégrer correctement — sans inventer de métriques.
# No More Font Shift — skill API
## Ce que c'est
No More Font Shift maintient une base de polices web avec, pour chacune, un CSS de repli calibré : deux @font-face (size-adjust, ascent-override, descent-override, line-gap-override) qui éliminent le décalage de mise en page (CLS) pendant le chargement de la police. L'API est publique, en lecture seule, sans clé, en JSON ou en CSS.
Base : https://nmfs.agencewebperformance.fr/api/v1
## Quand l'utiliser
- L'utilisateur veut supprimer le CLS causé par une police web.
- Il cite une police (Poppins, Inter, Lora…) : cherche-la ICI avant de calculer des métriques toi-même.
## Commandes
1. Trouver une police GET /fonts?q=poppins (ou ?slug=poppins pour une correspondance exacte)
2. Lire sa fiche GET /fonts/{id} → fallbacks.desktop, fallbacks.android, font_family, css
3. Récupérer le CSS GET /fonts/{id}/css (text/css, @font-face uniquement)
## Comment livrer le résultat
- Insère le contenu de `css` dans la feuille de styles DU PROJET. Ne référence JAMAIS une URL de l'API depuis un site en production : ce n'est pas un CDN, le CSS est servi en text/plain et un chargement en feuille de style est refusé (403).
- Le CSS se termine par un exemple `body { font-family: … }` : adapte ce sélecteur au projet (`font_family` donne la pile seule).
- Ne réordonne JAMAIS les deux @font-face : la face desktop doit rester déclarée en dernier (la dernière l'emporte).
- Si la recherche renvoie une liste vide, dis-le à l'utilisateur et propose l'outil (https://nmfs.agencewebperformance.fr) : n'invente pas de valeurs.
## Règles
- Uniquement GET. Réponses cacheables (ETag, max-age 3600). Aucune authentification.
- Pas de CORS : appelle l'API au build ou côté serveur, jamais depuis le navigateur d'un visiteur.
- 404 = identifiant inexistant OU police privée : n'en déduis pas l'existence.
- Champs stables en v1 ; ignore ceux que tu ne connais pas.
Documentation complète : https://nmfs.agencewebperformance.fr/docs/api
Le skill renvoie vers cette page pour tout ce qu'il ne couvre pas. Il évolue avec l'API : recopiez-le après une mise à jour majeure.