Documentation

La mise en place, pas à pas

Chaque extrait de cette page est prêt à coller. Les chiffres — quotas, limites, valeurs par défaut, noms de champs — sont relevés dans le code qui tourne : cette page et le serveur disent donc la même chose.

Pour commencer

Obtenez votre clé

Tout ici sauf les flux RSS utilise une clé API. Ouvrez votre compte, section API développeur, et copiez-la. Le plan gratuit n'a pas besoin de carte.

La clé porte votre plan, et votre plan porte vos volumes quotidiens. Deux compteurs fonctionnent côte à côte : un pour l'API REST, un pour le serveur MCP. Un éditeur posant quelques questions ne consomme jamais votre budget API.

Régénérer la clé depuis votre compte révoque immédiatement l'ancienne. Tout ce qui l'utilise encore cesse de fonctionner — mettez d'abord à jour vos intégrations.

De quoi ai-je besoin ?

Vous voulez…Utiliser
Poser des questions depuis votre éditeurServeur MCP
Interroger les données depuis votre propre codeAPI REST
Être informé lorsque quelque chose changeWebhooks
L'intégrer dans n8n, Zapier ou MakeAutomatisations
Vérifier les dépendances sur chaque demande de tirageGitHub Action
Suivre dans un lecteur, pas de cléFlux RSS

Serveur MCP

Le serveur MCP, en bref

MCP (Model Context Protocol) est une manière standard pour un assistant IA d'appeler un service externe. Vous collez une adresse dans votre client, et l'assistant obtient cinq outils qu'il peut utiliser pendant que vous travaillez. Ces outils interrogent le catalogue olud.ai : projets IA open-source, modèles IA avec leurs prix, et alternatives open-source aux produits commerciaux.

FaitValeur
Point de terminaisonhttps://olud.ai/mcp.php
MéthodePOST, JSON-RPC 2.0. GET renvoie 405 (voir ci-dessous)
TransportHTTP diffusé, un point de terminaison, pas de flux SSE
Version du protocole2025-06-18. Si le client demande 2025-03-26 ou 2024-11-05, le serveur répond dans cette version
Version du serveur1.0.0
SessionAucune. Pas de Mcp-Session-Id à conserver, rien à expirer, rien à reconnecter
Capacitésoutils uniquement. resources/list, resources/templates/list et prompts/list répondent avec des listes vides au lieu d'une erreur
AuthEn-tête de requête X-Api-Key. Optionnel
BatchingNon pris en charge. Un tableau JSON-RPC est rejeté avec HTTP 400

Le serveur lit. La seule chose qu'il écrit est votre compteur d'appels quotidien.

Chaque réponse d'outil contient l'URL de la page correspondante sur olud.ai, plus une ligne d'attribution : les scores et indices sont calculés par olud.ai, les faits contextuels proviennent de GitHub, Hugging Face, OpenRouter, PyPI, NPM et Docker Hub.

L'installer, client par client

Même URL pour chaque client : https://olud.ai/mcp.php. La clé voyage dans l'en-tête X-Api-Key. Sans clé, le serveur répond toujours, avec une allocation plus petite (25 appels par jour par IP, 5 résultats par appel). Votre clé et un bloc prêt à coller se trouvent dans Votre compte, section serveur MCP.

Claude Code, une commande :

claude mcp add --transport http opensourceai https://olud.ai/mcp.php \
  --header "X-Api-Key: votre-clé"

# sans clé :
claude mcp add --transport http opensourceai https://olud.ai/mcp.php

# vérifiez ce qui a été enregistré :
claude mcp list

Claude Desktop, claude_desktop_config.json :

{
  "mcpServers": {
    "opensourceai": {
      "type": "http",
      "url": "https://olud.ai/mcp.php",
      "headers": { "X-Api-Key": "your-key" }
    }
  }
}

Ce fichier se trouve à ~/Library/Application Support/Claude/claude_desktop_config.json sur macOS, et %APPDATA%\Claude\claude_desktop_config.json sur Windows. Quittez et rouvrez Claude Desktop après l'avoir modifié ; il lit le fichier au démarrage.

Si Claude Desktop indique que le serveur a échoué ou ne le liste pas du tout, votre build n'accepte que les serveurs locaux (stdio). Connectez-le avec mcp-remote, qui nécessite Node installé :

{
  "mcpServers": {
    "opensourceai": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://olud.ai/mcp.php",
               "--header", "X-Api-Key:${OSAI_KEY}"],
      "env": { "OSAI_KEY": "votre-clé" }
    }
  }
}
Dans ce bloc de pont, écrivez X-Api-Key:${OSAI_KEY} sans espace après le deux-points et mettez la valeur dans env. mcp-remote divise un argument d'en-tête sur des espaces, et un en-tête écrit en ligne perd sa valeur.

Curseur, .cursor/mcp.json dans le projet (ou ~/.cursor/mcp.json pour chaque projet) :

{
  "mcpServers": {
    "opensourceai": {
      "url": "https://olud.ai/mcp.php",
      "headers": { "X-Api-Key": "votre-clé" }
    }
  }
}

VS Code, .vscode/mcp.json dans l'espace de travail :

{
  "servers": {
    "opensourceai": {
      "type": "http",
      "url": "https://olud.ai/mcp.php",
      "headers": { "X-Api-Key": "${input:osaiKey}" }
    }
  },
  "inputs": [
    { "id": "osaiKey", "type": "promptString",
      "description": "clé API olud.ai", "password": true }
  ]
}
VS Code nomme l'objet de niveau supérieur serveurs, pas mcpServers. Copier le bloc Claude ou Cursor tel quel est la raison habituelle pour laquelle le serveur n'apparaît jamais. L'prompt des entrées demande la clé lors de la première utilisation, donc le fichier reste sûr à valider.

Une clé dans l'URL sous la forme ?key=votre-clé fonctionne également, car le serveur la lit. Préférez l'en-tête : les chaînes de requête finissent dans l'historique du navigateur, les journaux de proxy et l'historique de la console.

Vérifier que ça marche, avec curl

Lorsque un client dit qu'un serveur est indisponible, testez d'abord le serveur lui-même. Cet appel liste les outils et ne coûte rien : seuls tools/call diminuent votre quota, donc initialize, tools/list et ping sont gratuits. Redémarrer votre éditeur ne consomme jamais la journée.

curl -s -X POST https://olud.ai/mcp.php \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: votre-clé" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Une réponse saine est HTTP 200 avec {"jsonrpc":"2.0","id":1,"result":{"tools":[ ... ]}} et les cinq noms à l'intérieur. Ensuite, dépensez un appel :

curl -s -X POST https://olud.ai/mcp.php \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: votre-clé" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"search_open_source_ai",
                 "arguments":{"query":"texte à parole","limit":5}}}'

Un tools/call réussi contient deux en-têtes de réponse qui valent la peine d'être lus : X-RateLimit-Limit est votre allocation quotidienne, X-RateLimit-Remaining est ce qu'il reste après cet appel. Ils sont définis uniquement sur tools/call.

Vérification de vivacité la plus courte possible. Elle répond {"jsonrpc":"2.0","id":0,"result":{}} et n'a pas besoin de clé :

curl -s -X POST https://olud.ai/mcp.php \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":0,"method":"ping"}'

Les cinq outils

Vous ne les appelez pas par leur nom. Vous posez une question, le client choisit l'outil. Les noms comptent lorsque vous lisez un journal ou écrivez l'appel vous-même, et ils sont correspondus exactement : search_open_source_ai fonctionne, Search_Open_Source_AI ne fonctionne pas.

OutilParamètresLimites et valeurs par défautRetours
search_open_source_aiquery (chaîne, requis), limit (entier)limite limitée à 1 … votre plafond de résultats par appel. Par défaut 10, ou votre plafond s'il est inférieurcorrespondances (total trouvé, non tronqué), puis id, nom, résumé, étoiles, langue, licence, github, page, score de santé et étiquette, tendance récente
get_projectproject (chaîne, requis)Accepte un slug (ollama-ollama), owner/repo, une URL complète de github.com, ou le nom exact du projetTout ce qui précède plus owner, forks, créé, poussé, sujets, le détail complet de la santé, étoiles gagnées, et jusqu'à 5 versions récentes
find_alternativesproduct (chaîne, requis)Nom du produit commercial. Correspond à son slug, puis à son nom exactproduit, page, et la liste des alternatives, elle-même limitée par votre plafond de résultats par appel
list_modelsfournisseur (chaîne), max_price_out (nombre), min_context (entier), free_only (booléen), limit (entier)le fournisseur est une sous-chaîne insensible à la casse. max_price_out est en dollars par million de tokens de sortie. min_context est en tokens. limit est limité à 1 … votre plafond, par défaut 15 ou votre plafond si inférieurcorrespondances, puis id, nom, fournisseur, contexte, price_per_million (entrée et sortie), modalité, appel_outil, et l'URL du leaderboard
projets_tendancetype (chaîne), limit (entier)le type est tendance, émergent ou top_health. Tout autre type revient à tendance sans erreur. Par défaut tendance. limit est limité à 1 … votre plafond, par défaut 10 ou votre plafond si inférieurtype, les cartes de projet, et le temps de génération de l'index

Questions qui atteignent chaque outil

  • search_open_source_ai — "Trouvez-moi une bibliothèque OCR open-source que je peux exécuter localement."
  • get_project — "Quelle est la santé d'ollama/ollama en ce moment, et quand a-t-il été expédié pour la dernière fois ?"
  • find_alternatives — "Quel outil open-source pourrait remplacer Notion pour nous ?"
  • list_models — "Quels modèles ont au moins 128k de contexte et coûtent moins de 1 $ par million de tokens de sortie ?"
  • trending_projects — "Quels projets d'IA open-source gagnent le plus d'étoiles cette semaine ?" Demandez plutôt émergent pour obtenir des projets sains qui sont encore peu connus, ou top_health pour les mieux entretenus.
