La configurazione, passo a passo
Ogni frammento di questa pagina è pronto da incollare. I numeri — quote, limiti, valori predefiniti, nomi dei campi — sono letti dal codice in esecuzione: questa pagina e il server dicono la stessa cosa.
Per iniziare
Ottieni la tua chiave
Tutto qui tranne i feed RSS utilizza una chiave API. Apri il tuo account, sezione Developer API, e copiala. Il piano gratuito non richiede carta.
La chiave porta il tuo piano, e il tuo piano porta i tuoi volumi giornalieri. Due contatori funzionano affiancati: uno per l'API REST, uno per il server MCP. Un editor che fa alcune domande non consuma mai il tuo budget API.
Di che cosa ho bisogno?
| Vuoi… | Usare |
|---|---|
| Porre domande dal tuo editor | Server MCP |
| Interrogare i dati dal tuo codice | REST API |
| Essere avvisato quando qualcosa cambia | Webhook |
| Collegarlo a n8n, Zapier o Make | Automazioni |
| Controllare le dipendenze in ogni pull request | Azione GitHub |
| Segui in un lettore, nessuna chiave | Feed RSS |
Server MCP
Il server MCP, in breve
MCP (Model Context Protocol) è un modo standard per un assistente AI di chiamare un servizio esterno. Incolli un indirizzo nel tuo client e l'assistente guadagna cinque strumenti che può usare mentre lavori. Quegli strumenti interrogano il catalogo olud.ai: progetti AI open-source, modelli AI con i loro prezzi e alternative open-source a prodotti commerciali.
| Fatto | Valore |
|---|---|
| Endpoint | https://olud.ai/mcp.php |
| Metodo | POST, JSON-RPC 2.0. GET restituisce 405 (vedi sotto) |
| Trasporto | HTTP streamabile, un endpoint, nessun flusso SSE |
| Versione del protocollo | 2025-06-18. Se il client richiede 2025-03-26 o 2024-11-05, il server risponde in quella versione |
| Versione del server | 1.0.0 |
| Sessione | Nessuna. Nessun Mcp-Session-Id da mantenere, nulla da scadere, nulla da riconnettere |
| Capacità | solo strumenti. resources/list, resources/templates/list e prompts/list rispondono con liste vuote invece di un errore |
| Auth | Intestazione della richiesta X-Api-Key. Facoltativa |
| Batching | Non supportato. Un array JSON-RPC viene rifiutato con HTTP 400 |
Il server legge. L'unica cosa che scrive è il tuo contatore di chiamate giornaliere.
Installarlo, client per client
Stesso URL per ogni client: https://olud.ai/mcp.php. La chiave viaggia nell'intestazione X-Api-Key. Senza una chiave, il server risponde comunque, con un limite minore (25 chiamate al giorno per IP, 5 risultati per chiamata). La tua chiave e un blocco pronto da incollare si trovano nel tuo account, sezione server MCP.
Claude Code, un comando:
claude mcp add --transport http opensourceai https://olud.ai/mcp.php \ --header "X-Api-Key: your-key" # senza una chiave: claude mcp add --transport http opensourceai https://olud.ai/mcp.php # controlla cosa è stato registrato: 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" }
}
}
}Quel file si trova in ~/Library/Application Support/Claude/claude_desktop_config.json su macOS, e %APPDATA%\Claude\claude_desktop_config.json su Windows. Esci e riapri Claude Desktop dopo averlo modificato; legge il file all'avvio.
Se Claude Desktop mostra il server come non disponibile o non lo elenca affatto, la tua build accetta solo server locali (stdio). Collega con mcp-remote, che necessita di Node installato:
{
"mcpServers": {
"opensourceai": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://olud.ai/mcp.php",
"--header", "X-Api-Key:${OSAI_KEY}"],
"env": { "OSAI_KEY": "your-key" }
}
}
}Cursor, .cursor/mcp.json nel progetto (o ~/.cursor/mcp.json per ogni progetto):
{
"mcpServers": {
"opensourceai": {
"url": "https://olud.ai/mcp.php",
"headers": { "X-Api-Key": "your-key" }
}
}
}VS Code, .vscode/mcp.json nello spazio di lavoro:
{
"servers": {
"opensourceai": {
"type": "http",
"url": "https://olud.ai/mcp.php",
"headers": { "X-Api-Key": "${input:osaiKey}" }
}
},
"inputs": [
{ "id": "osaiKey", "type": "promptString",
"description": "chiave API olud.ai", "password": true }
]
}Una chiave nell'URL come ?key=your-key funziona anche, perché il server la legge. Preferisci l'intestazione: le stringhe di query finiscono nella cronologia del browser, nei log del proxy e nella cronologia della shell.
Verificare che funzioni, con curl
Quando un client dice che un server non è disponibile, testa prima il server stesso. Questa chiamata elenca gli strumenti e non costa nulla: solo tools/call decrementa il tuo quota, quindi initialize, tools/list e ping sono gratuiti. Riavviare il tuo editor non consuma il giorno.
curl -s -X POST https://olud.ai/mcp.php \
-H "Content-Type: application/json" \
-H "X-Api-Key: your-key" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Una risposta sana è HTTP 200 con {"jsonrpc":"2.0","id":1,"result":{"tools":[ ... ]}} e i cinque nomi all'interno. Successivamente, spendi una chiamata:
curl -s -X POST https://olud.ai/mcp.php \
-H "Content-Type: application/json" \
-H "X-Api-Key: your-key" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search_open_source_ai",
"arguments":{"query":"text to speech","limit":5}}}'Una chiamata tools/call di successo porta due intestazioni di risposta da leggere: X-RateLimit-Limit è il tuo limite giornaliero, X-RateLimit-Remaining è ciò che rimane dopo questa chiamata. Sono impostati solo su tools/call.
Controllo di liveness più breve possibile. Risponde {"jsonrpc":"2.0","id":0,"result":{}} e non ha bisogno di chiave:
curl -s -X POST https://olud.ai/mcp.php \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":0,"method":"ping"}'I cinque strumenti
Non chiami questi per nome. Fai una domanda, il client sceglie lo strumento. I nomi sono importanti quando leggi un log o scrivi tu stesso la chiamata, e sono abbinati esattamente: search_open_source_ai funziona, Search_Open_Source_AI non funziona.
| Strumento | Parametri | Limiti e predefiniti | Restituisce |
|---|---|---|---|
| search_open_source_ai | query (stringa, obbligatoria), limit (intero) | limite limitato a 1 … il tuo tetto di risultati per chiamata. Predefinito 10, o il tuo tetto se è più basso | corrispondenze (totale trovato, non troncato), poi id, nome, riepilogo, stelle, linguaggio, licenza, github, pagina, punteggio di salute e etichetta, tendenza recente |
| get_project | progetto (stringa, obbligatoria) | Accetta uno slug (ollama-ollama), owner/repo, un URL completo di github.com, o il nome esatto del progetto | Tutto quanto sopra più proprietario, fork, creato, spinto, argomenti, il completo breakdown della salute, stelle guadagnate, e fino a 5 rilasci recenti |
| find_alternatives | prodotto (stringa, obbligatoria) | Nome del prodotto commerciale. Abbinato sul suo slug, poi sul suo nome esatto | prodotto, pagina, e l'elenco delle alternative, a sua volta limitato dal tuo tetto di risultati per chiamata |
| list_models | fornitore (string), max_price_out (numero), min_context (intero), free_only (booleano), limit (intero) | fornitore è una sottostringa non sensibile al maiuscolo. max_price_out è dollari per milione di token di output. min_context è in token. limit è limitato a 1 … il tuo limite massimo, predefinito 15 o il tuo limite massimo se inferiore | corrispondenze, poi id, nome, fornitore, contesto, price_per_million (input e output), modalità, tool_calling e l'URL della leaderboard |
| progetti_trending | tipo (string), limit (intero) | tipo è trending, emerging o top_health. Qualsiasi altra cosa torna a trending senza errore. Predefinito trending. limit è limitato a 1 … il tuo limite massimo, predefinito 10 o il tuo limite massimo se inferiore | tipo, le schede del progetto e il tempo di generazione dell'indice |
Domande che raggiungono ogni strumento
- search_open_source_ai — "Trova una libreria OCR open-source che posso eseguire localmente."
- get_project — "Quanto è sano ollama/ollama in questo momento, e quando è stata l'ultima spedizione?"
- find_alternatives — "Quale strumento open-source potrebbe sostituire Notion per noi?"
- list_models — "Quali modelli hanno almeno 128k di contesto e costano meno di $1 per milione di token di output?"
- trending_projects — "Quali progetti di AI open-source stanno guadagnando stelle più velocemente questa settimana?" Chiedi emerging invece per ottenere progetti sani che sono ancora poco conosciuti, o top_health per quelli meglio mantenuti.
I filtri scartano ciò che non possono giudicare. Con max_price_out impostato, un modello il cui prezzo di output non abbiamo un valore è escluso piuttosto che indovinato. Con min_context impostato, un modello senza finestra di contesto registrata conta come zero ed è escluso. free_only mantiene i modelli il cui livello è esattamente gratuito.
Un limite superiore al tuo massimo non è un errore: viene limitato silenziosamente. Lo schema pubblicato dice massimo 100 perché è il più alto che qualsiasi piano consente, non ciò che la tua chiave consente. Un limite che non è un numero torna al predefinito.
Volumi per piano
| Nessuna chiave | Gratuito | Sviluppatore | Pro | Org | |
|---|---|---|---|---|---|
| Chiamate al giorno | 25 | 200 | 2,000 | 20,000 | 200,000 |
| Risultati per chiamata | 5 | 10 | 25 | 50 | 100 |
Due limiti, perché fermano due cose diverse: le chiamate al giorno limitano il carico, i risultati per chiamata limitano quanti cataloghi escono in una risposta.
- Le chiamate MCP sono contate separatamente dalle chiamate API REST. Stessa chiave, due contatori. Un editor che pone alcune domande non tocca mai il tuo budget API.
- Solo tools/call è conteggiato. initialize, tools/list e ping non costano nulla.
- Entrambi i contatori si azzerano a 00:00 UTC.
- Senza una chiave, il conteggio è per indirizzo IP. Tutti dietro un IP di ufficio condividono i 25.
- Su Free e senza chiave, ogni risposta termina con una riga di nota che indica i tuoi due limiti. Le risposte Dev, Pro e Org non portano tale riga.
- Org è il piano chiamato business all'interno del sistema. I messaggi di errore stampano il nome interno.
Quando non funziona
Chiave digitata in modo errato, o revocata. Ogni domanda fallisce con:
Quella chiave API è sconosciuta o è stata revocata. Rimuovila per utilizzare il piano gratuito, o ottieni una nuova chiave su https://olud.ai/account.html
Una chiave errata non torna silenziosamente all'assegnazione anonima. Correggi la chiave o rimuovi completamente l'intestazione X-Api-Key, quindi riavvia il client in modo che legga di nuovo la configurazione.
Limite giornaliero raggiunto. Con una chiave, poi senza:
Quota giornaliera MCP raggiunta (200 chiamate/giorno sul piano "free"). Si azzera a 00:00 UTC. Livelli superiori: https://olud.ai/plans.html Limite anonimo raggiunto (25 chiamate/giorno per IP). Si azzera a 00:00 UTC. Una chiave API gratuita lo porta a 200/giorno — https://olud.ai/account.html
Niente è rotto e niente è addebitato. Aspetta le 00:00 UTC, o passa a un piano superiore. Se non hai una chiave, una chiave gratuita ti porta da 25 a 200 chiamate e da 5 a 10 risultati per chiamata.
Nome dello strumento non uno dei cinque:
Strumento sconosciuto "list_projects". Chiama tools/list per vedere cosa è disponibile.
Un argomento richiesto è mancante. Ogni messaggio mostra la forma che desidera:
Manca "query". Esempio: {"query": "testo in voce"}
Manca "project". Esempio: {"project": "ollama/ollama"}
Manca "product". Esempio: {"product": "Midjourney"}Niente trovato. Entrambi i messaggi ti dicono dove andare dopo:
Nessun progetto trovato per "ollamaa". Prova prima search_open_source_ai per ottenere il suo id esatto. Non tracciamo ancora "photoshop". Corrispondenze più vicine: <fino a 8 nomi>. Elenco completo: https://olud.ai/alternatives-hub.html
Il catalogo è in fase di ricostruzione. Aspetta un minuto e chiedi di nuovo; queste sono le quattro formulazioni, una per set di dati:
Il catalogo è in fase di ricostruzione. Prova di nuovo tra un minuto. Quell'elenco è in fase di ricostruzione. Prova di nuovo tra un minuto. Il catalogo dei modelli non è disponibile in questo momento. L'indice delle alternative non è disponibile in questo momento.
Aprire https://olud.ai/mcp.php in un browser restituisce HTTP 405. Questa è la risposta corretta: l'endpoint parla JSON-RPC su POST, e la specifica richiede 405 quando un server non offre alcun flusso GET. Il corpo ti dice cosa fare e porta la configurazione da copiare. Arriva su una riga; è spaziato qui per essere letto.
{
"server": "olud.ai MCP",
"version": "1.0.0",
"protocol": "2025-06-18",
"usage": "Questo endpoint parla MCP tramite JSON-RPC 2.0. Invia una richiesta POST da 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"
}Rifiuti a livello di trasporto. Quattro di questi portano un codice di errore JSON-RPC, e i primi tre portano anche un codice di errore HTTP:
405 -32600 Usa POST con un corpo JSON-RPC 2.0. (PUT, DELETE, HEAD…) 400 -32700 Errore di parsing: il corpo non è un JSON valido. 400 -32600 Il batching JSON-RPC non è supportato (rimosso in MCP 2025-06-18). 200 -32601 Metodo sconosciuto "tools/execute". 200 -32602 Nome dello strumento mancante nei parametri.
Se il tuo client si lamenta di un id sessione mancante, ignoralo e controlla il tipo di trasporto nella tua configurazione. Questo server non mantiene sessioni e non c'è un Mcp-Session-Id da restituire. Un client configurato per stdio o per SSE contro questo URL fallirà prima della prima chiamata; il tipo è http.
REST API
Autenticazione e chiavi
URL di base: https://olud.ai/api/v1. Ogni endpoint è un GET. L'intestazione CORS pubblicizza POST, ma v1 non ha una rotta POST: i parametri vengono sempre letti dalla stringa di query.
Invia la tua chiave nell'intestazione X-Api-Key. Un parametro di query ?key= funziona anche, per una scheda del browser o un test rapido. Se entrambi sono presenti, vince l'intestazione. Solo /meta funziona senza una chiave.
Entrambe le forme sono accettate:
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"
Per ottenere una chiave: accedi su olud.ai e apri la tua pagina account, sezione Developer API. La chiave viene creata alla tua prima visita, nel piano gratuito, e appare come osk_ seguito da 40 caratteri esadecimali. Ruotarla dalla stessa pagina elimina immediatamente la vecchia chiave — il vecchio valore restituisce quindi 403 invalid_key.
| Intestazione | Valore | Inviato il |
|---|---|---|
| X-RateLimit-Limit | Il tuo tetto giornaliero, come intero | Ogni richiesta che ha superato il controllo della chiave |
| X-RateLimit-Remaining | Richieste rimaste oggi, arrotondate a 0 | Ogni richiesta che ha superato il controllo della chiave, incluso il 429 |
| ETag | MD5 quotato del tempo di costruzione del grafo dei progetti | Ogni endpoint tranne /meta |
| Access-Control-Allow-Origin | * | Ogni risposta, incluso OPTIONS (che risponde 204) |
Involucro di risposta
Un successo è sempre HTTP 200 con tre chiavi di primo livello: ok è true, data contiene il payload, meta contiene il contesto. data è un oggetto sugli endpoint a singolo record e un array sugli endpoint di elenco.
GET /api/v1/meta, letteralmente:
{"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":"Punteggi & indici calcolati da olud.ai (olud.ai). Fatti contestuali da fonti pubbliche: GitHub, Hugging Face, OpenRouter, Artificial Analysis, PyPI, NPM, Docker Hub."}}meta.attribution è impostato su ogni risposta di successo, su ogni endpoint, e non può essere disattivato. Il resto di meta varia a seconda dell'endpoint: generated, total, limit, offset, sort, freshness, points. Leggi le sezioni degli endpoint per quali campi ottieni.
Un errore porta ok false, uno stato HTTP diverso da 200, e error.code più error.message. Non c'è un oggetto meta su un errore, quindi nessun campo di attribuzione. Testa ok prima di leggere data — non assumere la forma di successo.
GET /api/v1/projects senza chiave, letteralmente:
{"ok":false,"error":{"code":"missing_key","message":"Fornisci la tua chiave API tramite l'intestazione X-Api-Key (o ?key=). Ottienila su https://olud.ai/api/"}}Invia l'ETag come If-None-Match e riceverai 304 con un corpo vuoto quando il grafo non è stato ricostruito. Il grafo viene ricostruito ogni mattina, quindi il polling più frequente di così restituisce 304 tutto il giorno.
Endpoint: meta e progetti
GET /meta
Stato del grafo. L'unico endpoint senza chiave, e l'unico che non conta contro la tua quota. Restituisce il nome e la versione dell'API, generated (timestamp UTC dell'ultima costruzione del grafo), counts.projects, un elenco di endpoint e l'URL della documentazione. Nessun parametro. Se i file del grafo non sono leggibili, counts è nullo e la chiamata restituisce comunque 200 — usalo come controllo di salute e per decidere se un nuovo recupero ne vale la pena. Non invia ETag e intestazioni di limite di frequenza.
curl -s https://olud.ai/api/v1/meta
GET /project/{id}
Un progetto, record completo. Questa è la vista unificata: fatti di GitHub, il nostro punteggio di manutenzione, velocità delle stelle, rilasci, impulso di adozione.
- Identità: id, nome, proprietario, url, pagina (percorso della pagina del progetto sul sito), desc.
- Fatti di GitHub alla ultima costruzione: stelle, fork, lang, license, topics, created, pushed.
- salute: punteggio su 100, etichetta, perché, più i quattro componenti di cui è composto — attività (max 30), slancio (max 20), comunità (max 30), manutenzione (max 20) — pesi v2 dal 29 luglio 2026 — e controllato. Le etichette seguono il punteggio: 85+ Fiorente, 70+ Sano, 50+ Manutenuto, 30+ In rallentamento, sotto 30 A rischio. Un progetto senza commit in quattro settimane ha il punteggio limitato a 45, quindi i componenti possono sommarsi a più del punteggio. Non tutti i progetti sono valutati; prova per il campo.
- velocità: stelle_1d e stelle_7d, dalla nostra storia quotidiana delle stelle.
- rilasci: tag, data, livello, url — più recente per primo.
- strumento: slug, cat, pulse, docker_pulls, npm_month, pip_month, hn_hits, bsky_week, compare_pages. Presentato solo per progetti abbinati a uno strumento tracciato.
- classifica, tendenza (stabile, tranquilla o in accelerazione) e segnali (stars_accel, has_page).
| Parametro | Tipo | Predefinito | Comportamento |
|---|---|---|---|
| id | segmento del percorso, richiesto | nessuno | In minuscolo. Risolto in tre passaggi: slug esatto, poi l'indice proprietario/nome, poi proprietario-nome. Nessun segmento restituisce 400 missing_id; nessuna corrispondenza restituisce 404 not_found con un puntatore a /search. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/project/ollama-ollama
GET /projects
Filtra, ordina e pagina il catalogo. I filtri si applicano per primi, poi l'ordinamento, poi offset e limite.
| Parametro | Tipo / limiti | Predefinito | Comportamento |
|---|---|---|---|
| lang | stringa | nessun filtro | Corrispondenza esatta sulla lingua del progetto, non sensibile al maiuscolo. lang=rust mantiene solo Rust. |
| licenza | stringa | nessun filtro | Corrispondenza parziale sull'id della licenza, non sensibile al maiuscolo. license=gpl mantiene GPL-2.0 e AGPL-3.0. |
| verticale | stringa | nessun filtro | Corrispondenza esatta, non sensibile al maiuscolo. Valori presenti nel grafico: robotica, sicurezza, finanza, scienza, sanità, istruzione, legale. La maggior parte dei progetti non ne ha, e tutti scompaiono quando imposti questo. |
| health_min | intero | nessun filtro | Mantiene i progetti il cui health.score è maggiore o uguale al valore. I progetti non valutati contano come 0 e vengono esclusi. Un valore non numerico diventa 0, il che non filtra nulla. |
| ordina | stelle, salute, slancio o recente | stelle | Sempre in ordine decrescente. lo slancio legge velocity.stars_7d, recente legge la data dell'ultimo push. Un valore sconosciuto ordina per stelle ma viene restituito letteralmente in meta.sort — controlla meta.sort se l'ordine ti sorprende. |
| limite | intero 1-100 | 25 | I valori fuori intervallo vengono limitati ai limiti, i valori non numerici tornano a 25. Non viene sollevato alcun errore, quindi limit=500 ti dà silenziosamente 100. |
| offset | intero 0-100000 | 0 | Limitato allo stesso modo. |
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"
i dati sono un array di schede compatte: id, nome, url, desc, stelle, lang, licenza, salute (solo il punteggio), slancio (stars_7d), tendenza, verticale. I campi senza valore sono presenti e nulli. Per i componenti, le versioni e i dati di adozione, chiama /project/{id}. meta fornisce totale (corrispondenze dopo il filtraggio, prima della paginazione), limite, offset, ordinamento e generato.
Endpoint: ricerca, emergenti, alternative
GET /search
Cerca progetti per nome, descrizione e argomenti. Non cerca modelli o righe di Hugging Face — usa /models e /hf per quelli.
| Parametro | Tipo / limiti | Predefinito | Comportamento |
|---|---|---|---|
| q | stringa, obbligatoria | nessuno | Trimmata e in minuscolo. Vuota o contenente solo spazi restituisce 400 missing_query. |
| limite | intero 1-50 | 15 | Limitato ai limiti; non numerico torna a 15. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ "https://olud.ai/api/v1/search?q=ocr&limit=5"
Punteggio, così puoi prevedere l'ordine: corrispondenza esatta del nome 100, il nome inizia con q 60, il nome contiene q 40, la descrizione contiene q 15, e +20 quando q è esattamente uno degli argomenti del progetto. I pareggi si risolvono in base alle stelle. Qualsiasi punteggio di 0 viene scartato. La corrispondenza è una sottostringa semplice — niente stemming, nessuna tolleranza agli errori. Le schede hanno la stessa forma di /projects. meta fornisce il totale (tutte le corrispondenze, non la pagina) e q come normalizzato.
GET /emerging
L'indice di scoperta giornaliero: repository giovani con crescita sostenuta e, dove possiamo valutarli, buona salute. Classificati dal nostro Discovery Score. Le schede contengono due campi extra: segnali e rilevati, la data in cui il progetto è entrato per la prima volta nell'indice (null quando sconosciuto).
| Parametro | Tipo / limiti | Predefinito | Comportamento |
|---|---|---|---|
| limite | intero 1-100 | 25 | Limitato. Puoi ricevere meno righe di quelle richieste: gli id che non sono più nel grafo dei progetti vengono saltati. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ "https://olud.ai/api/v1/emerging?limit=10"
La freschezza dipende dal piano, e meta indica quale hai ottenuto. Pro e Business leggono l'indice di questa mattina e ottengono meta.freshness "realtime". Free e Dev leggono lo snapshot di ieri e ottengono "previous-day" più una meta.note. Due conseguenze da conoscere: su Free e Dev, meta.criteria è null, perché il testo dei criteri è memorizzato solo con l'indice attuale; e se il file dello snapshot di ieri è mancante, a Free e Dev viene servito l'indice attuale mentre meta.freshness continua a leggere "previous-day".
GET /alternatives/{product}
Alternative open-source a un prodotto commerciale. data restituisce nome, dominio, descrizione, pagina, categoria e un array di alternative i cui elementi contengono nome, repo, sito, licenza e descrizione. repo può essere una stringa vuota quando il progetto non ha un repository GitHub. meta fornisce generato.
| Parametro | Tipo | Predefinito | Comportamento |
|---|---|---|---|
| prodotto | segmento del percorso, richiesto | nessuno | In minuscolo. Chiave del prodotto esatta prima; in caso contrario, il primo prodotto la cui chiave o nome contiene la tua stringa, in ordine di file. Nessun segmento restituisce 400 missing_id; nessuna corrispondenza restituisce 404 not_found. Passa lo slug esatto — quello nell'URL della pagina /alternatives/<slug>.html — quando hai bisogno di un prodotto specifico, perché la corrispondenza approssimativa prende il primo colpo, non il migliore. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/alternatives/notion
Endpoint: modelli, hf, storia
GET /models
Catalogo dei modelli costruito da OpenRouter. Ogni riga: id, nome, fornitore, tier, ctx (finestra di contesto in token), price_in e price_out (dollari USA per milione di token, arrotondati a due decimali, 0 quando la fonte non riporta alcun prezzo), modalità (ad esempio text->text o text+image+file->text), strumenti (booleano) e rank. I campi senza valore vengono scartati dalla riga, quindi controlla la presenza piuttosto che assumere che price_in esista. meta fornisce totale, limite, offset, generato e fonte.
| Parametro | Tipo / limiti | Predefinito | Comportamento |
|---|---|---|---|
| fornitore | stringa | nessun filtro | Corrispondenza di sottostringa, non sensibile al maiuscolo. provider=mistral corrisponde a "Mistral AI". |
| tier | gratuito o a pagamento | nessun filtro | Corrispondenza esatta, non sensibile al maiuscolo sulla tua input. |
| limite | intero 1-200 | 50 | Limitato ai limiti; non numerico torna a 50. |
| offset | intero 0-100000 | 0 | Applicato alla lista unita. Le righe open-weight vengono prima, poi quelle proprietarie, ciascun blocco in ordine di rank. |
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: più scaricati e di tendenza. Nessun parametro. data ha tre array — top_llm (modelli di generazione di testo per download negli ultimi 30 giorni), top (tutti i compiti, limitato a 30 righe da questo endpoint) e trending (i salitori di oggi). Ogni riga: id, org, nome, compito, download (rolling 30 giorni), mi piace, licenza, gated, creato, aggiornato, trend (aumento dei mi piace sulle righe di tendenza, null altrove). meta fornisce generato e fonte.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/hf
GET /history/{slug}
Serie giornaliera per un progetto: stelle, salute e velocità a 7 giorni. Solo chiavi Pro e Business — il piano Business è quello venduto come Org. Qualsiasi altro piano ottiene 403 pro_required. data restituisce slug, giorni (date nel formato YYYY-MM-DD, più vecchie prima) e serie con tre array, stelle, salute e velocità_7d, allineati indice per indice con i giorni. Le voci individuali possono essere null quando il valore di un giorno era mancante. meta fornisce punti e finestra.
| Parametro | Tipo | Predefinito | Comportamento |
|---|---|---|---|
| slug | segmento del percorso, richiesto | nessuno | In minuscolo, e deve corrispondere a ^[a-z0-9][a-z0-9._-]*$ — lo stesso id di /project/{id}. Qualsiasi altra cosa restituisce 400 bad_slug. Uno slug valido senza nulla registrato restituisce 404 not_found. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/history/ollama-ollama
Quote giornaliere
Le richieste sono conteggiate per chiave, per giorno UTC. Il contatore si azzera a 00:00 UTC. Non ci sono limiti per secondo o per minuto nel codice.
| Piano sulla chiave | Richieste / giorno | Nome pubblico | Cosa cambia |
|---|---|---|---|
| gratuito | 500 | Gratuito | Tutto tranne /history. /emerging serve l'indice del giorno precedente. |
| sviluppo | 5000 | Sviluppatore | Stesso accesso di Free, limite più alto. |
| pro | 50000 | Pro | /history si apre. /emerging serve l'indice di questa mattina. |
| business | 500000 | Org | Stesso accesso di Pro, limite più alto. |
Cosa conta: ogni endpoint tranne /meta, un'unità per richiesta. Il contatore viene incrementato subito dopo il controllo della chiave e prima di qualsiasi altra cosa, quindi un 304, un 404 not_found, un 400 missing_query e un 503 graph_unavailable costano tutti un'unità. Solo 401 missing_key e 403 invalid_key non costano nulla, poiché non c'è una chiave valida da addebitare.
Un campo quota impostato sulla tua chiave sovrascrive il valore predefinito del piano. X-RateLimit-Limit è l'autorità sul tuo limite, non la tabella sopra.
Superato il limite, ogni richiesta restituisce 429 quota_exceeded fino al reset. La richiesta rifiutata non viene conteggiata, nulla viene messo in coda e nulla viene addebitato. Nessun header Retry-After viene inviato — il reset è a 00:00 UTC.
Come appare una richiesta rifiutata su una chiave gratuita:
HTTP/2 429
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 0
{"ok":false,"error":{"code":"quota_exceeded","message":"Quota giornaliera raggiunta (500 richieste/giorno sul piano \"free\"). Si resetta a 00:00 UTC."}}Codici di errore
Ogni errore restituisce error.code come stringa stabile. Fai branching su quello, non sul testo del messaggio, che contiene slugs e nomi di piano e cambia con la richiesta.
| HTTP | codice | Attiva | Cosa fare |
|---|---|---|---|
| 401 | missing_key | Nessun header X-Api-Key e nessun ?key=. Anche ciò che ottieni per un percorso sconosciuto quando non viene inviata alcuna chiave, poiché il controllo della chiave avviene prima del routing. | Invia la chiave. Un 401 su un percorso di cui sei sicuro che esista è un header mancante, non un percorso errato. |
| 403 | invalid_key | La chiave non è nel negozio, o il suo flag attivo è falso. Ruotare la tua chiave elimina la precedente. | Leggi la chiave attuale dalla tua pagina dell'account e aggiorna il chiamante. La rotazione interrompe ogni copia distribuita della vecchia chiave contemporaneamente. |
| 403 | pro_required | /history chiamato con una chiave gratuita o di sviluppo. | Leggi i valori di oggi da /project/{id} (salute, velocità), o passa a Pro. |
| 429 | quota_exceeded | Il contatore giornaliero ha raggiunto il tuo limite. | Smetti di chiamare fino a 00:00 UTC, o aumenta il piano. Leggi X-RateLimit-Limit per confermare il tuo vero limite. |
| 400 | missing_id | /project o /alternatives chiamato senza segmento di percorso. | Aggiungi il segmento. /v1/project da solo non è un elenco — usa /v1/projects. |
| 400 | query_mancante | /search con q vuoto o solo spazi bianchi. | Invia un q non vuoto. Nota che la richiesta è stata comunque conteggiata. |
| 400 | slug_non_valido | /history slug vuoto o contenente un carattere al di fuori di ^[a-z0-9][a-z0-9._-]*$. | Passa l'id esattamente come restituito da /projects o /search. |
| 404 | non_trovato | ID progetto sconosciuto, nessuna alternativa tracciata per quel prodotto, o nessuna storia memorizzata per quel slug. | Per un progetto, riprova tramite /search?q=. Per la storia, il progetto potrebbe essere entrato nel tracciamento troppo recentemente per avere punti. |
| 404 | endpoint_sconosciuto | Chiave valida, percorso non presente nel router. | Controlla l'ortografia. /meta elenca sei endpoint e omette /hf e /history, che esistono entrambi. |
| 503 | grafico_non_disponibile | Un file grafico è mancante o illeggibile, il che accade mentre la build mattutina lo scrive. | Riprova tra un minuto. La tua chiave è valida; non ruotarla. |
| 503 | non_pronto | /hf prima che la scansione mattutina di Hugging Face abbia prodotto il suo file. | Riprova più tardi durante la giornata. Altri endpoint non sono influenzati. |
| 304 | nessun corpo | If-None-Match ha corrisposto all'ETag corrente. | Servi la tua copia cache. Ricorda che ha consumato una richiesta dal tuo quota. |
Politica di ripetizione che corrisponde al codice: ripeti su 503 dopo un minuto, e su 429 solo dopo il reset UTC. Non ripetere mai un 400, 403 o 404 invariato — la risposta non cambierà e ogni tentativo costa una richiesta.
Webhook
Creazione di un webhook
Un webhook è un POST firmato al tuo endpoint per ogni evento. Scegli gli eventi, i repository e la forma del corpo. I webhook necessitano di un piano a pagamento: con una chiave gratuita, la creazione restituisce 403 paid_feature e la pagina dell'account mostra un pannello bloccato invece del modulo.
| Piano riportato dall'API | Webhook consentiti |
|---|---|
| gratuito | 0 |
| sviluppo | 3 |
| pro | 10 |
| business | 50 |
Un nome di piano che non riconosciamo torna a 1. Una volta raggiunto il tuo conteggio, la creazione restituisce 429 limit_reached — elimina uno o cambia piano.
Dal tuo account
- Accedi e apri /account.html. La scheda Webhooks appare una volta che la tua chiave API è stata caricata, e rimane nascosta fino ad allora.
- Incolla il tuo endpoint nel campo URL. La pagina rifiuta qualsiasi cosa che non inizi con https://.
- Seleziona gli eventi. release e health sono selezionati per te; license e new_project non lo sono.
- Lascia il campo repos vuoto per ricevere tutto, oppure digita le voci owner/name separate da virgole.
- Scegli la destinazione nel selettore: il tuo stesso endpoint (JSON firmato raw), Slack o Discord.
- Premi Crea. Il segreto appare una sola volta, in una casella verde. Copialo prima di lasciare la pagina — non verrà mostrato di nuovo.
- Ogni riga del webhook porta quindi un pulsante Invia ping di test e un link Elimina. Eliminare ferma immediatamente le consegne.
Dall'API — crea. La tua chiave API è quella nel tuo account, inviata come X-Api-Key (o ?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"
}'Il blocco dati della risposta. Ogni risposta porta anche un blocco meta con la nostra linea di attribuzione. Il segreto è qui e da nessun'altra parte.
{
"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": "Conserva questo segreto ora — viene mostrato solo una volta. Verifica ogni consegna: X-OSAI-Signature == \"sha256=\" + HMAC_SHA256(raw_body, secret). Testalo: POST {\"ping\":\"whk_9b41c7e2f0a5d3861c4e\"}."
}Cosa deve soddisfare l'URL
- Schema https. http è rifiutato.
- Un host deve essere presente.
- Se scrivi una porta, deve essere 443. https://your-app.example:8443/hook è rifiutato.
- L'host non può essere localhost e non può terminare con .local o .internal.
- Risolviamo l'host. Se un indirizzo restituito si trova in un intervallo privato o riservato, l'URL è rifiutato.
- Non chiamiamo il tuo endpoint durante la creazione. Un host che non riesce a risolversi supera questo controllo e fallisce più tardi, durante la consegna. Invia un ping di prova per scoprirlo.
- Un URL errato restituisce 400 bad_url.
Quali repo accetta
- Il predefinito è ["*"] — ogni repository che tracciamo.
- Le voci vengono troncate e convertite in minuscolo, quindi il confronto è insensibile al maiuscolo.
- Un'entrata deve apparire come owner/name: owner inizia con una lettera o un numero, seguito da lettere, numeri, punto, underscore, trattino.
- Le voci che non corrispondono vengono eliminate senza parole. Se nulla sopravvive, ricevi 400 bad_repos.
- "*" ovunque nella lista sostituisce l'intera lista. ["acme/one", "*"] è memorizzato come ["*"].
- Più di 100 voci restituisce 400 too_many_repos. Usa "*" oltre quel punto.
I quattro eventi
Ogni evento porta un repository. Un evento è un POST — gli eventi non vengono mai raggruppati insieme. Il nome si trova nell'intestazione X-OSAI-Event e nel campo evento del corpo.
| evento | Si attiva quando | Chiavi all'interno dei dati |
|---|---|---|
| salute | l'etichetta di salute di un repository cambia | da, a, stelle, pagina |
| rilascio | un nuovo tag di rilascio appare per un repository | tag, nome, url |
| licenza | la licenza che possediamo per un repository cambia | da, a |
| nuovo_progetto | un repository entra nella directory | stelle, pagina |
salute
{
"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"
}da e a sono etichette, non numeri. Le sei etichette che pubblichiamo: Thriving, Healthy, Maintained, Slowing down, At risk, Archived. stelle è il conteggio delle stelle che possediamo per il repository. Nessun evento si attiva la prima volta che un repository ottiene un'etichetta — un cambiamento ha bisogno di un valore precedente con cui confrontarsi.
rilascio
{
"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 è il tag git. name è il nome visualizzato del progetto nella nostra directory, non il titolo del rilascio. url punta sempre alla pagina dei rilasci del repository, mai a un rilascio specifico — costruisci tu stesso l'URL del tag se ne hai bisogno.
licenza
da e a sono le stringhe di licenza che possediamo. Questo evento non ha una chiave pagina.
{
"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"
}nuovo_progetto
{
"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"
}Al massimo 25 eventi new_project per esecuzione, conteggi di stelle più alti per primi. In una prima esecuzione, o quando più di 200 repository appaiono contemporaneamente, vengono registrati e nessuno viene inviato — quella protezione impedisce a un re-import di inondarti.
Corpo e intestazioni
Con formato json, il corpo ha cinque chiavi, in quest'ordine. Nient'altro viene aggiunto.
{
"webhook_id": "whk_9b41c7e2f0a5d3861c4e",
"event": "release",
"repo": "acme/inference-server",
"data": { },
"sent_at": "2026-07-27T05:41:12+00:00"
}webhook_id è l'id che hai creato: whk_ più 20 caratteri esadecimali. repo è il proprietario/nome in minuscolo, ed è nullo solo su un ping di prova. data è un oggetto, vuoto per un evento che non porta campi. sent_at è UTC, ISO 8601 con un offset.
| Intestazione | Valore |
|---|---|
| Content-Type | application/json |
| User-Agent | olud.ai-Webhooks/1.0 |
| X-OSAI-Event | salute, rilascio, licenza, nuovo_progetto o ping |
| X-OSAI-Delivery | 16 caratteri esadecimali, freschi per ogni POST |
| X-OSAI-Signature | sha256= seguito dall'HMAC-SHA256 del corpo |
Quei cinque sono l'intero insieme. X-OSAI-Delivery non è memorizzato da parte nostra — usalo per individuare un duplicato nel tuo log.
Un ping di test, inviato tramite POST {"ping":"whk_…"}. Stesse intestazioni, stessa firma, X-OSAI-Event: ping.
{
"webhook_id": "whk_9b41c7e2f0a5d3861c4e",
"event": "ping",
"repo": null,
"data": {
"message": "Funziona. Gli eventi reali arrivano dopo ogni scansione mattutina."
},
"sent_at": "2026-07-27T05:41:12+00:00"
}Verifica della firma
Ricalcola l'HMAC-SHA256 del corpo grezzo con il tuo segreto, prefisso con sha256= e confrontalo con X-OSAI-Signature. Il segreto è la stringa whs_ dalla risposta di creazione: whs_ più 48 caratteri esadecimali.
PHP
<?php
$raw = file_get_contents('php://input'); // byte grezzi, prima di qualsiasi parsing
$sent = $_SERVER['HTTP_X_OSAI_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $raw, $SECRET);
if (!hash_equals($expect, $sent)) { // tempo costante
http_response_code(401);
exit;
}
$event = json_decode($raw, true); // analizza solo dopo il controllo
http_response_code(200);Node, con Express
const crypto = require('crypto');
const express = require('express');
const app = express();
// express.raw mantiene i byte. express.json() li distruggerebbe.
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 genera un RangeError su buffer di lunghezza diversa,
// quindi controlla prima la lunghezza, poi confronta in tempo costante.
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, con 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() # byte, prima di qualsiasi 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): # tempo costante
abort(401)
event = json.loads(raw)
return "", 200Confronta con hash_equals, crypto.timingSafeEqual o hmac.compare_digest. Mai con == o ===. Un confronto di stringhe semplice si ferma al primo byte che differisce, quindi il tempo che impiega indica a un attaccante quanti byte iniziali hanno indovinato correttamente. Ripeti la misurazione e recuperano la firma byte per byte, poi ti inviano eventi falsificati. Le tre funzioni sopra leggono ogni byte, qualunque sia l'input.
- Hash i byte grezzi, prima di qualsiasi parsing. Un corpo che un framework ha analizzato e ri-serializzato hash a qualcos'altro, e ogni consegna sembrerà non valida.
- Il formato json esegue l'escape dei caratteri non ASCII come \uXXXX. La ri-serializzazione perde anche quello.
- Un'intestazione mancante arriva come una stringa vuota. Tutti e tre gli esempi la rifiutano invece di andare in crash.
- Il segreto viene mostrato una sola volta, alla creazione. Se lo perdi, elimina il webhook e creane un altro — ottieni un nuovo id e un nuovo segreto.
- Mantieni un segreto per webhook. Due webhook non condividono mai uno.
json, slack, discord
il formato è scelto alla creazione e predefinito su json. Non c'è un endpoint di aggiornamento: per cambiarlo, elimina il webhook e creane un altro. GET riporta il formato di ogni webhook; la risposta di creazione non lo riecheggia. Un valore al di fuori dei tre risponde 400 bad_format.
| formato | Cosa postiamo | URL da incollare |
|---|---|---|
| json | il payload sopra, invariato | il tuo endpoint |
| slack | testo, più un blocco di sezione mrkdwn | un URL di webhook in entrata di Slack |
| discord | un embed: titolo, url, descrizione, colore, piè di pagina | un URL di webhook di canale Discord |
slack
Un evento di rilascio, postato su Slack
{
"text": "🚀 acme/inference-server rilasciato v0.6.2 — Acme Inference Server",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🚀 *<https://github.com/acme/inference-server/releases|acme/inference-server rilasciato v0.6.2>*\nAcme Inference Server"
}
}
]
}Ottieni l'URL da Slack: aggiungi un'app al workspace, attiva Incoming Webhooks, aggiungi uno per il canale che desideri, copia l'URL https://hooks.slack.com/services/… e incollalo nel campo url. Il testo è sempre compilato — è ciò che Slack mostra nella notifica e nei client che non rendono i blocchi.
discord
Un evento di licenza, postato su Discord
{
"embeds": [
{
"title": "🔑 acme/inference-server ha cambiato licenza",
"url": "https://github.com/acme/inference-server",
"description": "Apache-2.0 → BSL-1.1",
"color": 14427686,
"footer": { "text": "olud.ai" }
}
]
}Ottieni l'URL da Discord: impostazioni del canale, Integrazioni, Webhook, Nuovo Webhook, Copia URL Webhook — https://discord.com/api/webhooks/… — e incollalo nel campo url. Il colore è un intero decimale: 14427686 (#DC2626) per la licenza, 6514417 (#6366F1) per il rilascio, new_project e ping.
Entrambi gli host superano il controllo dell'URL. Quando un evento di licenza non porta a nessuna pagina, il link torna a https://github.com/owner/name; health e new_project rimandano alla pagina del progetto su olud.ai; il rilascio rimanda alla pagina dei rilasci del repository.
Consegna, errore, disabilitazione
Le consegne si basano sull'allerta pass, send-alerts.php — la stessa rilevazione che produce le email dei membri. La spedizione avviene subito dopo la rilevazione, nello stesso processo. Non c'è un programma separato e nessuna coda. L'API descrive la cadenza come "giornaliera, dopo la scansione mattutina".
- Un POST per evento, per webhook corrispondente. Un webhook riceve un evento quando il nome dell'evento è nella sua lista di eventi, e quando il repository è nella sua lista di repos o repos è ["*"]
- Un successo è HTTP 200 a 299, entro il timeout di 6 secondi. Un 4xx, un 5xx, un timeout, un errore DNS o TLS contano tutti come fallimenti.
- Un successo riporta fails a 0 e aggiunge 1 a delivered.
- Un fallimento aggiunge 1 a fails. Non c'è ripetizione. L'evento non viene inviato di nuovo — la prossima consegna è il prossimo cambiamento che rileviamo.
- Dopo 10 fallimenti consecutivi il webhook viene disabilitato, timbrato con l'ora UTC di quel momento, e gli eventi rimanenti di quel ciclo vengono saltati per esso.
- Un webhook disabilitato rimane nella tua lista, contrassegnato come disabilitato. Non c'è chiamata per riabilitarlo: eliminalo e creane un altro, con un nuovo id e un nuovo segreto.
Lettura dello stato
curl -sS https://olud.ai/api/webhooks.php -H "X-Api-Key: $OSAI_KEY"
La risposta, meta inclusa
{
"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 è il conteggio totale dei POST riusciti. fails è la serie attuale consecutiva, quindi torna a 0 al prossimo successo. disabled è null, o il timestamp in cui ci siamo fermati. I segreti non vengono mai restituiti da GET.
Quattro modi in cui le consegne si fermano
- Il tuo endpoint continua a fallire. Dieci fallimenti di fila e il webhook viene disabilitato. Risolvi l'endpoint, elimina il webhook, creane uno nuovo, pingalo.
- Il tuo piano torna a gratuito — una cancellazione. Il webhook viene mantenuto e saltato ad ogni spedizione, silenziosamente. Cambiare piano di nuovo lo riattiva, stesso id, stesso segreto.
- Ruoti la tua chiave API nel tuo account. La vecchia chiave lascia il keystore mentre il webhook continua a puntare ad essa: esce dalla tua lista, non può più essere pingato o eliminato, e ogni spedizione lo salta come una chiave gratuita. Elimina i tuoi webhooks prima di ruotare la chiave, poi ricreali con la nuova.
- Lo elimini. Le consegne si fermano immediatamente.
Test senza aspettare
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"}'Entrambi i risultati
HTTP/1.1 200
{"ok":true,"data":{"ping":"delivered","http":200}}
HTTP/1.1 502
{"ok":false,"error":{"code":"ping_failed",
"message":"Il tuo endpoint ha risposto HTTP 500 — ci si aspetta un 2xx."}}Il ping viene inviato nel formato del webhook, firmato allo stesso modo, con evento ping e repo null. Non muove né delivered né fails, quindi un ping fallito non ti spinge mai verso la soglia di disabilitazione. Funziona anche su un piano gratuito quando il webhook esiste già, mentre le consegne programmate non lo fanno.
HTTP 0 in un messaggio ping_failed significa che non abbiamo ricevuto alcuna risposta: l'host non si è risolto, TLS non è completato, o i 6 secondi sono scaduti.
Codici di errore
Ogni fallimento torna nella stessa forma, con uno stato HTTP corrispondente.
{
"ok": false,
"error": {
"code": "bad_url",
"message": "L'URL deve essere https, raggiungibile pubblicamente, porta standard (niente localhost o intervalli privati)."
}
}| HTTP | codice | Quando |
|---|---|---|
| 400 | bad_json | Il corpo non è un oggetto JSON. |
| 400 | bad_url | L'URL ha fallito il controllo: non https, una porta diversa da 443, localhost, .local, .internal, o un indirizzo in un intervallo privato o riservato. |
| 400 | bad_events | Dopo il filtraggio, gli eventi non contenevano nessuno di health, release, license, new_project. |
| 400 | bad_repos | Nessuna voce era "*" o un proprietario/nome valido. |
| 400 | too_many_repos | Più di 100 repository su un webhook. |
| 400 | bad_format | il formato non era json, slack o discord. |
| 401 | missing_key | Nessun header X-Api-Key e nessun parametro ?key=. |
| 403 | chiave_non_valida | La chiave non è nel nostro archivio chiavi. Una chiave ruotata o revocata finisce qui. |
| 403 | funzione_a_pagamento | Il tuo piano consente 0 webhook. |
| 404 | non_trovato | L'id in ping o in ?id= non è associato alla tua chiave. |
| 405 | metodo_non_consentito | Un metodo diverso da GET, POST o DELETE. |
| 429 | limite_raggiunto | Hai già il numero massimo di webhook consentito dal tuo piano. |
| 500 | scrittura_archivio_fallita | Non siamo riusciti a scrivere la modifica su disco. Riprova. |
| 502 | ping_fallito | Il tuo endpoint ha risposto al ping di test con qualcosa di diverso da un 2xx. |
Due punti pratici. La gestione dei webhook non consuma la tua quota giornaliera di richieste API — questo endpoint non tocca mai il contatore e non invia header X-RateLimit. E DELETE è assente dall'elenco dei metodi consentiti CORS, quindi una chiamata cross-origin da un browser fallisce al preflight; elimina lato server, o dalla pagina dell'account, che è same-origin.
Automazioni
n8n: leggere e ricevere
Due nodi coprono entrambe le direzioni. Un nodo HTTP Request legge i nostri dati. Un nodo Webhook riceve i nostri eventi. Nulla da installare dalla lista della community.
Leggi il catalogo
Ogni endpoint risponde a GET su https://olud.ai/api/v1, con la tua chiave nell'header X-Api-Key. /meta è l'unico endpoint che risponde senza una chiave e senza toccare la tua quota — usalo come primo nodo mentre testi, perché prova l'URL e il percorso di rete prima di spendere una chiamata.
Ricevi gli eventi
Aggiungi un nodo Webhook, metodo POST, e copia il suo URL di produzione. Registra quell'URL nel tuo account (sezione Webhook), o POSTalo su https://olud.ai/api/webhooks.php con la tua chiave. Quattro eventi tra cui scegliere: release, health, license, new_project. Le consegne vengono effettuate una volta al giorno, nella stessa passata che invia gli avvisi via email — non appena accade qualcosa.
Un corpo di consegna, con formato json (il predefinito):
Cosa si trova in data dipende dall'evento:
- release — tag, nome, url (la pagina delle release del repository)
- health — from, to, stars, page
- license — from, to
- new_project — stars, page
- ping — message (solo consegne di test)
In un evento di salute, from e to sono etichette, non numeri: Thriving (punteggio 85 e oltre), Healthy (70+), Maintained (50+), Slowing down (30+), At risk (sotto 30). Confronta il testo nel tuo nodo IF, non interi.
Ogni consegna porta tre header: X-OSAI-Event, X-OSAI-Delivery (un id unico per consegna, usalo per eliminare i duplicati) e X-OSAI-Signature, della forma sha256=<HMAC-SHA256 del corpo con il tuo segreto webhook>. L'HMAC copre i byte esatti che abbiamo inviato, quindi se vuoi verificarlo, attiva l'opzione raw-body del nodo Webhook. Un corpo che è stato analizzato e ri-serializzato non corrisponde mai.
Incolla questo sulla tela. I due nodi sono lasciati disconnessi di proposito — ognuno è il proprio punto di partenza:
Poi metti la tua chiave nel campo header del nodo HTTP e attiva il workflow — l'URL di produzione di un nodo Webhook ascolta solo mentre il workflow è attivo.
Quando non arriva nulla
- Registrare l'URL restituisce 400 bad_url: l'endpoint deve essere https, sulla porta 443, su un host risolvibile pubblicamente. Un n8n auto-ospitato su http, su localhost, su un nome *.local o su un intervallo IP privato viene rifiutato.
- Registrare restituisce 403 paid_feature: i webhook iniziano dal piano Dev — 3 endpoint su Dev, 10 su Pro, 50 su Org. Una chiave Free non può crearne uno.
- Registrare restituisce 429 limit_reached: hai già il numero massimo di webhook consentito dal tuo piano. Elimina prima uno.
- Non aspettare domani per scoprirlo: POST {"ping":"whk_…"} a /api/webhooks.php e l'evento di test parte immediatamente, nella stessa forma di uno reale. Se il tuo endpoint non risponde 2xx ricevi 502 ping_failed con il codice che ha restituito.
- Dieci risposte consecutive non-2xx disabilitano il webhook, silenziosamente. Un successo ripristina il contatore. Un webhook disabilitato torna solo ricreandolo. Il timeout di consegna è di 6 secondi, quindi un nodo che risponde lentamente conta come un fallimento.
- Il webhook è stato creato ma nulla viene consegnato e non ci sono errori: controlla il piano sulla chiave che lo possiede. Se è tornato a Free, le consegne vengono saltate mentre l'endpoint rimane registrato.
- Il segreto viene mostrato una sola volta, alla creazione. Non c'è modo di leggerlo di nuovo — elimina e ricrea.
Zapier e Make
Stesse due direzioni, stessa chiave, nessuna app da trovare nelle loro directory.
Ricevi: cattura il hook
In Zapier: Webhooks by Zapier, attiva Catch Hook. In Make: il modulo Webhooks, Custom webhook. Entrambi ti forniscono un URL https sul loro dominio, che supera il nostro controllo. Incollalo nel tuo account come endpoint webhook, scegli i tuoi eventi, poi inviati un ping e conferma che lo Zap o lo scenario lo riceve prima di costruire i passaggi dietro di esso.
Se intendi verificare X-OSAI-Signature, hai bisogno del corpo raw e delle intestazioni. In Zapier ciò significa Catch Raw Hook piuttosto che Catch Hook. In Make, attiva l'impostazione del webhook che mantiene le intestazioni della richiesta. La nostra firma è un HMAC sui byte esatti del corpo, quindi un passaggio che analizza il JSON prima di vederlo rende impossibile il confronto.
Leggi: un passaggio HTTP
Azione GET dei Webhooks di Zapier, o modulo HTTP di Make. Un passaggio, un'intestazione:
Un successo è {"ok":true,"data":[…],"meta":{…}}. Un errore è {"ok":false,"error":{"code":"…","message":"…"}} con uno stato HTTP corrispondente. Testa ok prima di mappare i campi: un corpo di errore non ha dati, e una mappatura che legge data[0] su un errore produce silenziosamente valori vuoti a valle.
- 401 missing_key — l'intestazione non è arrivata. Controlla che il passaggio la invii ad ogni chiamata, non solo alla prima.
- 403 invalid_key — chiave sconosciuta o revocata.
- 429 quota_exceeded — quota giornaliera raggiunta, si resetta a 00:00 UTC. Le risposte di successo portano X-RateLimit-Limit e X-RateLimit-Remaining, così puoi vederlo arrivare.
- 503 graph_unavailable — il grafo è in fase di ricostruzione. Riprova tra un minuto; questo è l'unico errore che vale un tentativo automatico.
- 403 pro_required — /history/{slug} è solo per Pro e Org.
- 404 not_found — nessun progetto o prodotto del genere. Il messaggio porta un suggerimento /search che puoi seguire.
GitHub Action: monitoraggio delle dipendenze
L'azione legge i manifesti delle dipendenze del repository controllato, ci chiede la licenza e il record di manutenzione di ogni dipendenza, e pubblica un commento sulla richiesta di pull elencando ciò che necessita di una decisione. Riscrive lo stesso commento ad ogni esecuzione — lo trova di nuovo attraverso un marcatore nascosto — quindi una lunga richiesta di pull non si riempie di duplicati.
L'intero flusso di lavoro, con ogni input al suo valore predefinito. Solo api-key è richiesto:
actions/checkout deve venire per primo: l'azione legge i file dalla directory di lavoro, e solo alla radice a meno che tu non imposti i percorsi.
| Input | Predefinito | Cosa fa |
|---|---|---|
| api-key | richiesto | Inviato come X-Api-Key. Una ricerca per ogni dipendenza distinta. Una chiave gratuita funziona. |
| github-token | ${{ github.token }} | Pubblica il commento. Necessita di pull-requests: scrivi sul lavoro. |
| health-floor | 45 | Segna una dipendenza che ottiene un punteggio inferiore a questo su 100. 0 disattiva il controllo della salute e mantiene solo le licenze. Leggi il limite noto qui sotto. |
| licences | AGPL-3.0,BSL-1.1,SSPL-1.0,Elastic-2.0,BUSL-1.1,NOASSERTION | Separati da virgole, abbinati senza distinzione di maiuscole e minuscole rispetto alla licenza che possediamo. Impostarlo sostituisce l'elenco, non lo aggiunge. |
| fail-on-finding | false | Fallisci il controllo invece di commentare solo. |
| paths | vuoto | Manifesti separati da virgole da leggere. Vuoto significa: cerca i quattro nomi noti alla radice. |
NOASSERTION è nell'elenco predefinito per scelta: è ciò che possediamo quando il repository non porta un file di licenza che una macchina può riconoscere, il che è una decisione da prendere piuttosto che un dettaglio. L'azione espone un output, findings — il numero di dipendenze contrassegnate, scritto anche quando è 0.
Cosa legge
- package.json — le chiavi delle dipendenze e devDependencies. peerDependencies e optionalDependencies non vengono letti.
- requirements.txt — un nome per riga, commenti rimossi, righe che iniziano con - eliminate. Quindi -r other-requirements.txt non viene seguito e -e . viene ignorato.
- pyproject.toml — solo chiavi il cui valore inizia con una virgoletta o una parentesi graffa, che è lo stile Poetry: fastapi = "^0.110". Un blocco PEP 621, dependencies = ["fastapi>=0.110"], non produce nulla.
- go.mod — righe indentate della forma nome vX.
i percorsi possono puntare ovunque nell'albero, ad esempio apps/api/requirements.txt, ma il nome base del file deve essere uno di quei quattro. Un file chiamato requirements-dev.txt non ha un parser: viene saltato senza un messaggio.
Come fallisce
- Nessun manifesto alla radice, o nessun passo di checkout: "Nessun manifesto di dipendenza trovato — nulla da controllare." le scoperte sono 0 e il lavoro è verde. Una corsa verde non significa un albero pulito — leggi quella riga.
- L'evento non ha pull request, ad esempio su push: "Nessuna pull request nel contesto — commento saltato." Le scoperte sono ancora nel log.
- pull-requests: scrivi mancante: "Impossibile pubblicare il commento (HTTP 403)." Il lavoro rimane verde e il commento non appare mai. Guarda il log, non la pull request.
- Quota API giornaliera raggiunta a metà esecuzione: ::warning::Quota API giornaliera raggiunta — esecuzione parziale. Smette di cercare cose e commenta su ciò che è riuscito a risolvere. Una chiamata per ogni dipendenza distinta, quindi 300 dipendenze spendono 300 delle 500 chiamate giornaliere di una chiave Free.
- Un manifesto che non verrà analizzato: "⚠ <file> illeggibile (…) — saltato". Gli altri manifesti continuano a funzionare.
- Il passo composito esegue node dal PATH e utilizza fetch nativo. I runner ospitati da GitHub già portano Node 20. Un runner auto-ospitato ha bisogno di actions/setup-node con Node 20 o successivo.
- licenze: '' con health-floor: '0' produce zero scoperte per sempre. Quella combinazione disattiva entrambi i controlli.
Cosa manca all'Action
Un nome di pacchetto non è un nome di repository. Ogni nome passa attraverso /api/v1/search?q=<name>&limit=5 e l'azione mantiene un risultato solo quando il nome del progetto è uguale al nome del pacchetto, ignorando il maiuscolo. Qualsiasi altra cosa viene scartata in silenzio — nessuna riga di commento, nessun avviso. Indovinare il risultato più vicino solleverebbe allarmi sul progetto sbagliato, il che è peggio che rimanere in silenzio, ma significa che il controllo copre meno del tuo albero delle dipendenze.
- I pacchetti npm con ambito perdono il loro ambito prima della ricerca: @types/node viene cercato come node, il che può corrispondere a un repository non correlato con quel nome. Questo è l'unico caso in cui il progetto sbagliato può finire nel commento.
- I moduli Go mantengono il loro percorso: github.com/gin-gonic/gin diventa gin-gonic/gin, che non è mai uguale a un nome di repository (gin). Le dipendenze di go.mod non si risolvono.
- Un pacchetto il cui repository ha un nome diverso — il caso comune in Python — non si risolve mai.
- Solo i primi cinque risultati vengono esaminati e solo la prima corrispondenza esatta viene utilizzata. Quando due repository condividono un nome, quello con più stelle vince.
La riga da leggere nel log è: Risolto N di M dipendenze · K contrassegnate. Se N è molto al di sotto di M, il controllo ha esaminato solo una frazione del tuo albero. Niente nel commento della pull request lo dice.
Specifica OpenAPI
Un file descrive l'API di lettura: https://olud.ai/openapi.json. OpenAPI 3.0.3, un server (https://olud.ai/api/v1), nove operazioni GET, uno schema di sicurezza — una chiave API nell'intestazione X-Api-Key. Non scrivi un'integrazione; incolli un indirizzo.
- /meta — data di costruzione e conteggio dei progetti. Dichiarato senza sicurezza: è il controllo della salute.
- /search — q richiesto, limite da 1 a 50, predefinito 15.
- /projects — lang, license, vertical, health_min da 0 a 100, ordina in stars|health|momentum|recent (predefinito stars), limite da 1 a 100 (predefinito 25), offset da 0 a 100000 (predefinito 0).
- /project/{id} — il record completo unito, inclusa la salute con i suoi componenti.
- /emerging — limite da 1 a 100, predefinito 25.
- /alternatives/{product} — alternative open-source a un prodotto commerciale.
- /models — provider, tier, limite da 1 a 200 (predefinito 50), offset.
- /hf — più scaricati e in tendenza su Hugging Face.
- /history/{slug} — 90 giorni di stelle, salute e momentum. Pro e Org.
Importalo
- Custom GPT — Configura, poi Azioni, poi Importa da URL. Autenticazione: Chiave API, intestazione personalizzata, nome X-Api-Key.
- Dify, Flowise, Open WebUI — aggiungi uno strumento da uno schema OpenAPI, incolla l'URL, scegli le operazioni che vuoi esporre, aggiungi la stessa intestazione.
- Postman, Insomnia — Importa, poi Collega. Ottieni le nove richieste, documentate. Imposta X-Api-Key una volta a livello di collezione in modo che ogni richiesta la erediti.
- Generatori di codice — qualsiasi generatore OpenAPI produce un client tipizzato, TypeScript, Python o Go, solo da questo file.
Qualunque sia lo strumento, ci sono solo due cose da impostare: l'URL del file e la chiave come intestazione chiamata X-Api-Key. Se un'importazione mostra nove operazioni ma ogni chiamata risponde 401, l'intestazione non viene inviata — quella è la prima cosa da controllare.
Le quote sono per chiave e per giorno, ripristinate a 00:00 UTC: 500 richieste su Free, 5.000 su Dev, 50.000 su Pro, 500.000 su Org. Vengono conteggiate separatamente dal server MCP, quindi un assistente che fa domande nel tuo editor non consuma mai questo budget.
Quattro feed RSS
Nessuna chiave, nessuna registrazione, nessun account. feed.php accetta esattamente quattro tipi: notizie, blog, rilasci, emergenti.
| URL | Cosa porta | Da dove proviene |
|---|---|---|
| /feed.php | Fino a 30 nuovi progetti (★stelle · proprietario/nome), i nuovi modelli del giorno (Nuovo modello: nome (fornitore), con la finestra di contesto nella descrizione), nuovi Hugging Face Spaces (Nuovo Space: nome dell'autore, con il conteggio dei like), e il documento del giorno. | today-data.json, ricostruito ogni ora |
| /feed.php?type=releases | Fino a 60 versioni rilasciate dai progetti che seguiamo: titolo progetto + tag, link al rilascio, guid proprietario/nome@tag. | releases-data.json, ricostruito ogni ora |
| /feed.php?type=emerging | Fino a 40 progetti emergenti — sani, in accelerazione, ancora poco conosciuti. Descrizione: ★stelle · salute N/100 · +N stelle questa settimana. | il grafico, ricostruito ogni mattina |
| /feed.php?type=blog | Articoli sotto /blog/<slug>/, /blog/alternatives/ e /reports/. Titolo e descrizione sono il titolo e la meta descrizione della pagina; la data è il tempo di modifica del file. | lettura dal disco, quindi un nuovo articolo appare da solo |
Tutti e quattro rispondono a RSS 2.0 come application/rss+xml, in inglese, l'elemento più recente per primo, con Cache-Control: public, max-age=1800. Ovvero 30 minuti: un lettore che interroga più spesso ottiene la copia cache, che è la stessa risposta servita più velocemente.
I guid sono stabili e non sempre il link, che è ciò che desideri quando de-duplici in un'automazione: i rilasci usano proprietario/nome@tag, i progetti emergenti usano emerging:<id>, il documento del giorno usa il suo URL più la data, tutto il resto usa il suo link.
Punta Slack, Teams o Feedly su di essi per leggere, o usa il trigger RSS in n8n, Zapier e Make quando un programma si adatta meglio a te rispetto a un webhook — questo è anche il modo per ottenere rilasci senza un piano a pagamento. Se un file sorgente non è stato ricostruito, il feed risponde comunque 200 con un canale vuoto piuttosto che un errore, quindi un'automazione che improvvisamente non riceve nulla non è necessariamente rotta.