Documentação

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.

Regenerar a chave da sua conta revoga a antiga imediatamente. Qualquer coisa que ainda a utilize para de funcionar — atualize suas integrações primeiro.

De que preciso?

Você quer…Usar
Fazer perguntas ao seu editorServidor MCP
Consultar os dados do seu próprio códigoREST API
Ser informado quando algo mudaWebhooks
Integrá-lo ao n8n, Zapier ou MakeAutomatizações
Verificar dependências em cada pull requestGitHub Action
Acompanhar em um leitor, sem chaveFeeds 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.

FatoValor
Endpointhttps://olud.ai/mcp.php
MétodoPOST, JSON-RPC 2.0. GET retorna 405 (veja abaixo)
TransporteHTTP transmissível, um endpoint, sem fluxo SSE
Versão do protocolo2025-06-18. Se o cliente solicitar 2025-03-26 ou 2024-11-05, o servidor responde nessa versão
Versão do servidor1.0.0
SessãoNenhuma. Nenhum Mcp-Session-Id para manter, nada para expirar, nada para reconectar
Capacidadesapenas ferramentas. resources/list, resources/templates/list e prompts/list respondem com listas vazias em vez de um erro
AutenticaçãoCabeçalho de solicitação X-Api-Key. Opcional
AgrupamentoNã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.

Cada resposta de ferramenta carrega a URL da página correspondente em olud.ai, além de uma linha de atribuição: pontuações e índices são calculados por olud.ai, fatos contextuais vêm do GitHub, Hugging Face, OpenRouter, PyPI, NPM e Docker Hub.

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" }
    }
  }
}
Nesse bloco de ponte, escreva X-Api-Key:${OSAI_KEY} sem espaço após os dois pontos e coloque o valor em env. mcp-remote divide um argumento de cabeçalho em espaços, e um cabeçalho escrito inline perde seu valor.

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 }
  ]
}
O VS Code nomeia o objeto de nível superior como servers, não mcpServers. Copiar o bloco do Claude ou Cursor como está é a razão usual pela qual o servidor nunca aparece. O prompt de inputs pede a chave na primeira utilização, então o arquivo permanece seguro para ser enviado.

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.