La recherche correspond au texte, pas au sens. Elle attribue le score le plus élevé à un nom exact, puis à un nom qui commence par vos mots, puis à un nom qui les contient, puis à une description qui les contient, et ajoute des points pour une étiquette de sujet exacte. Une longue phrase ne trouve rien ; "synthèse vocale" ne trouve rien que "texte à la parole" ne trouve pas. Lorsque la recherche revient vide, raccourcissez la requête à un ou deux mots.

Les filtres éliminent ce qu'ils ne peuvent pas juger. Avec max_price_out défini, un modèle dont nous n'avons pas de chiffre pour le prix de sortie est exclu plutôt que deviné. Avec min_context défini, un modèle sans fenêtre de contexte enregistrée compte comme zéro et est exclu. free_only conserve les modèles dont le niveau est exactement gratuit.

Une limite au-dessus de votre plafond n'est pas une erreur : elle est réduite silencieusement. Le schéma publié indique un maximum de 100 car c'est le plus élevé que tout plan autorise, pas ce que votre clé permet. Une limite qui n'est pas un nombre revient à la valeur par défaut.

Pour quelques produits, nous avons une page de comparaison écrite à la main et pas de liste structurée dans l'index lisible par machine. find_alternatives répond alors avec succès avec un tableau d'alternatives vide, indique que la liste structurée n'est pas dans l'index pour celui-ci, et donne l'URL de la page. Un tableau vide là ne signifie pas qu'aucune alternative n'existe.

Volumes par plan

Pas de cléGratuitDevProOrg
Appels par jour252002,00020,000200,000
Résultats par appel5102550100

Deux plafonds, car ils arrêtent deux choses différentes : les appels par jour limitent la charge, les résultats par appel limitent combien du catalogue sort dans une réponse.

  • Les appels MCP sont comptés séparément des appels REST API. Même clé, deux compteurs. Un éditeur posant quelques questions ne touche jamais à votre budget API.
  • Seuls tools/call sont comptés. initialize, tools/list et ping ne coûtent rien.
  • Les deux compteurs se réinitialisent à 00:00 UTC.
  • Sans clé, le compte est par adresse IP. Tout le monde derrière une adresse IP de bureau partage les 25.
  • En mode gratuit et sans clé, chaque réponse se termine par une ligne de note indiquant vos deux plafonds. Les réponses Dev, Pro et Org ne portent pas cette ligne.
  • Org est le plan nommé business dans le système. Les messages d'erreur impriment le nom interne.

Quand ça ne marche pas

Un outil échouant renvoie HTTP 200 avec isError défini sur true et la raison en texte brut. C'est délibéré : le modèle lit la raison et peut essayer autre chose. Cela signifie également qu'un 200 dans votre journal proxy n'est pas une preuve que l'appel a fonctionné. Lisez le corps.

Clé mal saisie ou révoquée. Chaque question échoue avec :

Cette clé API est inconnue ou a été révoquée. Supprimez-la pour utiliser le niveau gratuit,
ou obtenez-en une nouvelle sur https://olud.ai/account.html

Une mauvaise clé ne revient pas silencieusement à l'allocation anonyme. Corrigez la clé ou supprimez complètement l'en-tête X-Api-Key, puis redémarrez le client pour qu'il relise la configuration.

Plafond quotidien atteint. Avec une clé, puis sans :

Quota quotidien MCP atteint (200 appels/jour sur le plan "gratuit"). Se réinitialise à 00:00 UTC.
Niveaux supérieurs : https://olud.ai/plans.html

Limite anonyme atteinte (25 appels/jour par IP). Se réinitialise à 00:00 UTC.
Une clé API gratuite augmente à 200/jour — https://olud.ai/account.html

Rien n'est cassé et rien n'est facturé. Attendez 00:00 UTC, ou passez à un niveau supérieur. Si vous n'avez pas de clé, une clé gratuite vous fait passer de 25 à 200 appels et de 5 à 10 résultats par appel.

Le nom de l'outil n'est pas l'un des cinq :

Outil inconnu "list_projects". Appelez tools/list pour voir ce qui est disponible.

Un argument requis est manquant. Chaque message montre la forme qu'il veut :

Manquant "query". Exemple : {"query": "texte à la parole"}
Manquant "project". Exemple : {"project": "ollama/ollama"}
Manquant "product". Exemple : {"product": "Midjourney"}

Rien trouvé. Les deux messages vous indiquent où aller ensuite :

Aucun projet trouvé pour "ollamaa". Essayez d'abord search_open_source_ai pour obtenir son id exact.

Nous ne suivons pas encore "photoshop". Correspondances les plus proches : <jusqu'à 8 noms>.
Liste complète : https://olud.ai/alternatives-hub.html

Le catalogue est en cours de reconstruction. Attendez une minute et demandez à nouveau ; voici les quatre formulations, une par ensemble de données :

Le catalogue est en cours de reconstruction. Essayez à nouveau dans une minute.
Cette liste est en cours de reconstruction. Essayez à nouveau dans une minute.
Le catalogue des modèles n'est pas disponible en ce moment.
L'index des alternatives n'est pas disponible en ce moment.

Ouvrir https://olud.ai/mcp.php dans un navigateur renvoie HTTP 405. C'est la bonne réponse : le point de terminaison parle JSON-RPC sur POST, et la spécification demande 405 lorsqu'un serveur n'offre pas de flux GET. Le corps vous dit quoi faire et contient la configuration à copier. Il arrive sur une ligne ; il est espacé ici pour être lu.

{
  "server": "olud.ai MCP",
  "version": "1.0.0",
  "protocol": "2025-06-18",
  "usage": "Ce point de terminaison parle MCP via JSON-RPC 2.0. POSTez-le depuis un client MCP.",
  "config": { "mcpServers": { "opensourceai": { "url": "https://olud.ai/mcp.php" } } },
  "tools": ["search_open_source_ai", "get_project", "find_alternatives",
            "list_models", "trending_projects"],
  "docs": "https://olud.ai/api.html"
}

Refus au niveau du transport. Ces quatre portent un code d'erreur JSON-RPC, et les trois premiers portent également un code d'erreur HTTP :

405  -32600  Utilisez POST avec un corps JSON-RPC 2.0.          (PUT, DELETE, HEAD…)
400  -32700  Erreur de parsing : le corps n'est pas un JSON valide.
400  -32600  Le regroupement JSON-RPC n'est pas supporté (supprimé dans MCP 2025-06-18).
200  -32601  Méthode inconnue "tools/execute".
200  -32602  Nom de l'outil manquant dans les paramètres.

Si votre client se plaint d'un identifiant de session manquant, ignorez-le et vérifiez le type de transport dans votre configuration. Ce serveur ne conserve aucune session, et il n'y a pas de Mcp-Session-Id à renvoyer. Un client configuré pour stdio ou pour SSE contre cette URL échouera avant le premier appel ; le type est http.

API REST

Authentification et clés

URL de base : https://olud.ai/api/v1. Chaque point de terminaison est un GET. L'en-tête CORS annonce POST, mais v1 n'a pas de route POST : les paramètres sont toujours lus à partir de la chaîne de requête.

Envoyez votre clé dans l'en-tête X-Api-Key. Un paramètre de requête ?key= fonctionne également, pour un onglet de navigateur ou un test rapide. Si les deux sont présents, l'en-tête l'emporte. Seul /meta fonctionne sans clé.

Les deux formes sont acceptées :

curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  "https://olud.ai/api/v1/projects?limit=3"

curl -s "https://olud.ai/api/v1/projects?limit=3&key=osk_YOUR_KEY"

Pour obtenir une clé : connectez-vous sur olud.ai et ouvrez votre page de compte, section API Développeur. La clé est créée lors de votre première visite, sur le plan gratuit, et ressemble à osk_ suivi de 40 caractères hexadécimaux. La rotation depuis la même page supprime immédiatement l'ancienne clé — l'ancienne valeur renvoie alors 403 invalid_key.

En-têteValeurEnvoyé le
X-RateLimit-LimitVotre plafond quotidien, sous forme d'entierChaque requête qui a passé la vérification de la clé
X-RateLimit-RemainingRequêtes restantes aujourd'hui, arrondies à 0Chaque requête qui a passé la vérification de la clé, y compris le 429
ETagMD5 cité du temps de construction du graphique des projetsChaque point de terminaison sauf /meta
Access-Control-Allow-Origin*Chaque réponse, y compris OPTIONS (qui répond 204)
CORS est ouvert et X-Api-Key est un en-tête autorisé, donc un navigateur peut appeler l'API directement. Quiconque lit le code source de votre page a alors votre clé et utilise votre quota. Appelez l'API depuis votre serveur et transmettez les résultats.
La vérification de la clé s'exécute avant le routage. Un chemin mal orthographié sans clé renvoie 401 missing_key, pas 404. Ajoutez la clé avant de chercher une faute de frappe dans le chemin.

Enveloppe de réponse

Un succès est toujours HTTP 200 avec trois clés de premier niveau : ok est vrai, data contient la charge utile, meta contient le contexte. data est un objet sur les points de terminaison à enregistrement unique et un tableau sur les points de terminaison de liste.

GET /api/v1/meta, tel quel :

{"ok":true,"data":{"name":"olud.ai API","version":"v1","generated":"2026-07-27T07:45:15+00:00","counts":{"projects":10142},"endpoints":["/project/{id}","/projects","/emerging","/alternatives/{id}","/search?q=","/models"],"docs":"https://olud.ai/api/"},"meta":{"attribution":"Scores & indices calculés par olud.ai (olud.ai). Faits contextuels provenant de sources publiques : GitHub, Hugging Face, OpenRouter, Artificial Analysis, PyPI, NPM, Docker Hub."}}

