A instalação, passo a passo
Cada trecho desta página está pronto a colar. Os números — quotas, limites, valores por omissão, nomes de campos — são lidos do código em execução: esta página e o servidor dizem o mesmo.
Para começar
Obtenha sua chave
Tudo aqui, exceto os feeds RSS, usa uma chave de API. Abra sua conta, seção Developer API, e copie-a. O plano gratuito não precisa de cartão.
A chave carrega seu plano, e seu plano carrega seus volumes diários. Dois contadores funcionam lado a lado: um para a API REST, um para o servidor MCP. Um editor que faz algumas perguntas nunca consome seu orçamento de API.
De que preciso?
| Você quer… | Usar |
|---|---|
| Fazer perguntas ao seu editor | Servidor MCP |
| Consultar os dados do seu próprio código | REST API |
| Ser informado quando algo muda | Webhooks |
| Integrá-lo ao n8n, Zapier ou Make | Automatizações |
| Verificar dependências em cada pull request | GitHub Action |
| Acompanhar em um leitor, sem chave | Feeds RSS |
Servidor MCP
O servidor MCP, em resumo
MCP (Modelo de Contexto de Protocolo) é uma maneira padrão para um assistente de IA chamar um serviço externo. Você cola um endereço no seu cliente, e o assistente ganha cinco ferramentas que pode usar enquanto trabalha. Essas ferramentas consultam o catálogo olud.ai: projetos de IA de código aberto, modelos de IA com seus preços e alternativas de código aberto a produtos comerciais.
| Fato | Valor |
|---|---|
| Endpoint | https://olud.ai/mcp.php |
| Método | POST, JSON-RPC 2.0. GET retorna 405 (veja abaixo) |
| Transporte | HTTP transmissível, um endpoint, sem fluxo SSE |
| Versão do protocolo | 2025-06-18. Se o cliente solicitar 2025-03-26 ou 2024-11-05, o servidor responde nessa versão |
| Versão do servidor | 1.0.0 |
| Sessão | Nenhuma. Nenhum Mcp-Session-Id para manter, nada para expirar, nada para reconectar |
| Capacidades | apenas ferramentas. resources/list, resources/templates/list e prompts/list respondem com listas vazias em vez de um erro |
| Autenticação | Cabeçalho de solicitação X-Api-Key. Opcional |
| Agrupamento | Não suportado. Um array JSON-RPC é rejeitado com HTTP 400 |
O servidor lê. A única coisa que ele escreve é o seu contador de chamadas diário.
Instalá-lo, cliente a cliente
Mesma URL para todos os clientes: https://olud.ai/mcp.php. A chave viaja no cabeçalho X-Api-Key. Sem uma chave, o servidor ainda responde, com uma cota menor (25 chamadas por dia por IP, 5 resultados por chamada). Sua chave e um bloco pronto para colar estão na Sua conta, seção servidor MCP.
Claude Code, um comando:
claude mcp add --transport http opensourceai https://olud.ai/mcp.php \ --header "X-Api-Key: sua-chave" # sem uma chave: claude mcp add --transport http opensourceai https://olud.ai/mcp.php # ver o que foi registrado: claude mcp list
Claude Desktop, claude_desktop_config.json:
{
"mcpServers": {
"opensourceai": {
"type": "http",
"url": "https://olud.ai/mcp.php",
"headers": { "X-Api-Key": "sua-chave" }
}
}
}Esse arquivo está em ~/Library/Application Support/Claude/claude_desktop_config.json no macOS, e %APPDATA%\Claude\claude_desktop_config.json no Windows. Saia e reabra o Claude Desktop após editá-lo; ele lê o arquivo na inicialização.
Se o Claude Desktop mostrar o servidor como falhado ou não listá-lo, sua versão aceita apenas servidores locais (stdio). Conecte-o com mcp-remote, que precisa do Node instalado:
{
"mcpServers": {
"opensourceai": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://olud.ai/mcp.php",
"--header", "X-Api-Key:${OSAI_KEY}"],
"env": { "OSAI_KEY": "sua-chave" }
}
}
}Cursor, .cursor/mcp.json no projeto (ou ~/.cursor/mcp.json para cada projeto):
{
"mcpServers": {
"opensourceai": {
"url": "https://olud.ai/mcp.php",
"headers": { "X-Api-Key": "sua-chave" }
}
}
}VS Code, .vscode/mcp.json no espaço de trabalho:
{
"servers": {
"opensourceai": {
"type": "http",
"url": "https://olud.ai/mcp.php",
"headers": { "X-Api-Key": "${input:osaiKey}" }
}
},
"inputs": [
{ "id": "osaiKey", "type": "promptString",
"description": "chave da API olud.ai", "password": true }
]
}Uma chave na URL como ?key=sua-chave também funciona, porque o servidor a lê. Prefira o cabeçalho: strings de consulta acabam no histórico do navegador, logs de proxy e histórico do shell.
Verificar que funciona, com curl
Quando um cliente diz que um servidor está indisponível, teste o servidor em si primeiro. Esta chamada lista as ferramentas e não custa nada: apenas tools/call diminui sua cota, então initialize, tools/list e ping são gratuitos. Reiniciar seu editor nunca consome o dia.
curl -s -X POST https://olud.ai/mcp.php \
-H "Content-Type: application/json" \
-H "X-Api-Key: sua-chave" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Uma resposta saudável é HTTP 200 com {"jsonrpc":"2.0","id":1,"result":{"tools":[ ... ]}} e os cinco nomes dentro. Em seguida, gaste uma chamada:
curl -s -X POST https://olud.ai/mcp.php \
-H "Content-Type: application/json" \
-H "X-Api-Key: sua-chave" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search_open_source_ai",
"arguments":{"query":"texto para fala","limit":5}}}'Uma chamada bem-sucedida de tools/call carrega dois cabeçalhos de resposta que valem a pena ler: X-RateLimit-Limit é sua cota diária, X-RateLimit-Remaining é o que resta após esta chamada. Eles são definidos apenas em tools/call.
Verificação de liveness mais curta possível. Ela responde {"jsonrpc":"2.0","id":0,"result":{}} e não precisa de chave:
curl -s -X POST https://olud.ai/mcp.php \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":0,"method":"ping"}'As cinco ferramentas
Você não chama esses pelo nome. Você faz uma pergunta, o cliente escolhe a ferramenta. Os nomes importam quando você lê um log ou escreve a chamada você mesmo, e eles são correspondidos exatamente: search_open_source_ai funciona, Search_Open_Source_AI não funciona.
| Ferramenta | Parâmetros | Limites e padrões | Retornos |
|---|---|---|---|
| search_open_source_ai | query (string, obrigatório), limit (inteiro) | limite restrito a 1 … seu teto de resultados por chamada. Padrão 10, ou seu teto se for menor | matches (total encontrado, não truncado), então id, nome, resumo, estrelas, linguagem, licença, github, página, pontuação de saúde e rótulo, tendência recente |
| get_project | projeto (string, obrigatório) | Aceita um slug (ollama-ollama), owner/repo, uma URL completa do github.com, ou o nome exato do projeto | Tudo acima, além de proprietário, forks, criado, enviado, tópicos, a análise completa de saúde, estrelas ganhas e até 5 lançamentos recentes |
| find_alternatives | produto (string, obrigatório) | Nome do produto comercial. Correspondido pelo seu slug, depois pelo seu nome exato | produto, página e a lista de alternativas, que também é limitada pelo seu teto de resultados por chamada |
| list_models | provedor (string), max_price_out (número), min_context (inteiro), free_only (booleano), limite (inteiro) | provedor é uma substring que não diferencia maiúsculas de minúsculas. max_price_out é em dólares por milhão de tokens de saída. min_context está em tokens. limite é restringido a 1 … seu teto, padrão 15 ou seu teto se for menor | correspondências, então id, nome, provedor, contexto, price_per_million (entrada e saída), modalidade, tool_calling, e a URL do leaderboard |
| projetos_em_alta | tipo (string), limite (inteiro) | tipo é em alta, emergente ou top_health. Qualquer outra coisa volta para em alta sem erro. Padrão em alta. limite é restringido a 1 … seu teto, padrão 10 ou seu teto se for menor | tipo, os cartões de projeto, e o tempo de geração do índice |
Perguntas que alcançam cada ferramenta
- search_open_source_ai — "Encontre uma biblioteca OCR de código aberto que eu possa executar localmente."
- get_project — "Quão saudável está ollama/ollama agora, e quando foi a última vez que foi lançado?"
- find_alternatives — "Que ferramenta de código aberto poderia substituir o Notion para nós?"
- list_models — "Quais modelos têm pelo menos 128k de contexto e custam menos de $1 por milhão de tokens de saída?"
- trending_projects — "Quais projetos de IA de código aberto estão ganhando estrelas mais rápido esta semana?" Pergunte por emergente para obter projetos saudáveis que ainda são pouco conhecidos, ou top_health para os melhor mantidos.
Filtros descartam o que não podem julgar. Com max_price_out definido, um modelo cujo preço de saída não temos figura é deixado de fora em vez de ser adivinhado. Com min_context definido, um modelo sem janela de contexto registrada conta como zero e é deixado de fora. free_only mantém modelos cujo nível é exatamente gratuito.
Um limite acima do seu teto não é um erro: ele é restringido silenciosamente. O esquema publicado diz máximo 100 porque esse é o mais alto que qualquer plano permite, não o que sua chave permite. Um limite que não é um número volta para o padrão.
Volumes por plano
| Sem chave | Gratuito | Dev | Pro | Org | |
|---|---|---|---|---|---|
| Chamadas por dia | 25 | 200 | 2,000 | 20,000 | 200,000 |
| Resultados por chamada | 5 | 10 | 25 | 50 | 100 |
Dois tetos, porque eles param duas coisas diferentes: chamadas por dia limitam a carga, resultados por chamada limitam quanto do catálogo sai em uma resposta.
- Chamadas MCP são contadas separadamente das chamadas da API REST. Mesma chave, dois contadores. Um editor fazendo algumas perguntas nunca toca no seu orçamento da API.
- Apenas ferramentas/chamada são contadas. initialize, tools/list e ping não custam nada.
- Ambos os contadores reiniciam às 00:00 UTC.
- Sem uma chave, a contagem é por endereço IP. Todos atrás de um IP de escritório compartilham os 25.
- No plano gratuito e sem chave, cada resposta termina com uma linha de nota informando seus dois tetos. Respostas Dev, Pro e Org não carregam tal linha.
- Org é o plano chamado business dentro do sistema. Mensagens de erro imprimem o nome interno.
Quando não funciona
Chave digitada incorretamente ou revogada. Cada pergunta falha com:
Essa chave da API é desconhecida ou foi revogada. Remova-a para usar o nível gratuito, ou obtenha uma nova em https://olud.ai/account.html
Uma chave ruim não volta silenciosamente para a concessão anônima. Corrija a chave ou remova o cabeçalho X-Api-Key completamente, e então reinicie o cliente para que ele releia a configuração.
Teto diário alcançado. Com uma chave, depois sem:
Cota diária MCP alcançada (200 chamadas/dia no plano "gratuito"). Reinicia às 00:00 UTC. Níveis mais altos: https://olud.ai/plans.html Limite anônimo alcançado (25 chamadas/dia por IP). Reinicia às 00:00 UTC. Uma chave de API gratuita aumenta para 200/dia — https://olud.ai/account.html
Nada está quebrado e nada está sendo cobrado. Aguarde até 00:00 UTC, ou suba de nível. Se você não tem chave, uma chave gratuita leva você de 25 para 200 chamadas e de 5 para 10 resultados por chamada.
Nome da ferramenta não é um dos cinco:
Ferramenta desconhecida "list_projects". Chame tools/list para ver o que está disponível.
Um argumento obrigatório está faltando. Cada mensagem mostra a forma que deseja:
Faltando "query". Exemplo: {"query": "texto para fala"}
Faltando "project". Exemplo: {"project": "ollama/ollama"}
Faltando "product". Exemplo: {"product": "Midjourney"}Nada encontrado. Ambas as mensagens dizem onde você deve ir a seguir:
Nenhum projeto encontrado para "ollamaa". Tente search_open_source_ai primeiro para obter seu id exato. Ainda não rastreamos "photoshop". Correspondências mais próximas: <até 8 nomes>. Lista completa: https://olud.ai/alternatives-hub.html
O catálogo está em meio a uma reconstrução. Aguarde um minuto e pergunte novamente; estas são as quatro formulações, uma por conjunto de dados:
O catálogo está sendo reconstruído. Tente novamente em um minuto. Essa lista está sendo reconstruída. Tente novamente em um minuto. O catálogo de modelos não está disponível no momento. O índice de alternativas não está disponível no momento.
Abrir https://olud.ai/mcp.php em um navegador retorna HTTP 405. Essa é a resposta correta: o endpoint fala JSON-RPC sobre POST, e a especificação pede 405 quando um servidor não oferece um fluxo GET. O corpo diz o que fazer e carrega a configuração para copiar. Ele chega em uma linha; está espaçado aqui para ser lido.
{
"server": "olud.ai MCP",
"version": "1.0.0",
"protocol": "2025-06-18",
"usage": "Este endpoint fala MCP sobre JSON-RPC 2.0. Envie um POST a partir de um cliente 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"
}Recusas de nível de transporte. Esses quatro carregam um código de erro JSON-RPC, e os três primeiros também carregam um código de erro HTTP:
405 -32600 Use POST com um corpo JSON-RPC 2.0. (PUT, DELETE, HEAD…) 400 -32700 Erro de análise: o corpo não é um JSON válido. 400 -32600 O agrupamento JSON-RPC não é suportado (removido em MCP 2025-06-18). 200 -32601 Método desconhecido "tools/execute". 200 -32602 Nome da ferramenta ausente nos parâmetros.
Se seu cliente reclamar sobre um ID de sessão ausente, ignore e verifique o tipo de transporte em sua configuração. Este servidor não mantém sessão, e não há Mcp-Session-Id para enviar de volta. Um cliente configurado para stdio ou para SSE contra esta URL falhará antes da primeira chamada; o tipo é http.
REST API
Autenticação e chaves
URL base: https://olud.ai/api/v1. Cada endpoint é um GET. O cabeçalho CORS anuncia POST, mas v1 não tem rota POST: os parâmetros são sempre lidos da string de consulta.
Envie sua chave no cabeçalho X-Api-Key. Um parâmetro de consulta ?key= também funciona, para uma aba do navegador ou um teste rápido. Se ambos estiverem presentes, o cabeçalho vence. Apenas /meta funciona sem uma chave.
Ambas as formas são aceitas:
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"
Para obter uma chave: faça login em olud.ai e abra sua página de conta, seção Developer API. A chave é criada na sua primeira visita, no plano gratuito, e se parece com osk_ seguido por 40 caracteres hexadecimais. Rotacioná-la a partir da mesma página exclui a chave antiga imediatamente — o valor antigo então retorna 403 invalid_key.
| Cabeçalho | Valor | Enviado em |
|---|---|---|
| X-RateLimit-Limit | Seu teto diário, como um inteiro | Cada solicitação que passou na verificação da chave |
| X-RateLimit-Remaining | Solicitações restantes hoje, arredondadas para 0 | Cada solicitação que passou na verificação da chave, incluindo o 429 |
| ETag | MD5 citado do tempo de construção do gráfico de projetos | Cada endpoint exceto /meta |
| Access-Control-Allow-Origin | * | Cada resposta, incluindo OPTIONS (que responde 204) |
Envelope de resposta
Um sucesso é sempre HTTP 200 com três chaves de nível superior: ok é true, data contém a carga, meta contém o contexto. data é um objeto em endpoints de registro único e um array em endpoints de lista.
GET /api/v1/meta, verbatim:
{"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 computed by olud.ai (olud.ai). Contextual facts from public sources: GitHub, Hugging Face, OpenRouter, Artificial Analysis, PyPI, NPM, Docker Hub."}}meta.attribution é definido em cada resposta de sucesso, em cada endpoint, e não pode ser desativado. O restante de meta varia por endpoint: generated, total, limit, offset, sort, freshness, points. Leia as seções do endpoint para quais campos você obtém.
Um erro carrega ok false, um status HTTP diferente de 200, e error.code mais error.message. Não há objeto meta em um erro, então não há campo de atribuição. Teste ok antes de ler data — não assuma a forma de sucesso.
GET /api/v1/projects sem chave, verbatim:
{"ok":false,"error":{"code":"missing_key","message":"Forneça sua chave API via o cabeçalho X-Api-Key (ou ?key=). Obtenha uma em https://olud.ai/api/"}}Envie o ETag de volta como If-None-Match e você receberá 304 com um corpo vazio quando o gráfico não tiver sido reconstruído. O gráfico é reconstruído uma vez a cada manhã, então consultar mais frequentemente do que isso retorna 304 o dia todo.
Endpoints: meta e projetos
GET /meta
Status do gráfico. O único endpoint sem uma chave, e o único que não conta contra sua cota. Retorna o nome e a versão da API, gerado (timestamp UTC da última construção do gráfico), counts.projects, uma lista de endpoints e a URL da documentação. Sem parâmetros. Se os arquivos do gráfico forem ilegíveis, counts é nulo e a chamada ainda retorna 200 — use-o como um verificador de saúde e para decidir se uma nova coleta vale a pena. Não envia ETag e nem cabeçalhos de limite de taxa.
curl -s https://olud.ai/api/v1/meta
GET /project/{id}
Um projeto, registro completo. Esta é a visão mesclada: fatos do GitHub, nossa pontuação de manutenção, velocidade de estrelas, lançamentos, pulso de adoção.
- Identidade: id, nome, proprietário, url, página (caminho da página do projeto no site), desc.
- Fatos do GitHub na última construção: estrelas, forks, lang, licença, tópicos, criado, enviado.
- saúde: pontuação de 100, rótulo, por que, além dos quatro componentes que a compõem — atividade (máx 30), impulso (máx 20), comunidade (máx 30), manutenção (máx 20) — pesos v2 desde 29 de julho de 2026 — e verificado. Os rótulos seguem a pontuação: 85+ Prosperando, 70+ Saudável, 50+ Mantido, 30+ Desacelerando, abaixo de 30 Em risco. Um projeto sem commit em quatro semanas tem sua pontuação limitada a 45, então os componentes podem somar mais do que a pontuação. Nem todo projeto é pontuado; teste para o campo.
- velocidade: stars_1d e stars_7d, a partir do nosso próprio histórico diário de estrelas.
- lançamentos: tag, data, nível, url — mais recente primeiro.
- ferramenta: slug, cat, pulse, docker_pulls, npm_month, pip_month, hn_hits, bsky_week, compare_pages. Apresentar apenas para projetos correspondentes a uma ferramenta rastreada.
- classificação, tendência (estável, silenciosa ou acelerando) e sinais (stars_accel, has_page).
| Parâmetro | Tipo | Padrão | Comportamento |
|---|---|---|---|
| id | segmento de caminho, obrigatório | nenhum | Minúsculas. Resolvido em três etapas: slug exato, depois o índice owner/name, depois owner-name. Nenhum segmento retorna 400 missing_id; nenhuma correspondência retorna 404 not_found com um ponteiro para /search. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/project/ollama-ollama
GET /projects
Filtrar, classificar e paginar o catálogo. Os filtros se aplicam primeiro, depois a classificação, depois o deslocamento e limite.
| Parâmetro | Tipo / limites | Padrão | Comportamento |
|---|---|---|---|
| lang | string | sem filtro | Correspondência exata na linguagem do projeto, sem diferenciação entre maiúsculas e minúsculas. lang=rust mantém apenas Rust. |
| licença | string | sem filtro | Correspondência de substring no id da licença, sem diferenciação entre maiúsculas e minúsculas. license=gpl mantém GPL-2.0 e AGPL-3.0. |
| vertical | string | sem filtro | Correspondência exata, sem diferenciação entre maiúsculas e minúsculas. Valores presentes no gráfico: robótica, segurança, finanças, ciência, saúde, educação, legal. A maioria dos projetos não tem nenhum, e todos desaparecem quando você define isso. |
| saúde_min | inteiro | sem filtro | Mantém projetos cuja saúde.score é maior ou igual ao valor. Projetos não pontuados contam como 0 e são excluídos. Um valor não numérico se torna 0, o que não filtra nada. |
| classificar | stars, saúde, impulso ou recente | stars | Sempre em ordem decrescente. impulso lê velocity.stars_7d, recente lê a data do último push. Um valor desconhecido classifica por estrelas, mas é ecoado de volta verbatim em meta.sort — verifique meta.sort se a ordem te surpreender. |
| limite | inteiro 1-100 | 25 | Valores fora do intervalo são limitados aos limites, não numéricos retornam a 25. Nenhum erro é gerado, então limit=500 silenciosamente te dá 100. |
| deslocamento | inteiro 0-100000 | 0 | Limitado da mesma forma. |
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"
dados são um array de cartões compactos: id, nome, url, desc, stars, lang, license, saúde (apenas a pontuação), impulso (stars_7d), tendência, vertical. Campos sem valor estão presentes e nulos. Para os componentes, lançamentos e números de adoção, chame /project/{id}. meta fornece total (correspondências após filtragem, antes da paginação), limite, deslocamento, classificação e gerado.
Endpoints: pesquisa, emergentes, alternativas
GET /search
Busca projetos por nome, descrição e tópicos. Não busca modelos ou linhas do Hugging Face — use /models e /hf para isso.
| Parâmetro | Tipo / limites | Padrão | Comportamento |
|---|---|---|---|
| q | string, obrigatório | nenhum | Removido e em minúsculas. Vazio ou apenas espaços em branco retorna 400 missing_query. |
| limite | inteiro 1-50 | 15 | Limitado aos limites; não numérico retorna 15. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ "https://olud.ai/api/v1/search?q=ocr&limit=5"
Pontuação, para que você possa prever a ordem: correspondência exata do nome 100, nome começa com q 60, nome contém q 40, descrição contém q 15, e +20 quando q é igual a um dos tópicos do projeto exatamente. Empates são quebrados por estrelas. Qualquer coisa com pontuação 0 é descartada. A correspondência é uma substring simples — sem stemming, sem tolerância a erros de digitação. Os cartões têm a mesma forma que /projects. meta fornece total (todas as correspondências, não a página) e q como normalizado.
GET /emerging
O índice de descoberta diário: repositórios jovens com crescimento sustentado e, onde podemos pontuá-los, boa saúde. Classificado pelo nosso Discovery Score. Os cartões têm dois campos extras: signals e detected, a data em que o projeto entrou pela primeira vez no índice (nulo quando desconhecido).
| Parâmetro | Tipo / limites | Padrão | Comportamento |
|---|---|---|---|
| limite | inteiro 1-100 | 25 | Limitado. Você pode receber menos linhas do que pediu: ids que não estão mais no gráfico de projetos são ignorados. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ "https://olud.ai/api/v1/emerging?limit=10"
A frescura depende do plano, e meta diz qual você obteve. Pro e Business leem o índice da manhã e recebem meta.freshness "em tempo real". Free e Dev leem o snapshot de ontem e recebem "dia anterior" mais uma meta.note. Duas consequências que vale a pena saber: em Free e Dev, meta.criteria é nulo, porque o texto dos critérios é armazenado apenas com o índice atual; e se o arquivo do snapshot de ontem estiver faltando, Free e Dev recebem o índice atual enquanto meta.freshness ainda lê "dia anterior".
GET /alternatives/{product}
Alternativas de código aberto para um produto comercial. data retorna nome, domínio, desc, página, cat e um array de alternativas cujas entradas contêm nome, repo, site, licença e desc. repo pode ser uma string vazia quando o projeto não tem repositório no GitHub. meta fornece gerado.
| Parâmetro | Tipo | Padrão | Comportamento |
|---|---|---|---|
| produto | segmento de caminho, obrigatório | nenhum | Em minúsculas. Chave exata do produto primeiro; se falhar, o primeiro produto cuja chave ou nome contém sua string, na ordem do arquivo. Nenhum segmento retorna 400 missing_id; nenhuma correspondência retorna 404 not_found. Passe o slug exato — o que está na URL da página /alternatives/<slug>.html — quando você precisa de um produto específico, porque a correspondência solta pega o primeiro acerto, não o melhor. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/alternatives/notion
Endpoints: modelos, hf, histórico
GET /models
Catálogo de modelos construído a partir do OpenRouter. Cada linha: id, nome, provedor, tier, ctx (janela de contexto em tokens), price_in e price_out (dólares americanos por milhão de tokens, arredondado para duas casas decimais, 0 quando a fonte não relata preço), modalidade (por exemplo texto->text ou texto+imagem+arquivo->text), ferramentas (booleano) e classificação. Campos sem valor são descartados da linha, então verifique a presença em vez de assumir que price_in existe. meta fornece total, limite, offset, gerado e fonte.
| Parâmetro | Tipo / limites | Padrão | Comportamento |
|---|---|---|---|
| provedor | string | sem filtro | Correspondência de substring, sem distinção entre maiúsculas e minúsculas. provider=mistral corresponde a "Mistral AI". |
| tier | gratuito ou pago | sem filtro | Correspondência exata, sem distinção entre maiúsculas e minúsculas em sua entrada. |
| limite | inteiro 1-200 | 50 | Limitado aos limites; não numérico retorna 50. |
| deslocamento | inteiro 0-100000 | 0 | Aplicado à lista mesclada. Linhas de peso aberto vêm primeiro, depois as proprietárias, cada bloco em ordem de classificação. |
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: mais baixados e em alta. Sem parâmetros. data tem três arrays — top_llm (modelos de geração de texto por downloads de 30 dias), top (todas as tarefas, limitado a 30 linhas por este endpoint) e trending (os destaques de hoje). Cada linha: id, org, nome, tarefa, downloads (últimos 30 dias), likes, licença, gated, criado, atualizado, tendência (aumento de likes nas linhas em alta, nulo em outros lugares). meta fornece gerado e fonte.
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/hf
GET /history/{slug}
Série diária para um projeto: estrelas, saúde e velocidade de 7 dias. Apenas chaves Pro e Business — o plano Business é o vendido como Org. Qualquer outro plano recebe 403 pro_required. data retorna slug, dias (datas como YYYY-MM-DD, mais antigas primeiro) e série com três arrays, estrelas, saúde e velocidade_7d, alinhados índice por índice com dias. Entradas individuais podem ser nulas quando o valor de um dia estiver faltando. meta fornece pontos e janela.
| Parâmetro | Tipo | Padrão | Comportamento |
|---|---|---|---|
| slug | segmento de caminho, obrigatório | nenhum | Em minúsculas, e deve corresponder a ^[a-z0-9][a-z0-9._-]*$ — o mesmo id que /project/{id}. Qualquer outra coisa retorna 400 bad_slug. Um slug válido sem nada registrado ainda retorna 404 not_found. |
curl -s -H "X-Api-Key: osk_YOUR_KEY" \ https://olud.ai/api/v1/history/ollama-ollama
Cotas diárias
As requisições são contadas por chave, por dia UTC. O contador é reiniciado às 00:00 UTC. Não há limite por segundo ou por minuto no código.
| Planeje na chave | Requisições / dia | Nome público | O que muda |
|---|---|---|---|
| grátis | 500 | Gratuito | Tudo exceto /history. /emerging serve o índice do dia anterior. |
| dev | 5000 | Dev | Mesmo acesso que o Free, teto mais alto. |
| pro | 50000 | Pro | /history abre. /emerging serve o índice desta manhã. |
| business | 500000 | Org | Mesmo acesso que o Pro, teto mais alto. |
O que conta: cada endpoint exceto /meta, uma unidade por requisição. O contador é incrementado logo após a verificação da chave e antes de qualquer outra coisa, então um 304, um 404 not_found, um 400 missing_query e um 503 graph_unavailable custam uma unidade. Apenas 401 missing_key e 403 invalid_key não custam nada, já que não há chave válida para cobrar.
Um campo de cota definido na sua chave substitui o padrão do plano. X-RateLimit-Limit é a autoridade sobre o seu teto, não a tabela acima.
Além do teto, cada requisição retorna 429 quota_exceeded até o reinício. A requisição recusada não é contada, nada é enfileirado e nada é cobrado. Nenhum cabeçalho Retry-After é enviado — o reinício é às 00:00 UTC.
Como uma requisição recusada se parece em uma chave gratuita:
HTTP/2 429
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 0
{"ok":false,"error":{"code":"quota_exceeded","message":"Cota diária atingida (500 requisições/dia no plano \"grátis\"). Reinicia às 00:00 UTC."}}Códigos de erro
Cada erro retorna error.code como uma string estável. Baseie-se nisso, não no texto da mensagem, que contém slugs e nomes de planos e muda com a requisição.
| HTTP | código | Acionar | O que fazer |
|---|---|---|---|
| 401 | missing_key | Sem cabeçalho X-Api-Key e sem ?key=. Também o que você recebe para um caminho desconhecido quando nenhuma chave é enviada, porque a verificação da chave ocorre antes do roteamento. | Envie a chave. Um 401 em um caminho que você tem certeza que existe é um cabeçalho ausente, não um caminho errado. |
| 403 | invalid_key | A chave não está no armazenamento, ou sua flag ativa é falsa. Rotacionar sua chave deleta a anterior. | Leia a chave atual na sua página de conta e atualize o chamador. A rotação quebra todas as cópias implantadas da chave antiga de uma vez. |
| 403 | pro_required | /history chamado com uma chave gratuita ou dev. | Leia os valores de hoje de /project/{id} (saúde, velocidade), ou mude para Pro. |
| 429 | quota_exceeded | O contador diário atingiu seu teto. | Pare de chamar até às 00:00 UTC, ou aumente o plano. Leia X-RateLimit-Limit para confirmar seu teto real. |
| 400 | missing_id | /project ou /alternatives chamado sem segmento de caminho. | Adicione o segmento. /v1/project sozinho não é uma listagem — use /v1/projects. |
| 400 | consulta_faltando | /search com q vazio ou apenas espaços em branco. | Envie um q não vazio. Note que a solicitação ainda foi contabilizada. |
| 400 | slug_incorreto | /history slug vazio ou contendo um caractere fora de ^[a-z0-9][a-z0-9._-]*$. | Passe o id exatamente como retornado por /projects ou /search. |
| 404 | nao_encontrado | ID de projeto desconhecido, nenhuma alternativa rastreada para esse produto, ou nenhum histórico armazenado para esse slug. | Para um projeto, tente novamente através de /search?q=. Para o histórico, o projeto pode ter entrado em rastreamento muito recentemente para ter pontos. |
| 404 | endpoint_desconhecido | Chave válida, caminho não está no roteador. | Verifique a ortografia. /meta lista seis endpoints e omite /hf e /history, que ambos existem. |
| 503 | grafico_indisponivel | Um arquivo de gráfico está faltando ou ilegível, o que acontece enquanto a construção da manhã o escreve. | Tente novamente em um minuto. Sua chave está boa; não a gire. |
| 503 | nao_pronto | /hf antes da varredura matinal do Hugging Face produziu seu arquivo. | Tente mais tarde no dia. Outros endpoints não são afetados. |
| 304 | sem corpo | If-None-Match correspondeu ao ETag atual. | Sirva sua cópia em cache. Lembre-se de que isso consumiu uma solicitação do seu limite. |
Política de retry que corresponde ao código: tente novamente em 503 após um minuto, e em 429 apenas após o reset UTC. Nunca tente novamente um 400, 403 ou 404 inalterado — a resposta não mudará e cada tentativa custa uma solicitação.
Webhooks
Criando um webhook
Um webhook é um POST assinado para seu endpoint por evento. Você escolhe os eventos, os repositórios e a forma do corpo. Webhooks precisam de um plano pago: em uma chave gratuita, a criação responde 403 paid_feature e a página da conta mostra um painel bloqueado em vez do formulário.
| Plano reportado pela API | Webhooks permitidos |
|---|---|
| grátis | 0 |
| dev | 3 |
| pro | 10 |
| business | 50 |
Um nome de plano que não reconhecemos volta para 1. Uma vez que você atinge sua contagem, a criação responde 429 limit_reached — exclua um ou mude de plano.
Da sua conta
- Faça login e abra /account.html. O cartão de Webhooks aparece uma vez que sua chave API foi carregada, e permanece oculto até então.
- Cole seu endpoint no campo URL. A página recusa qualquer coisa que não comece com https://.
- Marque os eventos. release e health estão marcados para você; license e new_project não estão.
- Deixe o campo de repositórios vazio para receber tudo, ou digite entradas owner/name separadas por vírgulas.
- Escolha o destino na seleção: seu próprio endpoint (JSON assinado bruto), Slack ou Discord.
- Pressione Criar. O segredo aparece uma vez, em uma caixa verde. Copie-o antes de sair da página — nada o mostrará novamente.
- Cada linha de webhook então carrega um botão Enviar ping de teste e um link Excluir. Excluir interrompe as entregas imediatamente.
Da API — criar. Sua chave API é a que está na sua conta, enviada como 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"
}'O bloco de dados da resposta. Cada resposta também carrega um bloco meta com nossa linha de atribuição. O segredo está aqui e em nenhum outro lugar.
{
"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": "Armazene este segredo agora — ele é mostrado apenas uma vez. Verifique cada entrega: X-OSAI-Signature == \"sha256=\" + HMAC_SHA256(raw_body, secret). Teste-o: POST {\"ping\":\"whk_9b41c7e2f0a5d3861c4e\"}."
}O que a URL deve satisfazer
- Esquema https. http é rejeitado.
- Um host deve estar presente.
- Se você escrever uma porta, ela deve ser 443. https://your-app.example:8443/hook é rejeitado.
- O host não pode ser localhost e não pode terminar em .local ou .internal.
- Resolvemos o host. Se qualquer endereço retornado estiver em um intervalo privado ou reservado, a URL é rejeitada.
- Não chamamos seu endpoint durante a criação. Um host que falha ao resolver passa por essa verificação e falha depois, na entrega. Envie um ping de teste para descobrir.
- Uma URL ruim responde 400 bad_url.
Quais repositórios aceitam
- O padrão é ["*"] — todo repositório que rastreamos.
- As entradas são cortadas e convertidas para minúsculas, portanto, a correspondência não diferencia maiúsculas de minúsculas.
- Uma entrada deve parecer como owner/name: owner começa com uma letra ou dígito, seguido de letras, dígitos, ponto, sublinhado, hífen.
- Entradas que não correspondem são descartadas sem aviso. Se nada sobreviver, você recebe 400 bad_repos.
- "*" em qualquer lugar da lista substitui a lista inteira. ["acme/one", "*"] é armazenado como ["*"].
- Mais de 100 entradas respondem 400 too_many_repos. Use "*" além desse ponto.
Os quatro eventos
Cada evento carrega um repositório. Um evento é um POST — eventos nunca são agrupados. O nome está no cabeçalho X-OSAI-Event e no campo de evento do corpo.
| evento | Dispara quando | Chaves dentro dos dados |
|---|---|---|
| saúde | o rótulo de saúde de um repositório muda | de, para, estrelas, página |
| lançamento | um novo rótulo de lançamento aparece para um repositório | rótulo, nome, url |
| licença | a licença que temos para um repositório muda | de, para |
| novo_projeto | um repositório entra no diretório | estrelas, página |
saúde
{
"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 e para são rótulos, não números. Os seis rótulos que publicamos: Thriving, Healthy, Maintained, Slowing down, At risk, Archived. estrelas é a contagem de estrelas que temos para o repositório. Nenhum evento dispara na primeira vez que um repositório recebe um rótulo — uma mudança precisa de um valor anterior para comparar.
lançamento
{
"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"
}rótulo é a tag do git. nome é o nome de exibição do projeto em nosso diretório, não o título do lançamento. url sempre aponta para a página de lançamentos do repositório, nunca para um lançamento específico — construa a URL da tag você mesmo se precisar.
licença
de e para são as strings de licença que temos. Este evento não tem chave de página.
{
"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"
}novo_projeto
{
"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"
}No máximo 25 eventos new_project por execução, com as maiores contagens de estrelas primeiro. Em uma primeira execução, ou quando mais de 200 repositórios aparecem de uma vez, eles são registrados e nenhum é enviado — essa proteção evita que uma reimportação o sobrecarregue.
Corpo e cabeçalhos
Com o formato json, o corpo tem cinco chaves, nesta ordem. Nada mais é adicionado.
{
"webhook_id": "whk_9b41c7e2f0a5d3861c4e",
"event": "release",
"repo": "acme/inference-server",
"data": { },
"sent_at": "2026-07-27T05:41:12+00:00"
}webhook_id é o id que você criou: whk_ mais 20 caracteres hexadecimais. repo é o owner/name em letras minúsculas, e é nulo apenas em um ping de teste. data é um objeto, vazio para um evento que não carrega campos. sent_at é UTC, ISO 8601 com um deslocamento.
| Cabeçalho | Valor |
|---|---|
| Content-Type | application/json |
| User-Agent | olud.ai-Webhooks/1.0 |
| X-OSAI-Event | health, release, license, new_project ou ping |
| X-OSAI-Delivery | 16 caracteres hexadecimais, novos para cada POST |
| X-OSAI-Signature | sha256= seguido pelo HMAC-SHA256 do corpo |
Esses cinco são o conjunto completo. X-OSAI-Delivery não é armazenado do nosso lado — use-o para identificar um duplicado em seu próprio log.
Um ping de teste, enviado por POST {"ping":"whk_…"}. Mesmos cabeçalhos, mesma assinatura, X-OSAI-Event: ping.
{
"webhook_id": "whk_9b41c7e2f0a5d3861c4e",
"event": "ping",
"repo": null,
"data": {
"message": "Funciona. Eventos reais chegam após cada verificação matinal."
},
"sent_at": "2026-07-27T05:41:12+00:00"
}Verificando a assinatura
Recalcule o HMAC-SHA256 do corpo bruto com seu segredo, prefixe com sha256= e compare com X-OSAI-Signature. O segredo é a string whs_ da resposta de criação: whs_ mais 48 caracteres hexadecimais.
PHP
<?php
$raw = file_get_contents('php://input'); // bytes brutos, antes de qualquer análise
$sent = $_SERVER['HTTP_X_OSAI_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $raw, $SECRET);
if (!hash_equals($expect, $sent)) { // tempo constante
http_response_code(401);
exit;
}
$event = json_decode($raw, true); // analise apenas após a verificação
http_response_code(200);Node, com Express
const crypto = require('crypto');
const express = require('express');
const app = express();
// express.raw mantém os bytes. express.json() os destruiria.
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 lança um RangeError em buffers de diferentes comprimentos,
// então verifique o comprimento primeiro, depois compare em tempo constante.
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, com 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() # bytes, antes de qualquer análise
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 constante
abort(401)
event = json.loads(raw)
return "", 200Compare com hash_equals, crypto.timingSafeEqual ou hmac.compare_digest. Nunca com == ou ===. Uma comparação de string simples para ao primeiro byte que difere, então o tempo que leva informa um atacante quantos bytes iniciais eles acertaram. Repita a medição e eles recuperam a assinatura byte a byte, depois postam eventos forjados. As três funções acima leem cada byte, independentemente da entrada.
- Hash os bytes brutos, antes de qualquer análise. Um corpo que um framework analisou e re-serializou hash para algo diferente, e cada entrega parecerá inválida.
- O formato json escapa caracteres não-ASCII como \uXXXX. Re-serializar também perde isso.
- Um cabeçalho ausente chega como uma string vazia. Todos os três exemplos rejeitam isso em vez de travar.
- O segredo é mostrado uma vez, na criação. Se você perdê-lo, exclua o webhook e crie outro — você receberá um novo id e um novo segredo.
- Mantenha um segredo por webhook. Dois webhooks nunca compartilham um.
json, slack, discord
o formato é escolhido na criação e padrão para json. Não há endpoint de atualização: para mudá-lo, exclua o webhook e crie outro. GET relata o formato de cada webhook; a resposta de criação não o ecoa. Um valor fora dos três responde 400 bad_format.
| formato | O que postamos | URL para colar |
|---|---|---|
| json | o payload acima, inalterado | seu próprio endpoint |
| slack | texto, mais um bloco de seção mrkdwn | uma URL de webhook de entrada do Slack |
| discord | um embed: título, url, descrição, cor, rodapé | uma URL de webhook de canal do Discord |
slack
Um evento de lançamento, postado no Slack
{
"text": "🚀 acme/inference-server lançado v0.6.2 — Acme Inference Server",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🚀 *<https://github.com/acme/inference-server/releases|acme/inference-server lançado v0.6.2>*\nAcme Inference Server"
}
}
]
}Obtenha a URL do Slack: adicione um aplicativo ao workspace, ative Webhooks de Entrada, adicione um para o canal que você deseja, copie a URL https://hooks.slack.com/services/… e cole no campo url. O texto é sempre preenchido — é o que o Slack mostra na notificação e em clientes que não renderizam blocos.
discord
Um evento de licença, postado no Discord
{
"embeds": [
{
"title": "🔑 acme/inference-server mudou de licença",
"url": "https://github.com/acme/inference-server",
"description": "Apache-2.0 → BSL-1.1",
"color": 14427686,
"footer": { "text": "olud.ai" }
}
]
}Obtenha a URL do Discord: configurações do canal, Integrações, Webhooks, Novo Webhook, Copiar URL do Webhook — https://discord.com/api/webhooks/… — e cole no campo de URL. A cor é um inteiro decimal: 14427686 (#DC2626) para licença, 6514417 (#6366F1) para lançamento, new_project e ping.
Ambos os hosts passam na verificação de URL. Quando um evento de licença não possui página, o link volta para https://github.com/owner/name; saúde e new_project linkam para a página do projeto em olud.ai; o lançamento linka para a página de lançamentos do repositório.
Entrega, falha, desativação
As entregas dependem do alerta pass, send-alerts.php — a mesma detecção que produz os e-mails dos membros. O despacho ocorre logo após a detecção, no mesmo processo. Não há programação separada e nem fila. A API descreve a cadência como "diária, após a varredura matinal".
- Um POST por evento, por webhook correspondente. Um webhook recebe um evento quando o nome do evento está em sua lista de eventos, e quando o repositório está em sua lista de repos ou repos é ["*"].
- Um sucesso é HTTP 200 a 299, dentro do limite de 6 segundos. Um 4xx, um 5xx, um timeout, uma falha de DNS ou TLS contam como falhas.
- Um sucesso redefine fails para 0 e adiciona 1 a delivered.
- Uma falha adiciona 1 a fails. Não há nova tentativa. O evento não é enviado novamente — a próxima entrega é a próxima mudança que detectamos.
- Após 10 falhas consecutivas, o webhook é desativado, carimbado com a hora UTC daquele momento, e os eventos restantes daquela execução são ignorados para ele.
- Um webhook desativado permanece na sua lista, marcado como desativado. Não há chamada para reativá-lo: exclua-o e crie outro, com um novo id e um novo segredo.
Lendo o estado
curl -sS https://olud.ai/api/webhooks.php -H "X-Api-Key: $OSAI_KEY"
A resposta, meta incluída
{
"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": "diária, após a varredura matinal",
"signature": "X-OSAI-Signature: sha256=HMAC_SHA256(body, secret)"
}
}delivered é a contagem vitalícia de POSTs bem-sucedidos. fails é a sequência atual consecutiva, então volta para 0 no próximo sucesso. disabled é null, ou o timestamp em que paramos. Segredos nunca são retornados por GET.
Quatro maneiras de as entregas pararem
- Seu endpoint continua falhando. Dez falhas consecutivas e o webhook é desativado. Corrija o endpoint, exclua o webhook, crie um novo, faça ping nele.
- Seu plano volta para gratuito — um cancelamento. O webhook é mantido e ignorado em cada despacho, silenciosamente. Mudar o plano novamente o reativa, mesmo id, mesmo segredo.
- Você rotaciona sua chave API em sua conta. A chave antiga sai do armazenamento de chaves enquanto o webhook ainda aponta para ela: ele sai da sua lista, não pode mais ser pingado ou excluído, e cada despacho o ignora como uma chave gratuita. Exclua seus webhooks antes de rotacionar a chave, depois crie-os novamente com a nova.
- Você o exclui. As entregas param imediatamente.
Testando sem esperar
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"}'Ambos os resultados
HTTP/1.1 200
{"ok":true,"data":{"ping":"delivered","http":200}}
HTTP/1.1 502
{"ok":false,"error":{"code":"ping_failed",
"message":"Seu endpoint respondeu HTTP 500 — um 2xx é esperado."}}O ping é enviado no formato do webhook, assinado da mesma forma, com evento ping e repositório null. Não move nem delivered nem fails, então um ping falhado nunca o empurra em direção ao limite de desativação. Também funciona em um plano gratuito quando o webhook já existe, enquanto entregas agendadas não.
HTTP 0 em uma mensagem ping_failed significa que não recebemos resposta alguma: o host não foi resolvido, TLS não foi concluído, ou os 6 segundos se esgotaram.
Códigos de erro
Cada falha retorna na mesma forma, com um status HTTP correspondente.
{
"ok": false,
"error": {
"code": "bad_url",
"message": "A URL deve ser https, acessível publicamente, porta padrão (sem localhost ou faixas privadas)."
}
}| HTTP | código | Quando |
|---|---|---|
| 400 | bad_json | O corpo não é um objeto JSON. |
| 400 | bad_url | A URL falhou na verificação: não é https, uma porta diferente de 443, localhost, .local, .internal, ou um endereço em uma faixa privada ou reservada. |
| 400 | bad_events | Após a filtragem, os eventos não continham nenhum de health, release, license, new_project. |
| 400 | bad_repos | Nenhuma entrada era "*" ou um proprietário/nome válido. |
| 400 | too_many_repos | Mais de 100 repositórios em um webhook. |
| 400 | bad_format | o formato não era json, slack ou discord. |
| 401 | missing_key | Nenhum cabeçalho X-Api-Key e nenhum parâmetro ?key= |
| 403 | chave_inválida | A chave não está em nosso armazenamento de chaves. Uma chave rotacionada ou revogada cai aqui. |
| 403 | recurso_pago | Seu plano permite 0 webhooks. |
| 404 | nao_encontrado | O id em ping ou em ?id= não está associado à sua chave. |
| 405 | método_não_permitido | Um método diferente de GET, POST ou DELETE. |
| 429 | limite_alcançado | Você já possui tantos webhooks quanto seu plano permite. |
| 500 | falha_na_gravação_do_armazenamento | Não conseguimos gravar a alteração no disco. Tente novamente. |
| 502 | ping_falhou | Seu endpoint respondeu ao ping de teste com algo diferente de um 2xx. |
Dois pontos práticos. Gerenciar webhooks não consome sua cota diária de requisições da API — este endpoint nunca toca no contador e não envia cabeçalhos X-RateLimit. E DELETE está ausente da lista de métodos permitidos pelo CORS, então uma chamada de origem cruzada de um navegador falha na pré-verificação; exclua do lado do servidor ou da página da conta, que é da mesma origem.
Automatizações
n8n: ler e receber
Dois nós cobrem ambas as direções. Um nó de Requisição HTTP lê nossos dados. Um nó de Webhook recebe nossos eventos. Nada para instalar da lista da comunidade.
Leia o catálogo
Cada endpoint responde GET em https://olud.ai/api/v1, com sua chave no cabeçalho X-Api-Key. /meta é o único endpoint que responde sem uma chave e sem tocar na sua cota — use-o como o primeiro nó enquanto você testa, porque ele prova a URL e o caminho da rede antes de você gastar uma chamada.
Receba os eventos
Adicione um nó de Webhook, método POST, e copie sua URL de produção. Registre essa URL na sua conta (seção Webhooks), ou POST para https://olud.ai/api/webhooks.php com sua chave. Quatro eventos para escolher: release, health, license, new_project. As entregas são feitas uma vez por dia, na mesma passagem que envia os alertas por e-mail — não no segundo em que algo acontece.
Um corpo de entrega, com formato json (o padrão):
O que está em data depende do evento:
- release — tag, nome, url (a página de releases do repositório)
- health — de, para, estrelas, página
- license — de, para
- new_project — estrelas, página
- ping — mensagem (apenas entregas de teste)
Em um evento de health, de e para são rótulos, não números: Thriving (pontuação 85 ou mais), Healthy (70+), Maintained (50+), Slowing down (30+), At risk (abaixo de 30). Compare texto no seu nó IF, não inteiros.
Cada entrega carrega três cabeçalhos: X-OSAI-Event, X-OSAI-Delivery (um id único por entrega, use-o para descartar duplicatas) e X-OSAI-Signature, da forma sha256=<HMAC-SHA256 do corpo com seu segredo de webhook>. O HMAC cobre os bytes exatos que enviamos, então se você quiser verificá-lo, ative a opção raw-body do nó de Webhook. Um corpo que foi analisado e re-serializado nunca corresponde.
Cole isso na tela. Os dois nós estão intencionalmente desconectados — cada um é seu próprio ponto de partida:
Então coloque sua chave no campo de cabeçalho do nó HTTP e ative o fluxo de trabalho — a URL de produção de um nó de Webhook escuta apenas enquanto o fluxo de trabalho está ativo.
Quando nada chega
- Registrar a URL responde 400 bad_url: o endpoint deve ser https, na porta 443, em um host publicamente resolvível. Um n8n auto-hospedado em http, em localhost, em um nome *.local ou em um intervalo de IP privado é recusado.
- Registrar responde 403 recurso_pago: webhooks começam no plano Dev — 3 endpoints no Dev, 10 no Pro, 50 no Org. Uma chave Free não pode criar um.
- Registrar responde 429 limite_alcançado: você já tem tantos webhooks quanto seu plano permite. Exclua um primeiro.
- Não espere até amanhã para descobrir: POST {"ping":"whk_…"} para /api/webhooks.php e o evento de teste sai imediatamente, na mesma forma que um real. Se seu endpoint não responder 2xx você recebe 502 ping_falhou com o código que ele retornou.
- Dez respostas consecutivas não-2xx desativam o webhook, silenciosamente. Um sucesso redefine o contador. Um webhook desativado só volta a funcionar recriando-o. O tempo limite de entrega é de 6 segundos, então um nó que responde lentamente conta como uma falha.
- O webhook foi criado, mas nada é entregue e nada falha: verifique o plano na chave que o possui. Se ele voltou para Free, as entregas são puladas enquanto o endpoint permanece registrado.
- O segredo é mostrado uma vez, na criação. Não há como lê-lo novamente — exclua e recrie.
Zapier e Make
As mesmas duas direções, mesma chave, nenhum aplicativo para encontrar em seus diretórios.
Receber: capturar o hook
No Zapier: Webhooks by Zapier, acione Catch Hook. No Make: o módulo Webhooks, Webhook personalizado. Ambos fornecem uma URL https em seu próprio domínio, que passa nossa verificação. Cole-a em sua conta como um endpoint de webhook, escolha seus eventos, depois envie um ping para si mesmo e confirme se o Zap ou cenário o recebe antes de construir os passos atrás disso.
Se você planeja verificar a X-OSAI-Signature, precisa do corpo bruto e dos cabeçalhos. No Zapier, isso significa Catch Raw Hook em vez de Catch Hook. No Make, ative a configuração de webhook que mantém os cabeçalhos de solicitação. Nossa assinatura é um HMAC sobre os bytes exatos do corpo, então um passo que analisa o JSON antes de você vê-lo torna a comparação impossível.
Ler: um passo HTTP
A ação GET de Webhooks do Zapier, ou o módulo HTTP do Make. Um passo, um cabeçalho:
Um sucesso é {"ok":true,"data":[…],"meta":{…}}. Um erro é {"ok":false,"error":{"code":"…","message":"…"}} com um status HTTP correspondente. Teste ok antes de mapear campos: um corpo de erro não tem dados, e um mapeamento que lê data[0] em um erro produz silenciosamente valores vazios a jusante.
- 401 missing_key — o cabeçalho não chegou. Verifique se o passo o envia em cada chamada, não apenas na primeira.
- 403 invalid_key — chave desconhecida ou revogada.
- 429 quota_exceeded — cota diária atingida, reinicia às 00:00 UTC. Respostas bem-sucedidas carregam X-RateLimit-Limit e X-RateLimit-Remaining, para que você possa ver isso chegando.
- 503 graph_unavailable — o gráfico está sendo reconstruído. Tente novamente em um minuto; este é o único erro que vale uma nova tentativa automática.
- 403 pro_required — /history/{slug} é apenas para Pro e Org.
- 404 not_found — nenhum projeto ou produto desse tipo. A mensagem traz uma sugestão de /search que você pode seguir.
GitHub Action: monitoramento de dependências
A ação lê os manifests de dependência do repositório verificado, nos pergunta a licença e o registro de manutenção de cada dependência, e publica um comentário na solicitação de pull listando o que precisa de uma decisão. Ela reescreve esse mesmo comentário em cada execução — encontra-o novamente através de um marcador oculto — então uma longa solicitação de pull não se enche de duplicatas.
Todo o fluxo de trabalho, com cada entrada em seu valor padrão. Apenas a chave da API é necessária:
actions/checkout deve vir primeiro: a ação lê arquivos do diretório de trabalho, e na raiz apenas, a menos que você defina caminhos.
| Entrada | Padrão | O que faz |
|---|---|---|
| api-key | obrigatório | Enviado como X-Api-Key. Uma busca por dependência distinta. Uma chave gratuita funciona. |
| github-token | ${{ github.token }} | Publica o comentário. Necessita de pull-requests: escrever na tarefa. |
| health-floor | 45 | Marque uma dependência com pontuação abaixo disso em 100. 0 desativa a verificação de saúde e mantém apenas as licenças. Leia o limite conhecido abaixo. |
| licenças | AGPL-3.0,BSL-1.1,SSPL-1.0,Elastic-2.0,BUSL-1.1,NOASSERTION | Separadas por vírgulas, correspondidas sem considerar maiúsculas e minúsculas em relação à licença que possuímos. Defini-la substitui a lista, não a adiciona. |
| fail-on-finding | false | Falhe a verificação em vez de apenas comentar. |
| paths | vazio | Manifests separados por vírgulas para ler. Vazio significa: procure os quatro nomes conhecidos na raiz. |
NOASSERTION está na lista padrão de propósito: é o que mantemos quando o repositório não possui um arquivo de licença que uma máquina possa reconhecer, o que é uma decisão a ser tomada em vez de um detalhe. A ação expõe uma saída, findings — o número de dependências sinalizadas, escrito mesmo quando é 0.
O que lê
- package.json — as chaves de dependencies e devDependencies. peerDependencies e optionalDependencies não são lidas.
- requirements.txt — um nome por linha, comentários removidos, linhas que começam com - descartadas. Portanto, -r other-requirements.txt não é seguido e -e . é ignorado.
- pyproject.toml — apenas chaves cujo valor começa com uma aspa ou uma chave, que é o estilo Poetry: fastapi = "^0.110". Um bloco PEP 621, dependencies = ["fastapi>=0.110"], não produz nada.
- go.mod — linhas indentadas da forma name vX.
os caminhos podem apontar para qualquer lugar na árvore, por exemplo apps/api/requirements.txt, mas o nome base do arquivo deve ser um dos quatro. Um arquivo chamado requirements-dev.txt não possui parser: é ignorado sem uma mensagem.
Como falha
- Nenhum manifesto na raiz, ou nenhuma etapa de checkout: "Nenhum manifesto de dependência encontrado — nada para verificar." findings é 0 e o trabalho está verde. Uma execução verde não significa uma árvore limpa — leia essa linha.
- O evento não possui pull request, por exemplo em push: "Nenhum pull request em contexto — comentário ignorado." As descobertas ainda estão no log.
- pull-requests: comentário ausente: "Não foi possível postar o comentário (HTTP 403)." O trabalho permanece verde e o comentário nunca aparece. Olhe para o log, não para o pull request.
- Cota diária da API atingida durante a execução: ::warning::Cota diária da API atingida — execução parcial. Para de procurar coisas e comenta sobre o que conseguiu resolver. Uma chamada por dependência distinta, então 300 dependências consomem 300 das 500 chamadas diárias de uma chave Free.
- Um manifesto que não será analisado: "⚠ <file> ilegível (…) — ignorado". Os outros manifestos ainda são executados.
- A etapa composta executa node do PATH e usa fetch nativo. Runners hospedados no GitHub já possuem Node 20. Um runner auto-hospedado precisa de actions/setup-node com Node 20 ou posterior.
- licenças: '' com health-floor: '0' produz zero descobertas para sempre. Essa combinação desativa ambas as verificações.
O que a Ação não captura
Um nome de pacote não é um nome de repositório. Cada nome passa por /api/v1/search?q=<name>&limit=5 e a ação mantém um resultado apenas quando o nome do projeto é igual ao nome do pacote, ignorando maiúsculas e minúsculas. Qualquer outra coisa é descartada em silêncio — sem linha de comentário, sem aviso. Adivinhar o resultado mais próximo levantaria alarmes sobre o projeto errado, o que é pior do que ficar em silêncio, mas significa que a verificação cobre menos do que sua árvore de dependências.
- Pacotes npm com escopo perdem seu escopo antes da busca: @types/node é pesquisado como node, o que pode corresponder a um repositório não relacionado com esse nome. Este é o único caso em que o projeto errado pode acabar no comentário.
- Módulos Go mantêm seu caminho: github.com/gin-gonic/gin se torna gin-gonic/gin, que nunca é igual a um nome de repositório (gin). As dependências do go.mod não se resolvem.
- Um pacote cujo repositório é nomeado de forma diferente — o caso comum em Python — nunca se resolve.
- Apenas os cinco principais resultados são examinados e apenas a primeira correspondência exata é utilizada. Quando dois repositórios compartilham um nome, aquele com mais estrelas vence.
A linha a ser lida no log é: Resolved N of M dependencies · K flagged. Se N estiver muito abaixo de M, a verificação olhou para uma fração da sua árvore. Nada no comentário do pull request diz isso.
Especificação OpenAPI
Um arquivo descreve a API de leitura: https://olud.ai/openapi.json. OpenAPI 3.0.3, um servidor (https://olud.ai/api/v1), nove operações GET, um esquema de segurança — uma chave de API no cabeçalho X-Api-Key. Você não escreve uma integração; você cola um endereço.
- /meta — data de construção e contagem de projetos. Declarado sem segurança: é a verificação de saúde.
- /search — q obrigatório, limite de 1 a 50, padrão 15.
- /projects — lang, license, vertical, health_min 0 a 100, ordenar em stars|health|momentum|recent (padrão stars), limite de 1 a 100 (padrão 25), offset 0 a 100000 (padrão 0).
- /project/{id} — o registro completo mesclado, incluindo saúde com seus componentes.
- /emerging — limite de 1 a 100, padrão 25.
- /alternatives/{product} — alternativas de código aberto para um produto comercial.
- /models — provider, tier, limite de 1 a 200 (padrão 50), offset.
- /hf — mais baixados e em tendência no Hugging Face.
- /history/{slug} — 90 dias de estrelas, saúde e momentum. Pro e Org.
Importe-o
- Custom GPT — Configure, depois Ações, depois Importar de URL. Autenticação: Chave da API, cabeçalho personalizado, nome X-Api-Key.
- Dify, Flowise, Open WebUI — adicione uma ferramenta de um esquema OpenAPI, cole a URL, escolha as operações que deseja expor, adicione o mesmo cabeçalho.
- Postman, Insomnia — Importar, depois Link. Você obtém as nove requisições, documentadas. Defina X-Api-Key uma vez no nível da coleção para que cada requisição herde isso.
- Geradores de código — qualquer gerador OpenAPI produz um cliente tipado, TypeScript, Python ou Go, a partir deste arquivo sozinho.
Qualquer que seja a ferramenta, há apenas duas coisas a definir: a URL do arquivo e a chave como um cabeçalho chamado X-Api-Key. Se uma importação mostrar nove operações, mas cada chamada responder 401, o cabeçalho não está sendo enviado — essa é a primeira coisa a verificar.
As cotas são por chave e por dia, redefinidas às 00:00 UTC: 500 requisições no Free, 5.000 no Dev, 50.000 no Pro, 500.000 no Org. Elas são contadas separadamente do servidor MCP, então um assistente fazendo perguntas no seu editor nunca consome esse orçamento.
Quatro feeds RSS
Sem chave, sem inscrição, sem conta. feed.php aceita exatamente quatro tipos: notícias, blog, lançamentos, emergentes.
| URL | O que carrega | De onde vem |
|---|---|---|
| /feed.php | Até 30 novos projetos (★stars · owner/name), os novos modelos do dia (Novo modelo: name (provider), com a janela de contexto na descrição), novos Hugging Face Spaces (Novo Space: name por autor, com a contagem de likes), e o artigo do dia. | today-data.json, reconstruído a cada hora |
| /feed.php?type=releases | Até 60 versões lançadas pelos projetos que seguimos: título do projeto + tag, link para o lançamento, guid owner/name@tag. | releases-data.json, reconstruído a cada hora |
| /feed.php?type=emerging | Até 40 projetos emergentes — saudáveis, em aceleração, ainda pouco conhecidos. Descrição: ★stars · saúde N/100 · +N estrelas esta semana. | o gráfico, reconstruído todas as manhãs |
| /feed.php?type=blog | Artigos em /blog/<slug>/, /blog/alternatives/ e /reports/. O título e a descrição são o próprio título da página e a meta descrição; a data é o tempo de modificação do arquivo. | ler do disco, então um novo artigo aparece por conta própria |
Todos os quatro respondem RSS 2.0 como application/rss+xml, em inglês, item mais recente primeiro, com Cache-Control: public, max-age=1800. Isso é 30 minutos: um leitor que consulta mais frequentemente recebe a cópia em cache, que é a mesma resposta servida mais rapidamente.
Os Guids são estáveis e nem sempre o link, que é o que você quer ao desduplicar em uma automação: lançamentos usam owner/name@tag, projetos emergentes usam emerging:<id>, o artigo do dia usa sua URL mais a data, tudo o mais usa seu link.
Aponte Slack, Teams ou Feedly para eles para ler, ou use o gatilho RSS no n8n, Zapier e Make quando um cronograma se adequa melhor a você do que um webhook — essa também é a maneira de obter lançamentos sem um plano pago. Se um arquivo fonte não foi reconstruído, o feed ainda responde 200 com um canal vazio em vez de um erro, então uma automação que de repente não recebe nada não está necessariamente quebrada.