FerramentaParâmetrosLimites e padrõesRetornos
search_open_source_aiquery (string, obrigatório), limit (inteiro)limite restrito a 1 … seu teto de resultados por chamada. Padrão 10, ou seu teto se for menormatches (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_projectprojeto (string, obrigatório)Aceita um slug (ollama-ollama), owner/repo, uma URL completa do github.com, ou o nome exato do projetoTudo 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_alternativesproduto (string, obrigatório)Nome do produto comercial. Correspondido pelo seu slug, depois pelo seu nome exatoproduto, página e a lista de alternativas, que também é limitada pelo seu teto de resultados por chamada
list_modelsprovedor (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 menorcorrespondências, então id, nome, provedor, contexto, price_per_million (entrada e saída), modalidade, tool_calling, e a URL do leaderboard
projetos_em_altatipo (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 menortipo, 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.
A busca corresponde ao texto, não ao significado. Ela pontua um nome exato mais alto, depois um nome que começa com suas palavras, depois um nome que as contém, depois uma descrição que as contém, e adiciona pontos para uma tag de tópico exata. Uma frase longa não encontra nada; "síntese de fala" não encontra nada que "texto para fala" não encontre. Quando uma busca retorna vazia, encurte a consulta para uma ou duas palavras.

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.

Para alguns produtos, temos uma página de comparação escrita à mão e nenhuma lista estruturada no índice legível por máquina. find_alternatives então responde como um sucesso com um array de alternativas vazio, diz que a lista estruturada não está no índice para este, e fornece a URL da página. Um array vazio lá não significa que nenhuma alternativa existe.

Volumes por plano

Sem chaveGratuitoDevProOrg
Chamadas por dia252002,00020,000200,000
Resultados por chamada5102550100

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

Uma ferramenta com falha retorna HTTP 200 com isError definido como true e o motivo como texto simples. Isso é deliberado: o modelo lê o motivo e pode tentar algo diferente. Isso também significa que um 200 no seu log de proxy não é prova de que a chamada funcionou. Leia o corpo.

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çalhoValorEnviado em
X-RateLimit-LimitSeu teto diário, como um inteiroCada solicitação que passou na verificação da chave
X-RateLimit-RemainingSolicitações restantes hoje, arredondadas para 0Cada solicitação que passou na verificação da chave, incluindo o 429
ETagMD5 citado do tempo de construção do gráfico de projetosCada endpoint exceto /meta
Access-Control-Allow-Origin*Cada resposta, incluindo OPTIONS (que responde 204)
CORS está aberto e X-Api-Key é um cabeçalho permitido, então um navegador pode chamar a API diretamente. Qualquer um que leia o código-fonte da sua página terá sua chave e gastará sua cota. Chame a API do seu servidor e passe os resultados.
A verificação da chave é executada antes do roteamento. Um caminho mal escrito sem chave retorna 401 missing_key, não 404. Adicione a chave antes de procurar um erro de digitação no caminho.

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.

Duas coisas a saber sobre o ETag. Ele é calculado apenas a partir do gráfico de projetos, então o mesmo valor é retornado por /models, /hf e /history — envie um ETag de volta para o endpoint de onde você o obteve, ou você receberá um 304 enquanto dados mais novos estiverem atrás dele. E a cota é contada antes da comparação do ETag, então um 304 ainda custa uma solicitação.

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âmetroTipoPadrãoComportamento
idsegmento de caminho, obrigatórionenhumMinú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
Use o slug com hífen. Uma barra literal no caminho inicia um novo segmento de caminho, então /v1/project/ollama/ollama procura "ollama" e retorna 404. Os ids retornados por /projects e /search são sempre seguros para passar de volta.

GET /projects

Filtrar, classificar e paginar o catálogo. Os filtros se aplicam primeiro, depois a classificação, depois o deslocamento e limite.

ParâmetroTipo / limitesPadrãoComportamento
langstringsem filtroCorrespondência exata na linguagem do projeto, sem diferenciação entre maiúsculas e minúsculas. lang=rust mantém apenas Rust.
licençastringsem filtroCorrespondê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.
verticalstringsem filtroCorrespondê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_mininteirosem filtroManté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.
classificarstars, saúde, impulso ou recentestarsSempre 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.
limiteinteiro 1-10025Valores 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.
deslocamentointeiro 0-1000000Limitado 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âmetroTipo / limitesPadrãoComportamento
qstring, obrigatórionenhumRemovido e em minúsculas. Vazio ou apenas espaços em branco retorna 400 missing_query.
limiteinteiro 1-5015Limitado 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âmetroTipo / limitesPadrãoComportamento
limiteinteiro 1-10025Limitado. 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âmetroTipoPadrãoComportamento
produtosegmento de caminho, obrigatórionenhumEm 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âmetroTipo / limitesPadrãoComportamento
provedorstringsem filtroCorrespondência de substring, sem distinção entre maiúsculas e minúsculas. provider=mistral corresponde a "Mistral AI".
tiergratuito ou pagosem filtroCorrespondência exata, sem distinção entre maiúsculas e minúsculas em sua entrada.
limiteinteiro 1-20050Limitado aos limites; não numérico retorna 50.
deslocamentointeiro 0-1000000Aplicado à 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"
tier não é uma bandeira de preço. gratuito significa que os pesos são publicados (peso aberto), pago significa proprietário. Um modelo de peso aberto pode ter um price_in não zero, porque o preço é o que um provedor de API cobra para executá-lo. Além disso, a classificação reinicia em 1 em cada tier, então classificar a lista mesclada por classificação mistura duas escalas.

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
Este endpoint depende de uma varredura matinal do Hugging Face Hub. Antes que esse arquivo exista, a chamada retorna 503 not_ready. Nada do seu lado está errado; tente novamente mais tarde no dia.

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âmetroTipoPadrãoComportamento
slugsegmento de caminho, obrigatórionenhumEm 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
meta.window sempre lê "90 dias rolando" — esse é o limite mantido no tempo de construção, não uma promessa de 90 pontos. A gravação começa quando um projeto entra no gráfico, então séries curtas são normais. Leia meta.points para o que você realmente recebeu, e dimensione seu gráfico a partir de dias, nunca a partir de um 90 codificado.

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 chaveRequisições / diaNome públicoO que muda
grátis500GratuitoTudo exceto /history. /emerging serve o índice do dia anterior.
dev5000DevMesmo acesso que o Free, teto mais alto.
pro50000Pro/history abre. /emerging serve o índice desta manhã.
business500000OrgMesmo 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."}}
O servidor MCP em /mcp.php tem seu próprio contador: 25 chamadas/dia sem uma chave (contadas por IP), 200 no Free, 2000 no Dev, 20000 no Pro, 200000 no Business. Perguntas que seu editor faz via MCP não consomem a cota REST, e vice-versa.
Um modo de falha a reconhecer: se o arquivo do contador não puder ser aberto, as requisições são liberadas e X-RateLimit-Remaining lê 0. Um 200 junto com Remaining: 0 significa que o contador estava indisponível, não que você está sem cota.

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.

HTTPcódigoAcionarO que fazer
401missing_keySem 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.
403invalid_keyA 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.
403pro_required/history chamado com uma chave gratuita ou dev.Leia os valores de hoje de /project/{id} (saúde, velocidade), ou mude para Pro.
429quota_exceededO 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.
400missing_id/project ou /alternatives chamado sem segmento de caminho.Adicione o segmento. /v1/project sozinho não é uma listagem — use /v1/projects.
400consulta_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.
400slug_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.
404nao_encontradoID 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.
404endpoint_desconhecidoChave válida, caminho não está no roteador.Verifique a ortografia. /meta lista seis endpoints e omite /hf e /history, que ambos existem.
503grafico_indisponivelUm 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.
503nao_pronto/hf antes da varredura matinal do Hugging Face produziu seu arquivo.Tente mais tarde no dia. Outros endpoints não são afetados.
304sem corpoIf-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.

Valide os parâmetros do seu lado antes de enviar. Valores de limite e deslocamento fora do intervalo são ajustados em silêncio em vez de rejeitados, então um valor ruim custa uma solicitação e retorna uma página que você não pediu. Compare meta.limit e meta.offset com o que você enviou quando um resultado parecer curto.

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 APIWebhooks permitidos
grátis0
dev3
pro10
business50

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.

eventoDispara quandoChaves dentro dos dados
saúdeo rótulo de saúde de um repositório mudade, para, estrelas, página
lançamentoum novo rótulo de lançamento aparece para um repositóriorótulo, nome, url
licençaa licença que temos para um repositório mudade, para
novo_projetoum repositório entra no diretórioestrelas, 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çalhoValor
Content-Typeapplication/json
User-Agentolud.ai-Webhooks/1.0
X-OSAI-Eventhealth, release, license, new_project ou ping
X-OSAI-Delivery16 caracteres hexadecimais, novos para cada POST
X-OSAI-Signaturesha256= 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"
}
Responda 2xx e responda rápido. Esperamos 6 segundos, depois contamos uma falha. Verifique a assinatura, coloque o evento em uma fila, retorne 200 e faça o trabalho depois.

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 "", 200

Compare 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.
Slack e Discord ignoram X-OSAI-Signature. Nós o enviamos de qualquer forma, e ainda cobre o que eles recebem: a assinatura é calculada sobre os bytes realmente postados, independentemente do formato.

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.

formatoO que postamosURL para colar
jsono payload acima, inalteradoseu próprio endpoint
slacktexto, mais um bloco de seção mrkdwnuma URL de webhook de entrada do Slack
discordum 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.

Defeito conhecido, saúde no slack e discord apenas. O construtor de mensagens lê de e para como números, mas a saúde carrega rótulos. O título aparece como "acme/inference-server health 0 → 0", o corpo sempre lê "A manutenção está melhorando.", e a cor é sempre verde — incluindo quando a pontuação cai. O formato json não é afetado: ele encaminha os dois rótulos como estão. Use json se precisar dos rótulos de saúde.

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)."
  }
}
HTTPcódigoQuando
400bad_jsonO corpo não é um objeto JSON.
400bad_urlA 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.
400bad_eventsApós a filtragem, os eventos não continham nenhum de health, release, license, new_project.
400bad_reposNenhuma entrada era "*" ou um proprietário/nome válido.
400too_many_reposMais de 100 repositórios em um webhook.
400bad_formato formato não era json, slack ou discord.
401missing_keyNenhum cabeçalho X-Api-Key e nenhum parâmetro ?key=
403chave_inválidaA chave não está em nosso armazenamento de chaves. Uma chave rotacionada ou revogada cai aqui.
403recurso_pagoSeu plano permite 0 webhooks.
404nao_encontradoO id em ping ou em ?id= não está associado à sua chave.
405método_não_permitidoUm método diferente de GET, POST ou DELETE.
429limite_alcançadoVocê já possui tantos webhooks quanto seu plano permite.
500falha_na_gravação_do_armazenamentoNão conseguimos gravar a alteração no disco. Tente novamente.
502ping_falhouSeu 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.
Não agende isso a cada minuto. O gráfico é reconstruído uma vez todas as manhãs, então uma vez por hora já é mais frequente do que os dados mudam. As respostas carregam um ETag derivado da data de construção e uma solicitação repetida recebe 304 Not Modified — mas a chave é contada antes dessa comparação, então um 304 ainda custa uma chamada contra sua cota diária.

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.