meta.attribution est défini sur chaque réponse de succès, sur chaque point de terminaison, et ne peut pas être désactivé. Le reste de meta varie selon le point de terminaison : généré, total, limite, décalage, tri, fraîcheur, points. Lisez les sections des points de terminaison pour savoir quels champs vous obtenez.

Une erreur porte ok false, un statut HTTP autre que 200, et error.code plus error.message. Il n'y a pas d'objet meta sur une erreur, donc pas de champ d'attribution. Testez ok avant de lire data — ne supposez pas la forme de succès.

GET /api/v1/projects sans clé, tel quel :

{"ok":false,"error":{"code":"missing_key","message":"Fournissez votre clé API via l'en-tête X-Api-Key (ou ?key=). Obtenez-en une sur https://olud.ai/api/"}}

Envoyez l'ETag en retour comme If-None-Match et vous obtiendrez 304 avec un corps vide lorsque le graphique n'a pas été reconstruit. Le graphique est reconstruit chaque matin, donc interroger plus souvent que cela renvoie 304 toute la journée.

Deux choses à savoir sur l'ETag. Il est calculé uniquement à partir du graphique des projets, donc la même valeur est renvoyée par /models, /hf et /history — envoyez un ETag en retour au point de terminaison d'où vous l'avez obtenu, sinon vous obtiendrez un 304 pendant que des données plus récentes se trouvent derrière. Et le quota est compté avant que l'ETag ne soit comparé, donc un 304 coûte toujours une requête.

Points de terminaison : méta et projets

GET /meta

État du graphique. Le seul point de terminaison sans clé, et le seul qui ne compte pas contre votre quota. Renvoie le nom et la version de l'API, généré (horodatage UTC de la dernière construction du graphique), counts.projects, une liste de points de terminaison et l'URL de la documentation. Aucun paramètre. Si les fichiers graphiques sont illisibles, counts est nul et l'appel renvoie toujours 200 — utilisez-le comme vérification de santé et pour décider si un nouveau tirage en vaut la peine. Il n'envoie pas d'ETag et pas d'en-têtes de limite de taux.

curl -s https://olud.ai/api/v1/meta

GET /project/{id}

Un projet, enregistrement complet. C'est la vue fusionnée : faits GitHub, notre score de maintenance, vélocité des étoiles, versions, pouls d'adoption.

  • Identité : id, nom, propriétaire, url, page (chemin de la page du projet sur le site), desc.
  • Faits GitHub à la dernière construction : étoiles, forks, lang, licence, sujets, créé, poussé.
  • santé : score sur 100, étiquette, pourquoi, plus les quatre composants dont il est composé — activité (max 30), élan (max 20), communauté (max 30), maintenance (max 20) — poids v2 depuis le 29 juillet 2026 — et vérifié. Les étiquettes suivent le score : 85+ Florissant, 70+ Sain, 50+ Maintenu, 30+ Ralentissement, en dessous de 30 À risque. Un projet sans commit pendant quatre semaines a son score plafonné à 45, donc les composants peuvent s'additionner à plus que le score. Tous les projets ne sont pas notés ; testez pour le champ.
  • vitesse : étoiles_1j et étoiles_7j, à partir de notre propre historique quotidien des étoiles.
  • versions : tag, date, niveau, url — le plus récent en premier.
  • outil : slug, cat, pulse, docker_pulls, npm_month, pip_month, hn_hits, bsky_week, compare_pages. Présent uniquement pour les projets correspondant à un outil suivi.
  • rang, tendance (stable, calme ou accélérée) et signaux (stars_accel, has_page).
ParamètreTypePar défautComportement
idsegment de chemin, requisaucunEn minuscules. Résolu en trois étapes : slug exact, puis l'index propriétaire/nom, puis propriétaire-nom. Aucun segment du tout renvoie 400 missing_id ; aucune correspondance renvoie 404 not_found avec un pointeur vers /search.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  https://olud.ai/api/v1/project/ollama-ollama
Utilisez le slug avec des tirets. Un slash littéral dans le chemin commence un nouveau segment de chemin, donc /v1/project/ollama/ollama recherche "ollama" et renvoie 404. Les ids renvoyés par /projects et /search sont toujours sûrs à renvoyer.

GET /projects

Filtrer, trier et paginer le catalogue. Les filtres s'appliquent en premier, puis le tri, puis le décalage et la limite.

ParamètreType / limitesPar défautComportement
langchaînepas de filtreCorrespondance exacte sur la langue du projet, insensible à la casse. lang=rust garde uniquement Rust.
licencechaînepas de filtreCorrespondance de sous-chaîne sur l'id de licence, insensible à la casse. license=gpl garde GPL-2.0 et AGPL-3.0.
verticalchaînepas de filtreCorrespondance exacte, insensible à la casse. Valeurs présentes dans le graphique : robotique, sécurité, finance, science, santé, éducation, juridique. La plupart des projets n'en ont aucun, et ils disparaissent tous lorsque vous définissez cela.
santé_minentierpas de filtreGarde les projets dont health.score est supérieur ou égal à la valeur. Les projets non notés comptent comme 0 et sont exclus. Une valeur non numérique devient 0, ce qui ne filtre rien.
trierétoiles, santé, élan ou récentétoilesToujours décroissant. l'élan lit velocity.stars_7d, récent lit la dernière date de poussée. Une valeur inconnue est triée par étoiles mais est renvoyée telle quelle dans meta.sort — vérifiez meta.sort si l'ordre vous surprend.
limiteentier 1-10025Les valeurs hors limites sont plafonnées aux bornes, les non numériques retombent à 25. Aucune erreur n'est levée, donc limit=500 vous donne silencieusement 100.
décalageentier 0-1000000Plafonné de la même manière.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  "https://olud.ai/api/v1/projects?lang=python&license=apache&sort=health&health_min=70&limit=10"

les données sont un tableau de cartes compactes : id, nom, url, desc, étoiles, lang, licence, santé (le score seul), élan (stars_7d), tendance, vertical. Les champs sans valeur sont présents et null. Pour les composants, les versions et les chiffres d'adoption, appelez /project/{id}. meta donne total (correspondances après filtrage, avant pagination), limite, décalage, tri et généré.

Points de terminaison : recherche, émergents, alternatives

GET /search

Recherche des projets par nom, description et sujets. Cela ne recherche pas les modèles ou les lignes Hugging Face — utilisez /models et /hf pour cela.

ParamètreType / limitesPar défautComportement
qchaîne, requiseaucunRamené et en minuscules. Vide ou contenant uniquement des espaces renvoie 400 missing_query.
limiteentier 1-5015Limité aux bornes ; non numérique revient à 15.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  "https://olud.ai/api/v1/search?q=ocr&limit=5"

Notation, afin que vous puissiez prédire l'ordre : correspondance exacte du nom 100, le nom commence par q 60, le nom contient q 40, la description contient q 15, et +20 lorsque q est exactement l'un des sujets du projet. Les égalités sont départagées par les étoiles. Tout ce qui obtient 0 est supprimé. La correspondance est une sous-chaîne simple — pas de racinage, pas de tolérance aux fautes de frappe. Les cartes ont la même forme que /projects. meta donne le total (toutes les correspondances, pas la page) et q comme normalisé.

GET /emerging

L'indice de découverte quotidien : jeunes repos avec une croissance soutenue et, lorsque nous pouvons les évaluer, une bonne santé. Classé par notre Discovery Score. Les cartes portent deux champs supplémentaires : signaux et détectés, la date à laquelle le projet est entré pour la première fois dans l'indice (null lorsque inconnu).

ParamètreType / limitesPar défautComportement
limiteentier 1-10025Limité. Vous pouvez recevoir moins de lignes que demandé : les identifiants qui ne sont plus dans le graphique des projets sont ignorés.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  "https://olud.ai/api/v1/emerging?limit=10"

La fraîcheur dépend du plan, et meta indique lequel vous avez. Pro et Business lisent l'indice de ce matin et obtiennent meta.freshness "en temps réel". Free et Dev lisent l'instantané d'hier et obtiennent "précédent-jour" plus une meta.note. Deux conséquences à connaître : sur Free et Dev, meta.criteria est null, car le texte des critères est stocké uniquement avec l'indice actuel ; et si le fichier d'instantané d'hier est manquant, Free et Dev reçoivent l'indice actuel tandis que meta.freshness lit toujours "précédent-jour".

GET /alternatives/{product}

Alternatives open-source à un produit commercial. data renvoie nom, domaine, desc, page, cat et un tableau d'alternatives dont les entrées contiennent nom, repo, site, licence et desc. repo peut être une chaîne vide lorsque le projet n'a pas de repo GitHub. meta donne généré.

ParamètreTypePar défautComportement
produitsegment de chemin, requisaucunEn minuscules. Clé de produit exacte en premier ; à défaut, le premier produit dont la clé ou le nom contient votre chaîne, dans l'ordre des fichiers. Aucun segment ne renvoie 400 missing_id ; aucune correspondance ne renvoie 404 not_found. Passez le slug exact — celui dans l'URL de la page /alternatives/<slug>.html — lorsque vous avez besoin d'un produit spécifique, car la correspondance lâche prend le premier résultat, pas le meilleur.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  https://olud.ai/api/v1/alternatives/notion

Points de terminaison : modèles, hf, historique

GET /models

