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.
De quoi ai-je besoin ?
| Vous voulez… | Utiliser |
|---|---|
| Poser des questions depuis votre éditeur | Serveur MCP |
| Interroger les données depuis votre propre code | API REST |
| Être informé lorsque quelque chose change | Webhooks |
| L'intégrer dans n8n, Zapier ou Make | Automatisations |
| Vérifier les dépendances sur chaque demande de tirage | GitHub 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.
| Fait | Valeur |
|---|---|
| Point de terminaison | https://olud.ai/mcp.php |
| Méthode | POST, JSON-RPC 2.0. GET renvoie 405 (voir ci-dessous) |
| Transport | HTTP diffusé, un point de terminaison, pas de flux SSE |
| Version du protocole | 2025-06-18. Si le client demande 2025-03-26 ou 2024-11-05, le serveur répond dans cette version |
| Version du serveur | 1.0.0 |
| Session | Aucune. Pas de Mcp-Session-Id à conserver, rien à expirer, rien à reconnecter |
| Capacités | outils uniquement. resources/list, resources/templates/list et prompts/list répondent avec des listes vides au lieu d'une erreur |
| Auth | En-tête de requête X-Api-Key. Optionnel |
| Batching | Non 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.
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é" }
}
}
}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 }
]
}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.
| Outil | Paramètres | Limites et valeurs par défaut | Retours |
|---|---|---|---|
| search_open_source_ai | query (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érieur | correspondances (total trouvé, non tronqué), puis id, nom, résumé, étoiles, langue, licence, github, page, score de santé et étiquette, tendance récente |
| get_project | project (chaîne, requis) | Accepte un slug (ollama-ollama), owner/repo, une URL complète de github.com, ou le nom exact du projet | Tout 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_alternatives | product (chaîne, requis) | Nom du produit commercial. Correspond à son slug, puis à son nom exact | produit, page, et la liste des alternatives, elle-même limitée par votre plafond de résultats par appel |
| list_models | fournisseur (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érieur | correspondances, puis id, nom, fournisseur, contexte, price_per_million (entrée et sortie), modalité, appel_outil, et l'URL du leaderboard |
| projets_tendance | type (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érieur | type, 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.
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.
Volumes par plan
| Pas de clé | Gratuit | Dev | Pro | Org | |
|---|---|---|---|---|---|
| Appels par jour | 25 | 200 | 2,000 | 20,000 | 200,000 |
| Résultats par appel | 5 | 10 | 25 | 50 | 100 |
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
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ête | Valeur | Envoyé le |
|---|---|---|
| X-RateLimit-Limit | Votre plafond quotidien, sous forme d'entier | Chaque requête qui a passé la vérification de la clé |
| X-RateLimit-Remaining | Requêtes restantes aujourd'hui, arrondies à 0 | Chaque requête qui a passé la vérification de la clé, y compris le 429 |
| ETag | MD5 cité du temps de construction du graphique des projets | Chaque point de terminaison sauf /meta |
| Access-Control-Allow-Origin | * | Chaque réponse, y compris OPTIONS (qui répond 204) |
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.
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ètre | Type | Par défaut | Comportement |
|---|---|---|---|
| id | segment de chemin, requis | aucun | En 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
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ètre | Type / limites | Par défaut | Comportement |
|---|---|---|---|
| lang | chaîne | pas de filtre | Correspondance exacte sur la langue du projet, insensible à la casse. lang=rust garde uniquement Rust. |
| licence | chaîne | pas de filtre | Correspondance de sous-chaîne sur l'id de licence, insensible à la casse. license=gpl garde GPL-2.0 et AGPL-3.0. |
| vertical | chaîne | pas de filtre | Correspondance 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é_min | entier | pas de filtre | Garde 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 | étoiles | Toujours 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. |
| limite | entier 1-100 | 25 | Les 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écalage | entier 0-100000 | 0 | Plafonné 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ètre | Type / limites | Par défaut | Comportement |
|---|---|---|---|
| q | chaîne, requise | aucun | Ramené et en minuscules. Vide ou contenant uniquement des espaces renvoie 400 missing_query. |
| limite | entier 1-50 | 15 | Limité 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ètre | Type / limites | Par défaut | Comportement |
|---|---|---|---|
| limite | entier 1-100 | 25 | Limité. 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ètre | Type | Par défaut | Comportement |
|---|---|---|---|
| produit | segment de chemin, requis | aucun | En 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ètre | Type / limites | Par défaut | Comportement |
|---|---|---|---|
| fournisseur | chaîne | pas de filtre | Correspondance de sous-chaîne, insensible à la casse. provider=mistral correspond à "Mistral AI". |
| tier | gratuit ou payant | pas de filtre | Correspondance exacte, insensible à la casse sur votre entrée. |
| limite | entier 1-200 | 50 | Limité aux bornes ; non numérique revient à 50. |
| décalage | entier 0-100000 | 0 | Appliqué à 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"
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
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ètre | Type | Par défaut | Comportement |
|---|---|---|---|
| slug | segment de chemin, requis | aucun | En 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
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 / jour | Nom public | Ce que cela change |
|---|---|---|---|
| gratuit | 500 | Gratuit | Tout sauf /history. /emerging sert l'index du jour précédent. |
| dev | 5000 | Dev | Même accès que Gratuit, plafond plus élevé. |
| pro | 50000 | Pro | /history s'ouvre. /emerging sert l'index de ce matin. |
| business | 500000 | Org | Mê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."}}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.
| HTTP | code | Déclencheur | Que faire |
|---|---|---|---|
| 401 | missing_key | Pas 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. |
| 403 | invalid_key | La 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. |
| 403 | pro_required | /history appelé avec une clé gratuite ou dev. | Lisez les valeurs d'aujourd'hui depuis /project/{id} (santé, vélocité), ou passez à Pro. |
| 429 | quota_exceeded | Le 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. |
| 400 | missing_id | /project ou /alternatives appelé sans segment de chemin. | Ajoutez le segment. /v1/project seul n'est pas une liste — utilisez /v1/projects. |
| 400 | requê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. |
| 400 | slug_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. |
| 404 | non_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. |
| 404 | point_de_terminaison_inconnu | Clé 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. |
| 503 | graphique_indisponible | Un 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. |
| 503 | pas_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. |
| 304 | pas_de_corps | If-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.
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'API | Webhooks autorisés |
|---|---|
| gratuit | 0 |
| dev | 3 |
| pro | 10 |
| business | 50 |
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énement | Se déclenche lorsque | Clés à l'intérieur des données |
|---|---|---|
| santé | l'étiquette de santé d'un repo change | de, à, étoiles, page |
| version | un nouveau tag de version apparaît pour un repo | tag, nom, url |
| licence | la licence que nous détenons pour un repo change | de, à |
| nouveau_projet | un 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ête | Valeur |
|---|---|
| Content-Type | application/json |
| User-Agent | olud.ai-Webhooks/1.0 |
| X-OSAI-Event | santé, version, licence, nouveau_projet ou ping |
| X-OSAI-Delivery | 16 caractères hexadécimaux, frais pour chaque POST |
| X-OSAI-Signature | sha256= 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"
}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 "", 200Comparez 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.
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.
| format | Ce que nous postons | URL à coller |
|---|---|---|
| json | la charge utile ci-dessus, inchangée | votre propre point de terminaison |
| slack | texte, plus un bloc de section mrkdwn | une URL de webhook entrant Slack |
| discord | un embed : titre, url, description, couleur, pied de page | une 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.
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)."
}
}| HTTP | code | Quand |
|---|---|---|
| 400 | bad_json | Le corps n'est pas un objet JSON. |
| 400 | bad_url | L'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. |
| 400 | bad_events | Après filtrage, les événements ne contenaient aucun de health, release, license, new_project. |
| 400 | bad_repos | Aucune entrée n'était "*" ou un propriétaire/nom valide. |
| 400 | too_many_repos | Plus de 100 repos sur un seul webhook. |
| 400 | bad_format | le format n'était pas json, slack ou discord. |
| 401 | missing_key | Pas d'en-tête X-Api-Key et pas de paramètre ?key=. |
| 403 | bad_key | La clé n'est pas dans notre magasin de clés. Une clé tournée ou révoquée se retrouve ici. |
| 403 | paid_feature | Votre plan permet 0 webhooks. |
| 404 | non_trouvé | L'id dans ping ou dans ?id= n'est pas associé à votre clé. |
| 405 | method_not_allowed | Une méthode autre que GET, POST ou DELETE. |
| 429 | limit_reached | Vous avez déjà autant de webhooks que votre plan le permet. |
| 500 | store_write_failed | Nous n'avons pas pu écrire le changement sur le disque. Réessayez. |
| 502 | ping_failed | Votre 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.
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ée | Par défaut | Ce qu'il fait |
|---|---|---|
| api-key | requis | Envoyé 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-floor | 45 | Signalez 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. |
| licences | AGPL-3.0,BSL-1.1,SSPL-1.0,Elastic-2.0,BUSL-1.1,NOASSERTION | Sé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-finding | false | Échouer la vérification au lieu de simplement commenter. |
| paths | empty | Manifestes 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.
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.
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.
Quatre flux RSS
Pas de clé, pas d'inscription, pas de compte. feed.php accepte exactement quatre types : actualités, blog, versions, émergents.
| URL | Ce qu'il transporte | D'où cela vient |
|---|---|---|
| /feed.php | Jusqu'à 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=releases | Jusqu'à 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=emerging | Jusqu'à 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=blog | Articles 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.
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.