EntradaPadrãoO que faz
api-keyobrigatórioEnviado 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-floor45Marque 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çasAGPL-3.0,BSL-1.1,SSPL-1.0,Elastic-2.0,BUSL-1.1,NOASSERTIONSeparadas 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-findingfalseFalhe a verificação em vez de apenas comentar.
pathsvazioManifests 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.

Não falha sua verificação por padrão. fail-on-finding é falso, então uma descoberta é um comentário, não uma construção vermelha — uma mudança de licença é uma decisão, e um pipeline que fica vermelho por algo que ninguém pode corrigir em cinco minutos ensina a equipe a ignorá-lo. Duas coisas falham o trabalho: uma api-key ausente, registrada como ::error::api-key é necessária, e fail-on-finding: true com pelo menos uma descoberta.

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.

Limite conhecido no health-floor. A ação lê a pontuação de manutenção como um campo de objeto, enquanto /api/v1/search retorna saúde como um número simples. Com a resposta da API de hoje, a verificação de saúde não pode ser acionada, independentemente do piso que você definir: a lista de licenças é o que produz descobertas. A pontuação completa com seus quatro componentes é fornecida por /api/v1/project/{id} se você precisar dela enquanto 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.

A gestão de webhooks não está neste arquivo. Ela vive em https://olud.ai/api/webhooks.php com a mesma chave: GET lista seus endpoints com suas contagens de entrega e falha, POST cria um ou envia um ping, DELETE remove um. Uma ferramenta que importa a especificação não pode criar um webhook para você.

Quatro feeds RSS

Sem chave, sem inscrição, sem conta. feed.php aceita exatamente quatro tipos: notícias, blog, lançamentos, emergentes.

URLO que carregaDe onde vem
/feed.phpAté 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=releasesAté 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=emergingAté 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=blogArtigos 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.

A armadilha: um tipo desconhecido não é um erro. ?type=release no singular, ou um erro de digitação, retorna o feed de notícias com HTTP 200. Se dois dos seus feeds parecerem suspeitamente idênticos, verifique a ortografia de type. Os valores são cortados e convertidos para minúsculas, então ?type=Releases está bom.

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.