Catalogue de modèles construit à partir d'OpenRouter. Chaque ligne : id, nom, fournisseur, tier, ctx (fenêtre de contexte en tokens), price_in et price_out (dollars américains par million de tokens, arrondi à deux décimales, 0 lorsque la source ne signale aucun prix), modalité (par exemple texte->texte ou texte+image+fichier->texte), outils (booléen) et rang. Les champs sans valeur sont supprimés de la ligne, donc vérifiez la présence plutôt que de supposer que price_in existe. meta donne total, limite, décalage, généré et source.

ParamètreType / limitesPar défautComportement
fournisseurchaînepas de filtreCorrespondance de sous-chaîne, insensible à la casse. provider=mistral correspond à "Mistral AI".
tiergratuit ou payantpas de filtreCorrespondance exacte, insensible à la casse sur votre entrée.
limiteentier 1-20050Limité aux bornes ; non numérique revient à 50.
décalageentier 0-1000000Appliqué à la liste fusionnée. Les lignes à poids ouvert viennent en premier, puis celles propriétaires, chaque bloc dans l'ordre de classement.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  "https://olud.ai/api/v1/models?tier=free&provider=mistral&limit=20"
tier n'est pas un indicateur de prix. gratuit signifie que les poids sont publiés (poids ouverts), payant signifie propriétaire. Un modèle à poids ouvert peut avoir un price_in non nul, car le prix est ce qu'un fournisseur d'API facture pour l'exécuter. De plus, le rang redémarre à 1 dans chaque tier, donc trier la liste fusionnée par rang mélange deux échelles.

GET /hf

Hugging Face : les plus téléchargés et tendance. Aucun paramètre. data a trois tableaux — top_llm (modèles de génération de texte par téléchargements sur 30 jours), top (toutes les tâches, limité à 30 lignes par ce point de terminaison) et tendance (les hausses d'aujourd'hui). Chaque ligne : id, org, nom, tâche, téléchargements (30 jours glissants), likes, licence, gated, créé, mis à jour, tendance (hausse des likes sur les lignes tendance, null ailleurs). meta donne généré et source.

curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  https://olud.ai/api/v1/hf
Ce point de terminaison dépend d'un scan matinal du Hugging Face Hub. Avant que ce fichier n'existe, l'appel renvoie 503 not_ready. Rien de votre côté n'est incorrect ; réessayez plus tard dans la journée.

GET /history/{slug}

Série quotidienne pour un projet : étoiles, santé et vélocité sur 7 jours. Clés Pro et Business uniquement — le plan Business est celui vendu comme Org. Tout autre plan obtient 403 pro_required. data renvoie slug, jours (dates au format AAAA-MM-JJ, le plus ancien en premier) et série avec trois tableaux, étoiles, santé et vélocité_7d, alignés index par index avec les jours. Les entrées individuelles peuvent être nulles lorsque la valeur d'un jour était manquante. meta donne points et fenêtre.

ParamètreTypePar défautComportement
slugsegment de chemin, requisaucunEn minuscules, et doit correspondre à ^[a-z0-9][a-z0-9._-]*$ — le même id que /project/{id}. Tout autre chose renvoie 400 bad_slug. Un slug valide sans rien enregistré renvoie 404 not_found.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \
  https://olud.ai/api/v1/history/ollama-ollama
meta.window lit toujours "90 jours glissants" — c'est le plafond maintenu au moment de la construction, pas une promesse de 90 points. L'enregistrement commence lorsqu'un projet entre dans le graphique, donc les séries courtes sont normales. Lisez meta.points pour ce que vous avez réellement reçu, et dimensionnez votre graphique à partir des jours, jamais à partir d'un 90 codé en dur.

Quotas quotidiens

Les requêtes sont comptées par clé, par jour UTC. Le compteur se réinitialise à 00:00 UTC. Il n'y a pas de limite par seconde ou par minute dans le code.

Plan sur la cléRequêtes / jourNom publicCe que cela change
gratuit500GratuitTout sauf /history. /emerging sert l'index du jour précédent.
dev5000DevMême accès que Gratuit, plafond plus élevé.
pro50000Pro/history s'ouvre. /emerging sert l'index de ce matin.
business500000OrgMême accès que Pro, plafond plus élevé.

Ce qui compte : chaque point de terminaison sauf /meta, une unité par requête. Le compteur est incrémenté juste après la vérification de la clé et avant toute autre chose, donc un 304, un 404 not_found, un 400 missing_query et un 503 graph_unavailable coûtent tous une unité. Seuls 401 missing_key et 403 invalid_key ne coûtent rien, car il n'y a pas de clé valide à facturer.

Un champ de quota défini sur votre clé remplace le défaut du plan. X-RateLimit-Limit est l'autorité sur votre plafond, pas le tableau ci-dessus.

Au-delà du plafond, chaque requête renvoie 429 quota_exceeded jusqu'à la réinitialisation. La requête refusée n'est pas comptée, rien n'est mis en file d'attente et rien n'est facturé. Aucun en-tête Retry-After n'est envoyé — la réinitialisation est à 00:00 UTC.

À quoi ressemble une requête refusée sur une clé gratuite :

HTTP/2 429
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 0

{"ok":false,"error":{"code":"quota_exceeded","message":"Quota quotidien atteint (500 requêtes/jour sur le plan \"gratuit\"). Se réinitialise à 00:00 UTC."}}
Le serveur MCP à /mcp.php a son propre compteur : 25 appels/jour sans clé (comptés par IP), 200 sur Gratuit, 2000 sur Dev, 20000 sur Pro, 200000 sur Business. Les questions que votre éditeur pose via MCP ne consomment pas le quota REST, et vice versa.
Un mode de défaillance à reconnaître : si le fichier de compteur ne peut pas être ouvert, les requêtes sont laissées passer et X-RateLimit-Remaining lit 0. Un 200 accompagné de Remaining: 0 signifie que le compteur était indisponible, pas que vous êtes à court de quota.

Codes d'erreur

Chaque erreur renvoie error.code comme une chaîne stable. Branchez-vous là-dessus, pas sur le texte du message, qui contient des slugs et des noms de plan et change avec la requête.

HTTPcodeDéclencheurQue faire
401missing_keyPas d'en-tête X-Api-Key et pas de ?key=. Aussi ce que vous obtenez pour un chemin inconnu lorsque aucune clé n'est envoyée, car la vérification de la clé s'exécute avant le routage.Envoyez la clé. Un 401 sur un chemin dont vous êtes sûr qu'il existe est un en-tête manquant, pas un mauvais chemin.
403invalid_keyLa clé n'est pas dans le magasin, ou son drapeau actif est faux. Faire tourner votre clé supprime la précédente.Lisez la clé actuelle depuis votre page de compte et mettez à jour l'appelant. La rotation casse chaque copie déployée de l'ancienne clé en même temps.
403pro_required/history appelé avec une clé gratuite ou dev.Lisez les valeurs d'aujourd'hui depuis /project/{id} (santé, vélocité), ou passez à Pro.
429quota_exceededLe compteur quotidien a atteint votre plafond.Arrêtez d'appeler jusqu'à 00:00 UTC, ou augmentez le plan. Lisez X-RateLimit-Limit pour confirmer votre vrai plafond.
400missing_id/project ou /alternatives appelé sans segment de chemin.Ajoutez le segment. /v1/project seul n'est pas une liste — utilisez /v1/projects.
400requête_manquante/search avec q vide ou uniquement des espaces.Envoyez un q non vide. Notez que la demande a tout de même été comptabilisée.
400slug_invalide/history slug vide, ou contenant un caractère en dehors de ^[a-z0-9][a-z0-9._-]*$.Passez l'id exactement tel que retourné par /projects ou /search.
404non_trouvéID de projet inconnu, aucune alternative suivie pour ce produit, ou aucune histoire stockée pour ce slug.Pour un projet, réessayez via /search?q=. Pour l'historique, le projet a peut-être été suivi trop récemment pour avoir des points.
404point_de_terminaison_inconnuClé valide, chemin non présent dans le routeur.Vérifiez l'orthographe. /meta liste six points de terminaison et omet /hf et /history, qui existent tous deux.
503graphique_indisponibleUn fichier graphique est manquant ou illisible, ce qui se produit pendant que la construction du matin l'écrit.Réessayez dans une minute. Votre clé est correcte ; ne la faites pas tourner.
503pas_prêt/hf avant que le scan du matin de Hugging Face n'ait produit son fichier.Réessayez plus tard dans la journée. D'autres points de terminaison ne sont pas affectés.
304pas_de_corpsIf-None-Match a correspondu à l'ETag actuel.Servez votre copie mise en cache. N'oubliez pas qu'elle a consommé une demande de votre quota.

Politique de réessai qui correspond au code : réessayez sur 503 après une minute, et sur 429 seulement après la réinitialisation UTC. Ne réessayez jamais un 400, 403 ou 404 inchangé — la réponse ne changera pas et chaque tentative coûte une demande.

Validez les paramètres de votre côté avant d'envoyer. Les valeurs de limite et d'offset hors limites sont silencieusement clampées plutôt que rejetées, donc une mauvaise valeur vous coûte une demande et renvoie une page que vous n'avez pas demandée. Comparez meta.limit et meta.offset avec ce que vous avez envoyé lorsque le résultat semble court.

Webhooks

Créer un webhook

Un webhook est un POST signé à votre point de terminaison par événement. Vous choisissez les événements, les repos et la forme du corps. Les webhooks nécessitent un plan payant : sur une clé gratuite, la création répond 403 paid_feature et la page de compte montre un panneau verrouillé au lieu du formulaire.

Plan signalé par l'APIWebhooks autorisés
gratuit0
dev3
pro10
business50

Un nom de plan que nous ne reconnaissons pas revient à 1. Une fois que vous avez atteint votre quota, la création répond 429 limit_reached — supprimez-en un ou changez de plan.

Depuis votre compte

  • Connectez-vous et ouvrez /account.html. La carte Webhooks apparaît une fois que votre clé API a été chargée, et reste cachée jusqu'à ce moment.
  • Collez votre point de terminaison dans le champ URL. La page refuse tout ce qui ne commence pas par https://.
  • Cochez les événements. release et health sont cochés pour vous ; license et new_project ne le sont pas.
  • Laissez le champ des repos vide pour recevoir tout, ou tapez des entrées owner/name séparées par des virgules.
  • Choisissez la destination dans le sélecteur : votre propre point de terminaison (JSON brut signé), Slack ou Discord.
  • Appuyez sur Créer. Le secret apparaît une fois, dans une boîte verte. Copiez-le avant de quitter la page — rien ne le montrera à nouveau.
  • Chaque ligne de webhook comporte alors un bouton Envoyer un test ping et un lien Supprimer. La suppression arrête immédiatement les livraisons.

Depuis l'API — créer. Votre clé API est celle de votre compte, envoyée en tant que X-Api-Key (ou ?key=).

curl -sS -X POST https://olud.ai/api/webhooks.php \
  -H "X-Api-Key: $OSAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "url": "https://your-app.example/osai-hook",
        "events": ["release", "health", "license"],
        "repos": ["acme/inference-server", "acme/tiny-router"],
        "format": "json"
      }'

Le bloc de données de la réponse. Chaque réponse contient également un bloc méta avec notre ligne d'attribution. Le secret est ici et nulle part ailleurs.

{
  "id": "whk_9b41c7e2f0a5d3861c4e",
  "url": "https://your-app.example/osai-hook",
  "events": ["release", "health", "license"],
  "repos": ["acme/inference-server", "acme/tiny-router"],
  "secret": "whs_7d2a19f4c6b03e58a1d7f492c0b6e35847ac91d2e6f0b8a3",
  "note": "Conservez ce secret maintenant — il n'est montré qu'une seule fois. Vérifiez chaque livraison : X-OSAI-Signature == \"sha256=\" + HMAC_SHA256(raw_body, secret). Testez-le : POST {\"ping\":\"whk_9b41c7e2f0a5d3861c4e\"}."
}

Ce que l'URL doit satisfaire

  • Le schéma https. http est rejeté.
  • Un hôte doit être présent.
  • Si vous écrivez un port, il doit être 443. https://your-app.example:8443/hook est rejeté.
  • L'hôte ne peut pas être localhost et ne peut pas se terminer par .local ou .internal.
  • Nous résolvons l'hôte. Si une adresse qu'il retourne se trouve dans une plage privée ou réservée, l'URL est rejetée.
  • Nous n'appelons pas votre point de terminaison lors de la création. Un hôte qui échoue à se résoudre passe ce contrôle et échoue plus tard, lors de la livraison. Envoyez un ping de test pour le découvrir.
  • Une mauvaise URL répond 400 bad_url.

Quels repos acceptent

  • La valeur par défaut est ["*"] — chaque repo que nous suivons.
  • Les entrées sont tronquées et mises en minuscules, donc la correspondance est insensible à la casse.
  • Une entrée doit ressembler à owner/name : owner commence par une lettre ou un chiffre, puis des lettres, des chiffres, un point, un underscore, un tiret.
  • Les entrées qui ne correspondent pas sont supprimées sans un mot. Si rien ne survit, vous obtenez 400 bad_repos.
  • "*" n'importe où dans la liste remplace toute la liste. ["acme/one", "*"] est stocké comme ["*"].
  • Plus de 100 entrées répondent 400 too_many_repos. Utilisez "*" au-delà de ce point.

Les quatre événements

Chaque événement porte un repo. Un événement est un POST — les événements ne sont jamais regroupés. Le nom se trouve dans l'en-tête X-OSAI-Event et dans le champ événement du corps.

événementSe déclenche lorsqueClés à l'intérieur des données
santél'étiquette de santé d'un repo changede, à, étoiles, page
versionun nouveau tag de version apparaît pour un repotag, nom, url
licencela licence que nous détenons pour un repo changede, à
nouveau_projetun repo entre dans le répertoireétoiles, page

santé

{
  "webhook_id": "whk_9b41c7e2f0a5d3861c4e",
  "event": "health",
  "repo": "acme/inference-server",
  "data": {
    "from": "Thriving",
    "to": "Slowing down",
    "stars": 58120,
    "page": "https://olud.ai/project/acme-inference-server.html"
  },
  "sent_at": "2026-07-27T05:41:12+00:00"
}

de et à sont des étiquettes, pas des nombres. Les six étiquettes que nous publions : Thriving, Healthy, Maintained, Slowing down, At risk, Archived. étoiles est le nombre d'étoiles que nous détenons pour le repo. Aucun événement ne se déclenche la première fois qu'un repo obtient une étiquette — un changement nécessite une valeur précédente à comparer.

version

{
  "webhook_id": "whk_9b41c7e2f0a5d3861c4e",
  "event": "release",
  "repo": "acme/inference-server",
  "data": {
    "tag": "v0.6.2",
    "name": "Acme Inference Server",
    "url": "https://github.com/acme/inference-server/releases"
  },
  "sent_at": "2026-07-27T05:41:12+00:00"
}

tag est le tag git. nom est le nom d'affichage du projet dans notre répertoire, pas le titre de la version. url pointe toujours vers la page des versions du repo, jamais vers une version spécifique — construisez l'URL du tag vous-même si vous en avez besoin.

licence

de et à sont les chaînes de licence que nous détenons. Cet événement n'a pas de clé de page.

{
  "webhook_id": "whk_9b41c7e2f0a5d3861c4e",
  "event": "license",
  "repo": "acme/inference-server",
  "data": {
    "from": "Apache-2.0",
    "to": "BSL-1.1"
  },
  "sent_at": "2026-07-27T05:41:12+00:00"
}

nouveau_projet

{
  "webhook_id": "whk_9b41c7e2f0a5d3861c4e",
  "event": "new_project",
  "repo": "acme/tiny-router",
  "data": {
    "stars": 1840,
    "page": "https://olud.ai/project/acme-tiny-router.html"
  },
  "sent_at": "2026-07-27T05:41:12+00:00"
}

Au maximum 25 événements new_project par exécution, les plus hauts comptes d'étoiles en premier. Lors d'une première exécution, ou lorsque plus de 200 repos apparaissent en même temps, ils sont enregistrés et aucun n'est envoyé — cette protection empêche une réimportation de vous inonder.

Corps et en-têtes

Avec le format json, le corps a cinq clés, dans cet ordre. Rien d'autre n'est ajouté.

{
  "webhook_id": "whk_9b41c7e2f0a5d3861c4e",
  "event": "release",
  "repo": "acme/inference-server",
  "data": { },
  "sent_at": "2026-07-27T05:41:12+00:00"
}

webhook_id est l'id que vous avez créé : whk_ plus 20 caractères hexadécimaux. repo est le propriétaire/nom en minuscules, et est nul uniquement lors d'un ping de test. data est un objet, vide pour un événement qui ne porte aucun champ. sent_at est UTC, ISO 8601 avec un décalage.

En-têteValeur
Content-Typeapplication/json
User-Agentolud.ai-Webhooks/1.0
X-OSAI-Eventsanté, version, licence, nouveau_projet ou ping
X-OSAI-Delivery16 caractères hexadécimaux, frais pour chaque POST
X-OSAI-Signaturesha256= suivi de l'HMAC-SHA256 du corps

Ces cinq-là constituent l'ensemble complet. X-OSAI-Delivery n'est pas stocké de notre côté — utilisez-le pour repérer un doublon dans votre propre journal.

Un test de ping, envoyé par POST {"ping":"whk_…"}. Même en-têtes, même signature, X-OSAI-Event: ping.

{
  "webhook_id": "whk_9b41c7e2f0a5d3861c4e",
  "event": "ping",
  "repo": null,
  "data": {
    "message": "Ça fonctionne. De vrais événements arrivent après chaque scan matinal."
  },
  "sent_at": "2026-07-27T05:41:12+00:00"
}
Répondez 2xx, et répondez rapidement. Nous attendons 6 secondes, puis comptons un échec. Vérifiez la signature, poussez l'événement dans une file d'attente, renvoyez 200, et faites le travail ensuite.

Vérification de la signature

Recalculez l'HMAC-SHA256 du corps brut avec votre secret, préfixez-le avec sha256=, et comparez-le à X-OSAI-Signature. Le secret est la chaîne whs_ de la réponse de création : whs_ plus 48 caractères hexadécimaux.

PHP

<?php
$raw    = file_get_contents('php://input');   // octets bruts, avant tout parsing
$sent   = $_SERVER['HTTP_X_OSAI_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $raw, $SECRET);

if (!hash_equals($expect, $sent)) {           // temps constant
    http_response_code(401);
    exit;
}

$event = json_decode($raw, true);             // parse uniquement après la vérification
http_response_code(200);

Node, avec Express

const crypto  = require('crypto');
const express = require('express');
const app = express();

// express.raw garde les octets. express.json() les détruirait.
app.post('/osai-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const sent   = req.get('X-OSAI-Signature') || '';
  const expect = 'sha256=' + crypto.createHmac('sha256', SECRET)
                                   .update(req.body).digest('hex');

  // timingSafeEqual lance une RangeError sur des buffers de longueur différente,
  // donc vérifiez d'abord la longueur, puis comparez en temps constant.
  const a = Buffer.from(sent), b = Buffer.from(expect);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(200);
});

Python, avec Flask

import hmac, hashlib, json
from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/osai-hook")
def osai_hook():
    raw    = request.get_data()               # octets, avant tout parsing
    expect = "sha256=" + hmac.new(SECRET.encode(), raw, hashlib.sha256).hexdigest()
    sent   = request.headers.get("X-OSAI-Signature", "")

    if not hmac.compare_digest(expect, sent):  # temps constant
        abort(401)

    event = json.loads(raw)
    return "", 200

Comparez avec hash_equals, crypto.timingSafeEqual ou hmac.compare_digest. Jamais avec == ou ===. Une comparaison de chaînes simple s'arrête au premier octet qui diffère, donc le temps que cela prend indique à un attaquant combien d'octets de tête ils ont bien devinés. Répétez la mesure et ils récupèrent la signature octet par octet, puis vous postent des événements falsifiés. Les trois fonctions ci-dessus lisent chaque octet, quel que soit l'entrée.

  • Hachez les octets bruts, avant tout parsing. Un corps qu'un framework a analysé et re-sérialisé hache à quelque chose d'autre, et chaque livraison aura l'air invalide.
  • Le format json échappe les caractères non-ASCII sous la forme \uXXXX. La re-sérialisation perd cela aussi.
  • Un en-tête manquant arrive sous forme de chaîne vide. Tous les trois exemples le rejettent au lieu de planter.
  • Le secret est montré une fois, à la création. Si vous le perdez, supprimez le webhook et créez-en un autre — vous obtiendrez un nouvel id et un nouveau secret.
  • Gardez un secret par webhook. Deux webhooks ne partagent jamais un seul.
Slack et Discord ignorent X-OSAI-Signature. Nous l'envoyons quand même, et cela couvre toujours ce qu'ils reçoivent : la signature est calculée sur les octets effectivement postés, quel que soit le format.

json, slack, discord

le format est choisi à la création, et par défaut c'est json. Il n'y a pas de point de terminaison de mise à jour : pour le changer, supprimez le webhook et créez-en un autre. GET rapporte le format de chaque webhook ; la réponse de création ne l'écho pas. Une valeur en dehors des trois répond 400 bad_format.

formatCe que nous postonsURL à coller
jsonla charge utile ci-dessus, inchangéevotre propre point de terminaison
slacktexte, plus un bloc de section mrkdwnune URL de webhook entrant Slack
discordun embed : titre, url, description, couleur, pied de pageune URL de webhook de canal Discord

slack

Un événement de publication, posté sur Slack

{
  "text": "🚀 acme/inference-server a publié v0.6.2 — Acme Inference Server",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "🚀 *<https://github.com/acme/inference-server/releases|acme/inference-server a publié v0.6.2>*\nAcme Inference Server"
      }
    }
  ]
}

Obtenez l'URL de Slack : ajoutez une application à l'espace de travail, activez les Webhooks entrants, ajoutez-en un pour le canal que vous souhaitez, copiez l'URL https://hooks.slack.com/services/… et collez-la dans le champ url. Le texte est toujours rempli — c'est ce que Slack affiche dans la notification et dans les clients qui ne rendent pas les blocs.

discord

Un événement de licence, posté sur Discord

{
  "embeds": [
    {
      "title": "🔑 acme/inference-server a changé de licence",
      "url": "https://github.com/acme/inference-server",
      "description": "Apache-2.0 → BSL-1.1",
      "color": 14427686,
      "footer": { "text": "olud.ai" }
    }
  ]
}

Obtenez l'URL depuis Discord : paramètres de canal, Intégrations, Webhooks, Nouveau Webhook, Copier l'URL du Webhook — https://discord.com/api/webhooks/… — et collez-la dans le champ url. La couleur est un entier décimal : 14427686 (#DC2626) pour la licence, 6514417 (#6366F1) pour la version, new_project et ping.

Les deux hôtes passent la vérification de l'URL. Lorsqu'un événement de licence ne contient pas de page, le lien revient à https://github.com/owner/name ; health et new_project renvoient à la page du projet sur olud.ai ; release renvoie à la page des versions du repo.

Défaut connu, health sur slack et discord uniquement. Le constructeur de messages lit de et à en tant que nombres, mais health porte des étiquettes. Le titre apparaît comme "acme/inference-server health 0 → 0", le corps lit toujours "La maintenance s'améliore.", et la couleur est toujours verte — y compris lorsque le score diminue. Le format json n'est pas affecté : il transmet les deux étiquettes telles quelles. Utilisez json si vous avez besoin des étiquettes de santé.

Livraison, échec, désactivation

Les livraisons reposent sur l'alerte pass, send-alerts.php — la même détection qui produit les e-mails des membres. Le dispatch s'exécute juste après la détection, dans le même processus. Il n'y a pas de calendrier séparé et pas de file d'attente. L'API décrit la cadence comme "quotidienne, après le scan du matin".

  • Un POST par événement, par webhook correspondant. Un webhook reçoit un événement lorsque le nom de l'événement est dans sa liste d'événements, et lorsque le repo est dans sa liste de repos ou que repos est ["*"] .
  • Un succès est HTTP 200 à 299, dans le délai de 6 secondes. Un 4xx, un 5xx, un délai d'attente, un échec DNS ou TLS comptent tous comme des échecs.
  • Un succès remet fails à 0 et ajoute 1 à delivered.
  • Un échec ajoute 1 à fails. Il n'y a pas de nouvelle tentative. L'événement n'est pas renvoyé — la prochaine livraison est le prochain changement que nous détectons.
  • Après 10 échecs consécutifs, le webhook est désactivé, estampillé avec l'heure UTC de ce moment, et les événements restants de cette exécution sont ignorés pour lui.
  • Un webhook désactivé reste dans votre liste, marqué comme désactivé. Il n'y a pas d'appel pour le réactiver : supprimez-le et créez-en un autre, avec un nouvel id et un nouveau secret.

Lecture de l'état

curl -sS https://olud.ai/api/webhooks.php -H "X-Api-Key: $OSAI_KEY"

La réponse, méta incluse

{
  "ok": true,
  "data": [
    {
      "id": "whk_9b41c7e2f0a5d3861c4e",
      "url": "https://your-app.example/osai-hook",
      "events": ["release", "health"],
      "repos": ["*"],
      "format": "json",
      "created": "2026-07-20T09:14:02+00:00",
      "delivered": 37,
      "fails": 0,
      "disabled": null
    }
  ],
  "meta": {
    "plan": "dev",
    "used": 1,
    "limit": 3,
    "events": ["health", "release", "license", "new_project"],
    "formats": ["json", "slack", "discord"],
    "delivery": "daily, after the morning scan",
    "signature": "X-OSAI-Signature: sha256=HMAC_SHA256(body, secret)"
  }
}

delivered est le nombre total de POST réussis. fails est la série actuelle consécutive, donc il revient à 0 lors du prochain succès. disabled est null, ou le timestamp auquel nous nous sommes arrêtés. Les secrets ne sont jamais renvoyés par GET.

Quatre façons dont les livraisons s'arrêtent

  • Votre point de terminaison continue d'échouer. Dix échecs consécutifs et le webhook est désactivé. Corrigez le point de terminaison, supprimez le webhook, créez-en un nouveau, pinguez-le.
  • Votre plan revient à gratuit — une annulation. Le webhook est conservé et ignoré à chaque dispatch, silencieusement. Changer de plan à nouveau le réveille, même id, même secret.
  • Vous faites tourner votre clé API dans votre compte. L'ancienne clé quitte le magasin de clés tandis que le webhook pointe toujours dessus : il disparaît de votre liste, il ne peut plus être pingé ou supprimé, et chaque dispatch l'ignore comme une clé gratuite. Supprimez vos webhooks avant de faire tourner la clé, puis recréez-les avec la nouvelle.
  • Vous le supprimez. Les livraisons s'arrêtent immédiatement.

Test sans attendre

curl -sS -X POST https://olud.ai/api/webhooks.php \
  -H "X-Api-Key: $OSAI_KEY" -H "Content-Type: application/json" \
  -d '{"ping":"whk_9b41c7e2f0a5d3861c4e"}'

Les deux résultats

HTTP/1.1 200
{"ok":true,"data":{"ping":"delivered","http":200}}

HTTP/1.1 502
{"ok":false,"error":{"code":"ping_failed",
  "message":"Votre point de terminaison a répondu HTTP 500 — un 2xx est attendu."}}

Le ping sort au format du webhook, signé de la même manière, avec l'événement ping et le repo null. Il ne déplace ni delivered ni fails, donc un ping échoué ne vous pousse jamais vers le seuil de désactivation. Cela fonctionne également sur un plan gratuit lorsque le webhook existe déjà, tandis que les livraisons programmées ne le font pas.

HTTP 0 dans un message ping_failed signifie que nous n'avons reçu aucune réponse : l'hôte n'a pas été résolu, TLS n'a pas été complété, ou les 6 secondes se sont écoulées.

Codes d'erreur

Chaque échec revient sous la même forme, avec un statut HTTP correspondant.

{
  "ok": false,
  "error": {
    "code": "bad_url",
    "message": "L'URL doit être https, accessible publiquement, port standard (pas localhost ou plages privées)."
  }
}
HTTPcodeQuand
400bad_jsonLe corps n'est pas un objet JSON.
400bad_urlL'URL a échoué à la vérification : pas https, un port autre que 443, localhost, .local, .internal, ou une adresse dans une plage privée ou réservée.
400bad_eventsAprès filtrage, les événements ne contenaient aucun de health, release, license, new_project.
400bad_reposAucune entrée n'était "*" ou un propriétaire/nom valide.
400too_many_reposPlus de 100 repos sur un seul webhook.
400bad_formatle format n'était pas json, slack ou discord.
401missing_keyPas d'en-tête X-Api-Key et pas de paramètre ?key=.
403bad_keyLa clé n'est pas dans notre magasin de clés. Une clé tournée ou révoquée se retrouve ici.
403paid_featureVotre plan permet 0 webhooks.
404non_trouvéL'id dans ping ou dans ?id= n'est pas associé à votre clé.
405method_not_allowedUne méthode autre que GET, POST ou DELETE.
429limit_reachedVous avez déjà autant de webhooks que votre plan le permet.
500store_write_failedNous n'avons pas pu écrire le changement sur le disque. Réessayez.
502ping_failedVotre point de terminaison a répondu au test ping avec autre chose qu'un 2xx.

Deux points pratiques. La gestion des webhooks ne consomme pas votre quota quotidien de requêtes API — ce point de terminaison ne touche jamais le compteur et n'envoie pas d'en-têtes X-RateLimit. Et DELETE est absent de la liste des méthodes autorisées CORS, donc un appel cross-origin depuis un navigateur échoue au prévol ; supprimez-le côté serveur, ou depuis la page de compte, qui est de même origine.

Automatisations

n8n : lire et recevoir

Deux nœuds couvrent les deux directions. Un nœud de requête HTTP lit nos données. Un nœud Webhook reçoit nos événements. Rien à installer depuis la liste de la communauté.

Lire le catalogue

Chaque point de terminaison répond GET à https://olud.ai/api/v1, avec votre clé dans l'en-tête X-Api-Key. /meta est le seul point de terminaison qui répond sans clé et sans toucher votre quota — utilisez-le comme premier nœud pendant que vous testez, car il prouve l'URL et le chemin réseau avant que vous ne dépensiez un appel.

Recevoir les événements

Ajoutez un nœud Webhook, méthode POST, et copiez son URL de production. Enregistrez cette URL dans votre compte (section Webhooks), ou POSTez-la à https://olud.ai/api/webhooks.php avec votre clé. Quatre événements au choix : release, health, license, new_project. Les livraisons sortent une fois par jour, dans le même passage qui envoie les alertes par e-mail — pas la seconde où quelque chose se produit.

Un corps de livraison, avec le format json (par défaut) :

Ce qui se trouve dans les données dépend de l'événement :

  • release — tag, nom, url (la page des versions du repo)
  • health — from, to, stars, page
  • license — from, to
  • new_project — stars, page
  • ping — message (livraisons de test uniquement)

Lors d'un événement de santé, from et to sont des étiquettes, pas des nombres : Thriving (score 85 et plus), Healthy (70+), Maintained (50+), Slowing down (30+), At risk (en dessous de 30). Comparez le texte dans votre nœud IF, pas les entiers.

Chaque livraison porte trois en-têtes : X-OSAI-Event, X-OSAI-Delivery (un id unique par livraison, utilisez-le pour supprimer les doublons) et X-OSAI-Signature, de la forme sha256=<HMAC-SHA256 du corps avec votre secret de webhook>. Le HMAC couvre les octets exacts que nous avons envoyés, donc si vous voulez le vérifier, activez l'option raw-body du nœud Webhook. Un corps qui a été analysé et re-sérialisé ne correspond jamais.

Collez ceci sur le canevas. Les deux nœuds sont laissés non connectés intentionnellement — chacun est son propre point de départ :

Ensuite, mettez votre clé dans le champ d'en-tête du nœud HTTP, et activez le flux de travail — l'URL de production d'un nœud Webhook écoute uniquement pendant que le flux de travail est actif.

Quand rien n'arrive

  • L'enregistrement de l'URL répond 400 bad_url : le point de terminaison doit être https, sur le port 443, sur un hôte publiquement résolvable. Un n8n auto-hébergé sur http, sur localhost, sur un nom *.local ou sur une plage d'IP privée est refusé.
  • L'enregistrement répond 403 paid_feature : les webhooks commencent au plan Dev — 3 points de terminaison sur Dev, 10 sur Pro, 50 sur Org. Une clé gratuite ne peut pas en créer un.
  • L'enregistrement répond 429 limit_reached : vous avez déjà autant de webhooks que votre plan le permet. Supprimez-en un d'abord.
  • Ne attendez pas demain pour le découvrir : POST {"ping":"whk_…"} à /api/webhooks.php et l'événement de test part immédiatement, dans la même forme qu'un réel. Si votre point de terminaison ne répond pas 2xx, vous obtenez 502 ping_failed avec le code qu'il a renvoyé.
  • Dix réponses consécutives non-2xx désactivent le webhook, silencieusement. Un succès réinitialise le compteur. Un webhook désactivé ne revient que par sa recréation. Le délai d'attente de livraison est de 6 secondes, donc un nœud qui répond lentement compte comme un échec.
  • Le webhook a été créé mais rien n'est livré et rien ne génère d'erreur : vérifiez le plan sur la clé qui le possède. S'il est revenu à Free, les livraisons sont ignorées tant que le point de terminaison reste enregistré.
  • Le secret est affiché une fois, à la création. Il n'y a aucun moyen de le lire à nouveau — supprimez et recréez.

Zapier et Make

Les mêmes deux directions, la même clé, aucune application à trouver dans leurs répertoires.

Recevoir : attraper le hook

Dans Zapier : Webhooks par Zapier, déclencheur Catch Hook. Dans Make : le module Webhooks, Webhook personnalisé. Les deux vous fournissent une URL https sur leur propre domaine, qui passe notre vérification. Collez-la dans votre compte comme point de terminaison webhook, choisissez vos événements, puis envoyez-vous un ping et confirmez que le Zap ou le scénario le reçoit avant de construire les étapes derrière.

Si vous prévoyez de vérifier X-OSAI-Signature, vous avez besoin du corps brut et des en-têtes. Dans Zapier, cela signifie Catch Raw Hook plutôt que Catch Hook. Dans Make, activez le paramètre webhook qui conserve les en-têtes de requête. Notre signature est un HMAC sur les octets exacts du corps, donc une étape qui analyse le JSON avant que vous ne le voyiez rend la comparaison impossible.

Lire : une étape HTTP

L'action GET des Webhooks de Zapier, ou le module HTTP de Make. Une étape, un en-tête :

Un succès est {"ok":true,"data":[…],"meta":{…}}. Une erreur est {"ok":false,"error":{"code":"…","message":"…"}} avec un statut HTTP correspondant. Testez ok avant de mapper les champs : un corps d'erreur n'a pas de données du tout, et un mappage qui lit data[0] sur une erreur produit silencieusement des valeurs vides en aval.

  • 401 missing_key — l'en-tête n'est pas arrivé. Vérifiez que l'étape l'envoie à chaque appel, pas seulement le premier.
  • 403 invalid_key — clé inconnue ou révoquée.
  • 429 quota_exceeded — quota quotidien atteint, se réinitialise à 00:00 UTC. Les réponses réussies portent X-RateLimit-Limit et X-RateLimit-Remaining, donc vous pouvez le voir venir.
  • 503 graph_unavailable — le graphique est en cours de reconstruction. Réessayez dans une minute ; c'est la seule erreur qui mérite une nouvelle tentative automatique.
  • 403 pro_required — /history/{slug} est réservé aux Pro et Org uniquement.
  • 404 not_found — aucun projet ou produit de ce type. Le message contient une suggestion de /search que vous pouvez suivre.
Ne planifiez pas cela toutes les minutes. Le graphique est reconstruit une fois chaque matin, donc toutes les heures est déjà plus souvent que les données ne changent. Les réponses portent un ETag dérivé de la date de construction et une demande répétée obtient 304 Not Modified — mais la clé est comptée avant cette comparaison, donc un 304 coûte toujours un appel contre votre quota quotidien.

GitHub Action : surveillance des dépendances

L'action lit les manifestes de dépendance du repo vérifié, nous demande la licence et l'historique de maintenance de chaque dépendance, et poste un commentaire sur la demande de tirage listant ce qui nécessite une décision. Elle réécrit ce même commentaire à chaque exécution — elle le retrouve à travers un marqueur caché — donc une longue demande de tirage ne se remplit pas de doublons.

L'ensemble du flux de travail, avec chaque entrée à sa valeur par défaut. Seul api-key est requis :

actions/checkout doit venir en premier : l'action lit les fichiers du répertoire de travail, et à la racine uniquement à moins que vous ne définissiez des chemins.

EntréePar défautCe qu'il fait
api-keyrequisEnvoyé en tant que X-Api-Key. Une recherche par dépendance distincte. Une clé gratuite fonctionne.
github-token${{ github.token }}Poste le commentaire. Nécessite des demandes de tirage : écriture sur le travail.
health-floor45Signalez une dépendance ayant un score inférieur à cela sur 100. 0 désactive la vérification de santé et conserve uniquement les licences. Lisez la limite connue ci-dessous.
licencesAGPL-3.0,BSL-1.1,SSPL-1.0,Elastic-2.0,BUSL-1.1,NOASSERTIONSéparés par des virgules, correspondants sans tenir compte de la casse à la licence que nous détenons. Le paramètre la remplace, il ne l'ajoute pas.
fail-on-findingfalseÉchouer la vérification au lieu de simplement commenter.
pathsemptyManifestes séparés par des virgules à lire. Vide signifie : chercher les quatre noms connus à la racine.

NOASSERTION est dans la liste par défaut intentionnellement : c'est ce que nous détenons lorsque le repo ne contient pas de fichier de licence qu'une machine peut reconnaître, ce qui est une décision à prendre plutôt qu'un détail. L'action expose une sortie, findings — le nombre de dépendances signalées, écrit même quand c'est 0.

Ce qu'il lit

  • package.json — les clés des dépendances et devDependencies. peerDependencies et optionalDependencies ne sont pas lus.
  • requirements.txt — un nom par ligne, commentaires supprimés, lignes commençant par - supprimées. Donc -r other-requirements.txt n'est pas suivi et -e . est ignoré.
  • pyproject.toml — uniquement les clés dont la valeur commence par une citation ou une accolade, ce qui est le style Poetry : fastapi = "^0.110". Un bloc PEP 621, dependencies = ["fastapi>=0.110"], ne produit rien du tout.
  • go.mod — lignes indentées de la forme name vX.

les chemins peuvent pointer n'importe où dans l'arborescence, par exemple apps/api/requirements.txt, mais le nom de base du fichier doit être l'un de ces quatre. Un fichier appelé requirements-dev.txt n'a pas de parseur : il est ignoré sans message.

Il ne fait pas échouer votre vérification par défaut. fail-on-finding est faux, donc une découverte est un commentaire, pas une construction rouge — un changement de licence est une décision, et un pipeline qui devient rouge pour quelque chose que personne ne peut corriger en cinq minutes apprend à l'équipe à le contourner. Deux choses échouent le travail : une clé api manquante, enregistrée comme ::error::api-key is required, et fail-on-finding : true avec au moins une découverte.

Comment cela échoue

  • Pas de manifeste à la racine, ou pas d'étape de checkout : "Aucun manifeste de dépendance trouvé — rien à vérifier." findings est 0 et le travail est vert. Une exécution verte ne signifie pas un arbre propre — lisez cette ligne.
  • L'événement n'a pas de pull request, par exemple lors d'un push : "Aucune pull request dans le contexte — commentaire ignoré." Les découvertes sont toujours dans le journal.
  • pull-requests : écriture manquante : "Impossible de poster le commentaire (HTTP 403)." Le travail reste vert et le commentaire n'apparaît jamais. Regardez le journal, pas la pull request.
  • Quota API quotidien atteint en cours d'exécution : ::warning::Quota API quotidien atteint — exécution partielle. Il cesse de rechercher des éléments et commente ce qu'il a réussi à résoudre. Un appel par dépendance distincte, donc 300 dépendances dépensent 300 des 500 appels quotidiens d'une clé gratuite.
  • Un manifeste qui ne peut pas être analysé : "⚠ <file> illisible (…) — ignoré". Les autres manifestes s'exécutent toujours.
  • L'étape composite exécute node depuis le PATH et utilise fetch natif. Les runners hébergés par GitHub portent déjà Node 20. Un runner auto-hébergé a besoin de actions/setup-node avec Node 20 ou plus.
  • licences : '' avec health-floor : '0' produit zéro découvertes pour toujours. Cette combinaison désactive les deux vérifications.

Ce que l'Action manque

Un nom de package n'est pas un nom de repo. Chaque nom passe par /api/v1/search?q=<name>&limit=5 et l'action conserve un résultat uniquement lorsque le nom du projet est égal au nom du package, en ignorant la casse. Tout le reste est supprimé dans le silence — pas de ligne de commentaire, pas d'avertissement. Deviner le résultat le plus proche déclencherait des alarmes sur le mauvais projet, ce qui est pire que de rester silencieux, mais cela signifie que la vérification couvre moins que votre arbre de dépendance.

  • Les packages npm scoppés perdent leur portée avant la recherche : @types/node est recherché comme node, ce qui peut correspondre à un repo non lié de ce nom. C'est le seul cas où le mauvais projet peut se retrouver dans le commentaire.
  • Les modules Go conservent leur chemin : github.com/gin-gonic/gin devient gin-gonic/gin, ce qui n'est jamais égal à un nom de repo (gin). Les dépendances go.mod ne se résolvent pas.
  • Un package dont le repo est nommé différemment — le cas courant en Python — ne se résout jamais.
  • Seuls les cinq premiers résultats sont examinés et seule la première correspondance exacte est utilisée. Lorsque deux repos partagent un nom, celui avec le plus d'étoiles l'emporte.

La ligne à lire dans le journal est : Résolu N de M dépendances · K signalées. Si N est bien en dessous de M, la vérification a examiné une fraction de votre arbre. Rien dans le commentaire de la pull request ne le dit.

Limite connue sur le health-floor. L'action lit le score de maintenance comme un champ d'objet, tandis que /api/v1/search renvoie la santé comme un nombre simple. Avec la réponse API d'aujourd'hui, la vérification de santé ne peut pas se déclencher, quel que soit le seuil que vous définissez : la liste des licences est ce qui produit des découvertes. Le score complet avec ses quatre composants est servi par /api/v1/project/{id} si vous en avez besoin entre-temps.

Spécification OpenAPI

Un fichier décrit l'API de lecture : https://olud.ai/openapi.json. OpenAPI 3.0.3, un serveur (https://olud.ai/api/v1), neuf opérations GET, un schéma de sécurité — une clé API dans l'en-tête X-Api-Key. Vous n'écrivez pas une intégration ; vous collez une adresse.

  • /meta — date de construction et nombre de projets. Déclaré sans sécurité : c'est la vérification de santé.
  • /search — q requis, limite de 1 à 50, par défaut 15.
  • /projects — lang, licence, vertical, health_min 0 à 100, trier par étoiles|santé|momentum|récent (par défaut étoiles), limite de 1 à 100 (par défaut 25), décalage de 0 à 100000 (par défaut 0).
  • /project/{id} — l'enregistrement complet fusionné, y compris la santé avec ses composants.
  • /emerging — limite de 1 à 100, par défaut 25.
  • /alternatives/{product} — alternatives open-source à un produit commercial.
  • /models — fournisseur, niveau, limite de 1 à 200 (par défaut 50), décalage.
  • /hf — les plus téléchargés et tendance sur Hugging Face.
  • /history/{slug} — 90 jours d'étoiles, de santé et de momentum. Pro et Org.

Importez-le

  • GPT personnalisé — Configurez, puis Actions, puis Importer depuis l'URL. Authentification : clé API, en-tête personnalisé, nom X-Api-Key.
  • Dify, Flowise, Open WebUI — ajoutez un outil à partir d'un schéma OpenAPI, collez l'URL, choisissez les opérations que vous souhaitez exposer, ajoutez le même en-tête.
  • Postman, Insomnia — Importer, puis Lien. Vous obtenez les neuf requêtes, documentées. Définissez X-Api-Key une fois au niveau de la collection afin que chaque requête l'hérite.
  • Générateurs de code — tout générateur OpenAPI produit un client typé, TypeScript, Python ou Go, à partir de ce fichier seul.

Quel que soit l'outil, il n'y a que deux choses à définir : l'URL du fichier et la clé comme en-tête nommé X-Api-Key. Si un import montre neuf opérations mais que chaque appel répond 401, l'en-tête n'est pas envoyé — c'est la première chose à vérifier.

Les quotas sont par clé et par jour, réinitialisés à 00:00 UTC : 500 requêtes sur Free, 5 000 sur Dev, 50 000 sur Pro, 500 000 sur Org. Ils sont comptés séparément du serveur MCP, donc un assistant posant des questions dans votre éditeur ne consomme jamais ce budget.

La gestion des webhooks n'est pas dans ce fichier. Elle se trouve à https://olud.ai/api/webhooks.php avec la même clé : GET liste vos points de terminaison avec leurs comptes de livraison et d'échec, POST en crée un ou envoie un ping, DELETE en supprime un. Un outil qui importe la spécification ne peut pas créer un webhook pour vous.

Quatre flux RSS

Pas de clé, pas d'inscription, pas de compte. feed.php accepte exactement quatre types : actualités, blog, versions, émergents.

URLCe qu'il transporteD'où cela vient
/feed.phpJusqu'à 30 nouveaux projets (★stars · owner/name), les nouveaux modèles du jour (Nouveau modèle : name (provider), avec la fenêtre de contexte dans la description), nouveaux espaces Hugging Face (Nouvel espace : name par author, avec le nombre de likes), et l'article du jour.today-data.json, reconstruit chaque heure
/feed.php?type=releasesJusqu'à 60 versions expédiées par les projets que nous suivons : titre projet + tag, lien vers la version, guid owner/name@tag.releases-data.json, reconstruit chaque heure
/feed.php?type=emergingJusqu'à 40 projets émergents — sains, en accélération, encore peu connus. Description : ★stars · santé N/100 · +N étoiles cette semaine.le graphique, reconstruit chaque matin
/feed.php?type=blogArticles sous /blog/<slug>/, /blog/alternatives/ et /reports/. Le titre et la description sont le titre propre de la page et la méta description ; la date est le temps de modification du fichier.lu depuis le disque, donc un nouvel article apparaît de lui-même

Les quatre répondent à RSS 2.0 en tant qu'application/rss+xml, en anglais, le plus récent en premier, avec Cache-Control : public, max-age=1800. C'est 30 minutes : un lecteur qui interroge plus souvent obtient la copie mise en cache, qui est la même réponse servie plus rapidement.

Les Guids sont stables et ne sont pas toujours le lien, ce qui est ce que vous voulez lors de la dé-duplication dans une automatisation : les versions utilisent owner/name@tag, les projets émergents utilisent emerging:<id>, l'article du jour utilise son URL plus la date, tout le reste utilise son lien.

Le piège : un type inconnu n'est pas une erreur. ?type=release au singulier, ou une faute de frappe, renvoie le fil d'actualités avec HTTP 200. Si deux de vos fils semblent suspectement identiques, vérifiez l'orthographe de type. Les valeurs sont tronquées et mises en minuscules, donc ?type=Releases est correct.

Pointez Slack, Teams ou Feedly vers eux pour lire, ou utilisez le déclencheur RSS dans n8n, Zapier et Make quand un horaire vous convient mieux qu'un webhook — c'est aussi le moyen d'obtenir des versions sans un plan payant. Si un fichier source n'a pas été reconstruit, le fil répond toujours 200 avec un canal vide plutôt qu'une erreur, donc une automatisation qui ne reçoit soudainement rien n'est pas nécessairement cassée.